MOOBON
Architecture Note ・ AWS / ECS / EventBridge

Give Only Your ECS Batch Jobs More CPU and MemoryOverride the Task Definition from the EventBridge Schedule Input Instead of Splitting It

Published September 30, 20267 min readMOOBON Tech Blog

On a Rails system running on ECS Fargate, web and batch normally share the same image. Even so, it is common to find two almost identical task definitions, kept apart only because “batch needs more memory.”

An ECS “scheduled task” is an EventBridge rule, and the target's Input is passed straight to RunTask as overrides. That means task-level CPU/memory can be overridden on the rule, with no need for a second task definition. This is our record of setting that up on a system we operate for a client: the mechanism, what can be overridden, the CLI steps, and the caveats.

1. Background: two task definitions that differed only in CPU/memory

The system is a business application on Rails 7 / ECS Fargate that MOOBON operates and maintains. The web runs as an ECS service with two tasks always on; batch jobs are started from ECS “Scheduled tasks,” 8 rules in total, hourly and daily, each running a rake task.

The system had two task definition families: myapp-prod for the web and myapp-prod-batch for batch. Pulling the latest revision of each with describe-task-definition and comparing them (hashing the environment variable values) gave this:

Itemmyapp-prod (web)myapp-prod-batch (batch)
ImageSame (immutable prod-<commit sha> tag)
Environment variablesAll 22 keys identical, all values identical
Task role / execution role / network modeSame
CPU / memory1 vCPU / 4 GiB2 vCPU / 8 GiB
Log group (awslogs)/ecs/myapp-prod/ecs/myapp-prod-batch

The only substantive difference was CPU/memory (the log group comes up in §3). Yet because there were two task definitions, every deploy meant re-registering the batch one by hand and fixing the revision referenced by the 8 rules. “If only the batch could launch with a bigger task size, one task definition would do” is where this article starts.

Split task definitions only for differences RunTask can't override
CPU/memory, command, environment variables, and task roles can all be overridden at RunTask time (§3). If those are the only reasons, there is no need for a second task definition.

2. How it works: Input is passed straight through as RunTask overrides

2-1. A scheduled task is really an EventBridge rule

What you create in the ECS console's “Scheduled tasks” tab is, underneath, an EventBridge rule (cron expression) with the ECS cluster as its target. Looking at it with aws events list-targets-by-rule, a target holds:

  • Arn: the ECS cluster to run on
  • RoleArn: the role EventBridge uses to call RunTask (e.g. ecsEventsRole)
  • EcsParameters: task definition ARN, launch type, network configuration, task count
  • Input: the overrides passed to RunTask (a JSON string)

At the cron time, EventBridge calls ecs:RunTask with this content. Updating a rule does not run the batch; it simply starts at the next cron time with whatever the rule contains at that moment.

2-2. Put cpu / memory in the Input

The key is Input. The EventBridge documentation explains that if the input to an ECS target matches RunTask's TaskOverride structure, it is mapped one-to-one and passed through to RunTask. Most people only use it for containerOverrides to swap the command, but TaskOverride also includes task-level cpu / memory. In other words, writing this in Input changes the CPU/memory for that launch only, without touching the task definition:

{
  "cpu": "2048",
  "memory": "8192",
  "containerOverrides": [
    {
      "name": "Rails",
      "command": ["bin/rails", "sync:hourly"]
    }
  ]
}

In our setup the task definition myapp-prod stays at 1 vCPU / 4 GiB, and all 8 rules carry cpu: 2048 / memory: 8192 in their Input. The details page of a launched batch task shows “Task definition: myapp-prod:607” and “CPU | memory: 2 vCPU | 8 GiB”, confirming that the task was provisioned with the Input values, not the task definition values (§4-3).

3. What can and cannot be overridden

3-1. The scope of TaskOverride / ContainerOverride

What is overridable is defined by the TaskOverride / ContainerOverride API. Here is the summary we used to decide whether merging was viable.

