Secrets the Right Way: Secrets Manager & SSM
Inject sensitive values at runtime from Secrets Manager or SSM Parameter Store instead of baking them into environment variables.
If there is one thing that separates a hardened ECS setup from a fragile one, it is how secrets are handled. Plain environment entries feel convenient, but they show up in describe-task-definition, in the console, in CloudTrail, and in anyone's terminal history. Treat them as public.
Use the secrets field
The secrets block maps an environment variable name to a value stored in AWS Secrets Manager or SSM Parameter Store. ECS resolves it at task start and injects it into the container — the actual value never appears in the task definition.
- Secrets Manager when you want rotation, cross-account sharing, or JSON secrets. You can even reference a single key with
arn:...:secret:name:json-key::. - SSM Parameter Store (SecureString) when you want something lighter and cheaper for config-like secrets.
The permission that trips everyone up
The execution role — not the task role — is what pulls secrets at launch. If the value is encrypted with a customer-managed KMS key, that role also needs kms:Decrypt. A task stuck in PENDING with a ResourceInitializationError is almost always a missing secrets or KMS permission on the execution role.
Rule I never break: no secret ever lands inenvironmentor a Docker image. If it is sensitive, it comes fromsecretsat runtime.
Example
{
"containerDefinitions": [
{
"name": "api",
"image": "api:42",
"essential": true,
"secrets": [
{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/db-AbCdEf:password::"
},
{
"name": "STRIPE_KEY",
"valueFrom": "arn:aws:ssm:us-east-1:123456789012:parameter/prod/stripe-key"
}
]
}
]
}When to use it
- A team injects database credentials from Secrets Manager at task launch so rotating the secret takes effect on the next deploy with no code change.
- A security audit requires that no plaintext credentials appear in task definition JSON, so all API keys are stored in SSM Parameter Store SecureString parameters.
- A developer tests secret injection locally by storing a fake credential in SSM and running a local container that reads it via the AWS SDK.
More examples
Store Secret in Secrets Manager
Creates a Secrets Manager secret containing a JSON object with database credentials that can be injected into ECS tasks.
aws secretsmanager create-secret \
--name prod/api/db-credentials \
--secret-string '{"username":"dbuser","password":"s3cr3t"}' \
--description 'Production API database credentials'Reference Secret in Container Definition
Uses JSON key selectors to inject individual fields from a single Secrets Manager secret as separate environment variables.
{
"name": "api",
"image": "my-api:latest",
"secrets": [
{
"name": "DB_USERNAME",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/api/db-credentials:username::"
},
{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/api/db-credentials:password::"
}
]
}Rotate Secret and Redeploy Service
Rotates the Secrets Manager secret and forces an ECS rolling deployment so all new tasks launch with the fresh credentials.
# Rotate the secret
aws secretsmanager rotate-secret \
--secret-id prod/api/db-credentials
# Force new tasks to pick up the rotated credentials
aws ecs update-service \
--cluster prod \
--service api \
--force-new-deployment
Discussion