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.
- Open the stopped task in the console and note the container's exit code and reason.
- Read the container logs in CloudWatch (
awslogsdriver), especially the last lines before exit. - Exit code 1 with a traceback: fix the Django error (settings, missing env var, import error).
- Exit code 137: the container was killed, usually out of memory; raise task memory or reduce Gunicorn workers.
ResourceInitializationErroror 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.
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.
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 -escript that fails beforeexec gunicornexits with the failing command's code; log each step. - Health check grace period. Slow-starting apps need
healthCheckGracePeriodSecondson the service so the ALB does not kill them while they boot.
Next steps
- Review the full setup with Deploy Django on AWS ECS Fargate.
- For unhealthy targets and 502s, see Fix AWS ALB 502 for Django.
- Manage secrets with Manage Django Secrets With AWS Secrets Manager.
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.