ItemRunTask overrideNotes
Task cpu / memoryYesTask level. Must be a valid Fargate combination
ephemeralStorageYesTask level (Fargate)
taskRoleArn / executionRoleArnYesThe caller needs iam:PassRole on that role
Container command / environmentYescontainerOverrides. For environment, list only the keys to override
Container cpu / memory / memoryReservationYescontainerOverrides. Raise these too if the container definition has a hard limit
logConfiguration (log group etc.)NoAfter merging, batch logs go to the same log group as the web
healthCheck / portMappings / volumesNoThe web settings come along to the batch as they are

In our case, losing the separate log group was the only trade-off. Logs still go to a separate stream per task and can be filtered in Logs Insights, so we accepted it. If separate log groups are themselves a requirement, that alone remains a valid reason to keep two task definitions.

3-2. Fargate size combinations and container-level memory

The override must be a CPU/memory pair that Fargate supports, or RunTask itself fails (for example, 2 vCPU allows 4 to 16 GiB in 1 GiB steps). You can go above or below the task definition, but stay within the table.

The other thing to check is a container-level memory (hard limit). Our task definition had none on the container, so the task-level override was enough. If your container has something like memory: 3072, raising the task to 8 GiB still leaves the container OOM-killed at 3 GiB. In that case, override containerOverrides[].memory as well.

4. Setup steps (AWS CLI)

The same change goes into 8 rules, so we did it with the CLI in one pass rather than in the console. AWS CloudShell is convenient: it reuses the console sign-in and already has aws and jq.

4-1. Back up the current targets

The rules we care about are “rules whose target is the ECS cluster,” so list-rule-names-by-target with the cluster ARN lists them. First, dump every rule's targets to a file. These files double as the rollback input.

CLUSTER=arn:aws:ecs:ap-northeast-1:123456789012:cluster/my-cluster
mkdir -p backup
for rule in $(aws events list-rule-names-by-target --target-arn $CLUSTER --query RuleNames --output text); do
  aws events list-targets-by-rule --rule "$rule" --query Targets > "backup/$rule.json"
done

4-2. Rewrite with jq and put-targets

From the backed-up JSON, select only the targets that reference myapp-prod-batch, then change two things: (1) the task definition ARN to the web one, (2) add cpu/memory to Input. Write it back with put-targets. The command override, network configuration, and roles are carried over as they are.

