Your ECS environments run 168 hours a week. Your team works 40.
Dev and staging Fargate tasks run around the clock by default. At 10 environments with 8 services each, that's roughly $1,442/month in compute running while nobody is working. Below: the math, three native ways to fix it, and what those native tools cannot do once your fleet grows past a handful of environments.
What 24/7 ECS costs you
A 10-environment fleet with 8 services each, running 0.5 vCPU / 1 GB tasks, costs $1,442/month in compute running 24/7. Scheduled to business hours (Mon–Fri 9am–7pm), the same fleet costs $429/month in compute — a 70% reduction. Fixed overhead (ALB, NAT Gateway, CloudWatch: roughly $90/env) keeps running either way.
“A scheduled task is suitable when you have tasks to run at set intervals in your cluster. The EventBridge Scheduler schedule that you create can run one or more tasks in your cluster at specified times.”
— AWS ECS Developer Guide: Schedule your containers on Amazon ECS
Three native ways to schedule ECS environments
AWS provides three tools: EventBridge Scheduler (recommended — fully managed, per-invocation pricing), Application Auto Scaling scheduled actions (free, but one schedule per service), and DIY Lambda or CLI scripts (full control, full maintenance burden). All three work. None of them manage a fleet.
EventBridge Scheduler triggers the ECS UpdateService API with desiredCount=0 to stop and desiredCount=N to restart. Two schedules per service: one cron for stop, one for start. Timezone-aware. Fully managed — no Lambda, no code.
$1.00 per million invocations. First 14 million/month are free. Two invocations/day per environment across a 10-env fleet = 600 invocations/month. Cost: $0.00.
Each schedule targets one service. 10 environments × 8 services = 80 services = 160 schedules to create and maintain. No fleet view. No way to say "stop all of dev-qa1 now."
# Stop all services in an environment at 7pm Mon-Fri
aws scheduler create-schedule \
--name "stop-dev-env-evening" \
--schedule-expression "cron(0 19 ? * MON-FRI *)" \
--schedule-expression-timezone "America/New_York" \
--target '{
"Arn": "arn:aws:scheduler:::aws-sdk:ecs:updateService",
"RoleArn": "arn:aws:iam::ACCOUNT:role/EventBridgeSchedulerRole",
"Input": "{\"cluster\":\"dev-env\",\"service\":\"api\",\"desiredCount\":0}"
}' \
--flexible-time-window '{"Mode":"OFF"}'
# Restart the same service at 9am Mon-Fri
aws scheduler create-schedule \
--name "start-dev-env-morning" \
--schedule-expression "cron(0 9 ? * MON-FRI *)" \
--schedule-expression-timezone "America/New_York" \
--target '{
"Arn": "arn:aws:scheduler:::aws-sdk:ecs:updateService",
"RoleArn": "arn:aws:iam::ACCOUNT:role/EventBridgeSchedulerRole",
"Input": "{\"cluster\":\"dev-env\",\"service\":\"api\",\"desiredCount\":2}"
}' \
--flexible-time-window '{"Mode":"OFF"}'Source: AWS EventBridge Scheduler docs · EventBridge Scheduler pricing, verified June 2026
Register each ECS service as a scalable target, then attach scheduled actions that set MinCapacity=0,MaxCapacity=0 at the stop time and restore desired capacity in the morning. Built into ECS — no extra service to manage.
- —One action per service per direction. 80 services = 160 actions.
- —Ordering not guaranteed across scalable targets (AWS docs state this explicitly).
- —No audit trail — events in CloudTrail but not in a human-readable ops log.
# Register ECS service as a scalable target
aws application-autoscaling register-scalable-target \
--service-namespace ecs \
--resource-id service/dev-env/api \
--scalable-dimension ecs:service:DesiredCount \
--min-capacity 0 --max-capacity 10
# Schedule stop at 7pm Mon-Fri
aws application-autoscaling put-scheduled-action \
--service-namespace ecs \
--resource-id service/dev-env/api \
--scheduled-action-name stop-evening \
--scalable-dimension ecs:service:DesiredCount \
--schedule "cron(0 19 ? * MON-FRI *)" \
--scalable-target-action MinCapacity=0,MaxCapacity=0
# Schedule start at 9am Mon-Fri
aws application-autoscaling put-scheduled-action \
--service-namespace ecs \
--resource-id service/dev-env/api \
--scheduled-action-name start-morning \
--scalable-dimension ecs:service:DesiredCount \
--schedule "cron(0 9 ? * MON-FRI *)" \
--scalable-target-action MinCapacity=2,MaxCapacity=10Source: Application Auto Scaling scheduled scaling docs, verified June 2026
An EventBridge rule triggers a Lambda function on a cron schedule. The Lambda paginates through all services in a cluster by tag, calls update_service with desiredCount=0 on each, then does the reverse in the morning. Works well for one cluster.
- —DST: UTC crons shift an hour twice a year. Teams notice at 9am Monday.
- —ALB and NAT Gateway are not stopped by the Lambda — they keep billing.
- —Exclusion lists ("keep dev-alice on today") are hard-coded env vars.
- —Runtime upgrades, IAM changes, and service renames are your problem.
import boto3
ecs = boto3.client("ecs", region_name="us-east-1")
def handler(event, context):
action = event.get("action", "stop") # "stop" or "start"
cluster = event.get("cluster", "dev-env")
desired = 0 if action == "stop" else 2
paginator = ecs.get_paginator("list_services")
for page in paginator.paginate(cluster=cluster):
for svc_arn in page["serviceArns"]:
svc_name = svc_arn.split("/")[-1]
ecs.update_service(
cluster=cluster,
service=svc_name,
desiredCount=desired,
)
print(f"{action}: {svc_name} -> desiredCount={desired}")
# Trigger with EventBridge at 7pm: {"action": "stop", "cluster": "dev-env"}
# Trigger with EventBridge at 9am: {"action": "start", "cluster": "dev-env"}
#
# Problems you will hit at fleet scale:
# - Hard-coded cluster name: works for 1 env, breaks for 10
# - No ordering: db, api, and worker all stop simultaneously
# - No override: "keep dev-alice running today" = editing Lambda config
# - No audit: who toggled what and when? Check CloudTrail. Good luck.What native tools cannot do at fleet scale
EventBridge Scheduler and Application Auto Scaling handle individual services. At 10 environments with 8 services each, you are managing 160 individual schedules. There is no fleet-wide toggle, no per-environment override, no audit trail of who turned what on, and no dependency ordering on startup.
| Capability | EventBridge | App Auto Scaling | Lambda DIY | Fortem |
|---|---|---|---|---|
| Fleet-wide stop (all envs, one action) | ✗ | ✗ | Build it | ✓ |
| Per-env override ("keep dev-alice on today") | ✗ | ✗ | Build it | ✓ |
| Audit trail: who toggled what and when | ✗ | ✗ | Build it | ✓ |
| Dependency ordering (db → app → worker) | ✗ | ✗ | Build it | ✓ |
| Developer self-service (no AWS console) | ✗ | ✗ | Build it | ✓ |
At 3 environments, Lambda DIY is fine. At 10+, “Build it” becomes a platform project. The table above is the backlog that accumulates when you outgrow a single Lambda function.
Why 160 schedules matter: EventBridge schedules have no concept of an environment. To stop use1-dev-qa1 you call UpdateService on all 8 services individually. 10 environments × 8 services × 2 directions = 160 EventBridge schedules. Each one needs a name, a cron expression, an IAM role ARN, and the correct cluster/service parameters. When a service gets renamed or a new environment is cloned, you update schedules manually. There is no index of what is scheduled and what is not.
Why audit trails matter: When an environment fails to start at 9am, was it the schedule, an engineer, or a deployment that set desiredCount=0? CloudTrail shows the API call. It does not show you a human-readable ops log tied to a person and a reason. Building that from CloudTrail is a separate project.
See your fleet's scheduling savings
Enter your fleet size. The calculator uses published AWS Fargate pricing ($0.04048/vCPU-hr, us-east-1, Linux/x86) and shows the 24/7 baseline against a scheduled fleet. Fixed overhead (ALB, NAT, CloudWatch: ~$90/env) stays either way — the calculator shows compute savings only.
How much are you paying for environments nobody's using right now?
Adjust the sliders to match your fleet. Numbers update live using published AWS Fargate rates (us-east-1, Linux/x86).
How often do your dev/staging environments actually need to run?
Fortem starts at $790/month. Fortem starts at $790/month — it pays for itself in month one.
Your fleet is burning $1,215/mo right now.
Want us to verify this against your real AWS bill and show you exactly which environments are idle?
Show the math
All rates from AWS Fargate pricing page (us-east-1, Linux/x86, on-demand):
Baseline monthly cost (24/7, on-demand):
12 envs × 8 services × (0.5 vCPU × $0.04048 + 1 GB × $0.004445) × 730 hrs
= $1,730 / month
Schedule multiplier:
50 hrs ÷ 168 hrs/week = 29.8%
Optimized monthly cost:
$1,730 × 29.8% = $515/mo
Monthly savings:
$1,730 − $515 = $1,215/mo
Months to recoup Fortem plan ($790/month):
⌈$790 ÷ 1,215⌉ = 1 month
Compute-only — fixed overhead (ALB, NAT, CloudWatch ≈ ~$90/env) stays even when tasks stop. Full breakdown → That's why we say 60–70%, not 90%.
Frequently asked questions
How do I stop an ECS service on a schedule?
+
Use EventBridge Scheduler to call the ECS UpdateService API with desiredCount=0 at your stop time (e.g. 7pm Mon–Fri) and a second schedule to restart it in the morning. Each schedule targets one ECS service. Setup for a single service takes about 15 minutes. For 10+ environments with 8 services each, you end up managing 160 schedules — at that point most teams use a fleet scheduling tool.
How do I turn off ECS tasks when nobody is using them?
+
Set desiredCount=0 on the ECS service via the AWS CLI, console, or an automated schedule. When desiredCount=0, all Fargate tasks stop and you pay $0 in compute. A common approach: tag all non-production environments, then run an EventBridge Scheduler cron that calls UpdateService on each tagged service at the end of the business day and reverses it each morning.
Does ECS handle autoscaling and scheduling natively?
+
ECS supports two types of scaling: Application Auto Scaling (reactive — scales based on CPU/memory metrics) and scheduled actions (time-based — sets min/max capacity at a cron schedule). Neither gives you a fleet-wide on/off toggle. Scheduled actions target one service at a time, so 10 environments with 8 services each = 160 individual scheduled actions. For fleet-level scheduling with per-env overrides and an audit trail, you need a tool on top of the native AWS options.
Can ECS Fargate scale to zero?
+
Yes. Set desiredCount=0 on an ECS service and all Fargate tasks stop immediately — you pay $0 in compute. Unlike EKS, there are no nodes lingering for kube-system. The catch: fixed per-environment costs (ALB, NAT Gateway, CloudWatch — roughly $90/env/month) stay on the bill regardless of whether tasks are running. Realistic compute savings from scheduling are 60–70%, not 100%.
Does stopping ECS tasks affect my RDS database or other services?
+
Stopping ECS tasks (desiredCount=0) stops only the compute layer. RDS, ElastiCache, S3, and SQS keep running — they are not part of the ECS service. In-flight HTTP requests are dropped when the task stops, so schedule stop times after traffic has died off (e.g. 7pm local time). On restart, make sure your services handle cold-start gracefully — especially if they run database migrations or cache warm-up on boot.
Ready to schedule your fleet without managing 160 EventBridge rules?
Fortem connects to your AWS account in 4 minutes. Set a fleet-wide schedule, give developers an environment override, and see who turned what on — from one dashboard.
Response within 4 hours, weekdays.