Troubleshooting
AWS
Docker & Compose

Fix ECS "Essential Container in Task Exited" for a Django Service

Fix AWS ECS tasks that stop with Essential container in task exited for Django on Fargate or EC2: read the stopped reason and CloudWatch logs, fix missing secrets and permissions, health check failures, exit codes 137 and 1, and migration commands.

Problem statement

An ECS service never stabilizes. Tasks start and stop repeatedly, and the stopped task shows:

Stopped reason: Essential container in task exited
Exit code: 1

The deployment eventually rolls back, or the service keeps replacing tasks. ECS stops a task whenever a container marked essential exits, for any reason. The message only says that the Django container exited. Why it exited is in the container's exit code, the stopped reason details, and its logs in CloudWatch.

Why the task stopsTask startspulls, gets secretsDjango containerruns commandProcess exitserror or killedECS stops taskessential exitedECS reports that the main container stopped; the logs and exit code say why.
  1. Open the stopped task in the console and note the container's exit code and reason.
  2. Read the container logs in CloudWatch (awslogs driver), especially the last lines before exit.
  3. Exit code 1 with a traceback: fix the Django error (settings, missing env var, import error).
  4. Exit code 137: the container was killed, usually out of memory; raise task memory or reduce Gunicorn workers.
  5. ResourceInitializationError or secrets errors: give the task execution role access to Secrets Manager or SSM and fix networking to reach them.

Step-by-step solution

1. Read the stopped task details

aws ecs list-tasks --cluster prod --service-name web --desired-status STOPPED
aws ecs describe-tasks --cluster prod --tasks <task-arn> \
  --query "tasks[0].{stopped:stoppedReason,containers:containers[].{name:name,exit:exitCode,reason:reason}}"

Stopped tasks are kept only for a short time, so check soon after the failure.

2. Read the logs

With the awslogs log driver configured in the task definition:

aws logs tail /ecs/web --since 15m --follow

No log stream at all usually means the container never started (image pull or secrets failure), not that Django crashed. If logs are empty because the app logs to files, fix that first; see Fix Django Logs Not Showing in docker logs.

exit code or reason and fixExit code or reasonCause and fixexit 1 with tracebackDjango startup error;fix settings or envexit 137, OutOfMemorykilled for memory;raise memory or workersResourceInitializationErrorsecrets or image fetch;role and networkhealth checks failedALB target unhealthy;fix path and hostThe exit code narrows the cause before you open the logsdjango-deployment.com
A task replaced for failing health checks may show exit code 0 or 143, not 1.

3. Fix startup errors (exit code 1)

Typical tracebacks: ImproperlyConfigured: The SECRET_KEY setting must not be empty, KeyError: 'DATABASE_URL', ModuleNotFoundError, or a database connection error during an entrypoint migrate. Check that every variable is in the task definition's environment or secrets list. Running migrations from the web container's command means every task retries migrations; run them as a one-off task instead:

aws ecs run-task --cluster prod --task-definition web:42 --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[subnet-abc],securityGroups=[sg-123]}" \
  --overrides '{"containerOverrides":[{"name":"web","command":["python","manage.py","migrate","--noinput"]}]}'

4. Fix memory kills (exit code 137)

Fargate tasks have a fixed memory size. Several Gunicorn workers at 200 MB each exceed a 512 MB task. Either raise memory in the task definition or set workers explicitly:

gunicorn config.wsgi --bind 0.0.0.0:8000 --workers 2 --threads 4 --max-requests 1000

--workers defaults derived from CPU counts can be wrong in containers, so set them yourself.

5. Fix secrets and image fetch errors

ResourceInitializationError: unable to pull secrets or registry auth means the task execution role (not the task role) lacks secretsmanager:GetSecretValue or ssm:GetParameters, or the task in a private subnet cannot reach Secrets Manager, SSM, or ECR. Add the IAM permissions and either a NAT gateway or VPC endpoints for those services.

task lifecycleProvisionENI, subnetPull and secretsexecution roleContainer startsDjango importsHealth checksALB target groupSteady stateserving trafficFailures before start have no app logs; failures after start dodjango-deployment.com
Knowing the stage tells you whether to look at IAM, logs, or the load balancer.

Explanation

In an ECS task definition, a container marked essential: true defines the task's lifetime: when it exits, ECS stops every container in the task and records "Essential container in task exited". The service scheduler then starts a replacement, which fails the same way, so the service churns and the deployment circuit breaker may roll back. The message is a summary; the exit code distinguishes an application error (1), a kill signal (137 for SIGKILL, often memory), and a normal stop (0 or 143), and the logs carry the details.

Edge cases / notes

  • Sidecars. If a non-essential sidecar exits, the task keeps running; if an essential one like a log router exits, the task stops even if Django is fine.
  • Entrypoint scripts. A set -e script that fails before exec gunicorn exits with the failing command's code; log each step.
  • Health check grace period. Slow-starting apps need healthCheckGracePeriodSeconds on the service so the ALB does not kill them while they boot.

Next steps

FAQ

What does exit code 137 mean on ECS?

The container was killed with SIGKILL, most often for exceeding its memory limit.

Where are the Django logs for a stopped task?

In CloudWatch Logs, in the log group set by the awslogs driver in the task definition.

Should migrations run in the container command?

No. Run them as a one-off task before updating the service, so failures do not crash every web task.

Why does the stopped reason not show the error?

ECS only reports that the essential container exited. The application error is in the container logs.

Keep going