NEW=arn:aws:ecs:ap-northeast-1:123456789012:task-definition/myapp-prod:607
for f in backup/*.json; do
  rule=$(basename "$f" .json)
  jq --arg arn "$NEW" '[.[]
    | select((.EcsParameters.TaskDefinitionArn // "") | test("/myapp-prod-batch:"))
    | .EcsParameters.TaskDefinitionArn = $arn
    | .Input = (.Input | fromjson | .cpu = "2048" | .memory = "8192" | tojson)]' "$f" > new.json
  [ "$(jq length new.json)" -gt 0 ] || continue
  echo "== $rule"; jq -c '.[] | {TaskDefinitionArn: .EcsParameters.TaskDefinitionArn, Input}' new.json
  aws events put-targets --rule "$rule" --targets file://new.json
done
  • Always check that the put-targets response says FailedEntryCount: 0. Anything else means that rule was not updated
  • Comment out the put-targets line on the first pass and just read the echo output (dry run)
  • To roll back: aws events put-targets --rule <rule> --targets file://backup/<rule>.json

4-3. Verification

Updating a rule does not run the batch, so verification waits for the next cron time. We used the hourly sync at 10 minutes past the hour. What to check:

  • In the ECS task details, Task definition is myapp-prod:607, CPU | memory is 2 vCPU | 8 GiB, and Started by is the rule name
  • The container exit code is 0 (containers[].exitCode in describe-tasks)
  • The describe-tasks output contains both overrides.cpu / overrides.memory and the effective cpu / memory

5. Caveats

5-1. Don't save from the console form

The ECS console's Update screen for a scheduled task has the task definition revision, networking, a task role override, and container overrides (command and environment variables), but no field for task-level cpu / memory. Since there is no guarantee how cpu / memory written in Input are handled when saving from this form, we only touch the merged rules from the CLI. To just look at the stored values, open the rule in the EventBridge console → Targets → “Input to target: Constant” → View, which shows the JSON as is.

5-2. put-targets replaces the whole target

put-targets replaces the target with the same Id entirely with what you pass. If you pass only TaskDefinitionArn, you end up with a target that has lost its Input, NetworkConfiguration, and RoleArn. Always start from the list-targets-by-rule output and change only the fields you mean to (the script in §4-2 does exactly that).

5-3. Cost comparison with “just make the web bigger”

The simplest merge would be to skip overrides and raise the task definition itself to 2 vCPU / 8 GiB, but then the two web tasks pay that price around the clock. A rough estimate with Fargate's published Tokyo (Linux/x86) prices:

OptionWeb (2 tasks, always on)Monthly increase (approx.)
Raise the task definition to 2 vCPU / 8 GiB1 vCPU / 4 GiB → 2 vCPU / 8 GiB+ about $106
Override in Input (this article)Stays at 1 vCPU / 4 GiB$0

* Computed as 1 vCPU $0.05056/h + 4 GiB × $0.00553/h ≈ $0.0727/h, × 730 h × 2 tasks ≈ $106. The batch side is billed only while running; an hourly job that runs about 2 minutes costs around $4/month even at 2 vCPU / 8 GiB. Prices are approximate, based on the public price list as of September 2026; check the official AWS pricing page for current values.

Endnote

Afterword

That EventBridge's Input is passed straight into RunTask's TaskOverride is obvious once you know it, but it is easy to miss because the console form never shows a cpu / memory field. If the only reason you keep a second task definition is “the batch needs to be bigger,” a few lines of Input will do.

For the networking side of ECS, see our companion article Replacing NAT Gateway with fck-nat While Keeping a Static Egress IP for ECS. For AWS operations design or a cost review, feel free to contact us at info@moobon.jp.

FAQ

Frequently Asked Questions

QCan I override task-level CPU/memory from the ECS console's Scheduled tasks screen?
A

As of September 2026, the ECS console's Update screen for a scheduled task offers the task definition family / revision, number of tasks, networking, a task role override, the EventBridge IAM role, and container overrides (command and environment variables). There is no field for task-level cpu / memory. Task-level overrides have to be written directly into the Input (JSON) that the EventBridge rule passes to its target; we set them with the AWS CLI's put-targets. To check what is stored, open the rule in the EventBridge console → Targets → "Input to target: Constant" → View, or run aws events list-targets-by-rule.

QCan I override to a smaller CPU/memory than the task definition?
A

Yes. The cpu / memory in RunTask overrides are independent of the task definition values, and you can go up or down as long as the pair is a valid Fargate combination. In this article we raise the web task definition (1 vCPU / 4 GiB) to 2 vCPU / 8 GiB for batch, but the same mechanism lets you shrink a light batch to 0.5 vCPU / 1 GiB to save money. One caveat: if the container definition has a hard memory limit, raising the task level alone leaves the container capped at that limit, so override memory in containerOverrides as well.

QCan the log destination (logConfiguration) also be changed per rule?
A

No. RunTask's TaskOverride only covers cpu / memory / ephemeralStorage / taskRoleArn / executionRoleArn plus per-container command / environment / cpu / memory; the awslogs log group and stream prefix are not part of it. So once you merge into one task definition, batch logs land in the same log group as the web. If separate log groups are a hard requirement, that alone is a valid reason to keep separate task definitions.

QCan environment variables be overridden per rule too? Anything to watch out for?
A

Yes, via environment in containerOverrides (list only the keys you want to override; the rest come from the task definition). The catch is that values written on the rule take precedence over the task definition. If you later change a value on the task definition, the rule keeps using its old value for as long as it overrides that key. Keep rule-level overrides to keys that are truly batch-specific, and leave values you want to manage in one place, such as secrets, out of the rules.

MOOBONISO/IEC 27001 CertificationIT Introduction Subsidy Support ProviderAWS Partner Select Tier Services
Copyright © 2026 MOOBON, Inc. All Rights Reserved.
Standard: ISO/IEC 27001:2022
Scope: Web system design support / Development, operation, and maintenance of in-house cloud services / Contract system development, operation, and maintenance / Server construction, operation, and maintenance