Using AWS SSO Credentials Inside Containers
On the host, aws s3 ls --profile dev works after aws sso login, but the same call from a container fails with Unable to locate credentials, or with The SSO session associated with this profile has expired or is otherwise invalid after the mount was set up, or — the common "fix" — with nothing at all, because someone pasted a long-lived access key into .env that now sits in every developer's shell history. IAM Identity Center (formerly AWS SSO) issues short-lived credentials to people, which is exactly what local development should use; containers just need a way to see them. This page gives containers SSO-derived credentials safely, as part of local secret vaults and rotation.
The goal: no long-lived access keys on laptops, credentials that expire on their own, and the same profile working on the host and in containers.
Diagnostic
Compare the credential chain on the host with what the container sees:
#!/usr/bin/env bash
set -euo pipefail
aws configure list --profile dev
aws sts get-caller-identity --profile dev --query Arn --output text
docker compose exec -T api sh -c 'env | grep -E "^AWS_" | sed -E "s/(SECRET|TOKEN)=.*/\1=***/"; ls -la ~/.aws 2>&1 | head -3'
docker compose exec -T api aws sts get-caller-identity 2>&1 | tail -1
grep -nE '^AWS_(ACCESS_KEY_ID|SECRET_ACCESS_KEY)=AKIA' .env 2>/dev/null && echo "long-lived key in .env" || true
Expected bad output:
Name Value Type Location
profile dev manual --profile
access_key ****************QX7M sso
arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_Developer_3f1c/[email protected]
AWS_REGION=eu-west-1
ls: cannot access '/root/.aws': No such file or directory
Unable to locate credentials. You can configure credentials by running "aws configure".
The host resolves credentials through SSO; the container has neither the profile configuration nor the cached SSO token, so its credential chain finds nothing.
Root cause
AWS SDKs and the CLI search a chain of credential sources: environment variables, the shared config and credentials files for the selected profile, and then instance or container metadata endpoints. With IAM Identity Center, the profile in ~/.aws/config names an SSO session, and aws sso login writes an access token to ~/.aws/sso/cache; the SDK exchanges that token for short-lived role credentials on demand. A container has its own filesystem and environment, so none of that exists inside it, and there is no metadata endpoint on a laptop. Mounting only ~/.aws/config is not enough — the token cache is needed too — and the token itself expires (typically after 8 to 12 hours), producing the "session expired" error until the developer logs in again on the host. Long-lived IAM user keys "fix" all of this by bypassing SSO, at the cost of permanent credentials spread across laptops.
Resolution
- Mount the AWS directory read-only and select the profile so the SDK inside the container follows the same SSO chain as the host:
x-aws-sso: &aws-sso
volumes:
- ${HOME}/.aws:/home/app/.aws:ro
environment:
AWS_PROFILE: dev
AWS_SDK_LOAD_CONFIG: "1"
AWS_REGION: eu-west-1
services:
api:
<<: *aws-sso
build: ./api
user: "${UID:-1000}:${GID:-1000}"
environment:
HOME: /home/app
The SDK needs write access to refresh cached role credentials in some versions; if it logs a read-only error, mount ~/.aws/sso/cache and ~/.aws/cli/cache read-write and keep config read-only.
- Or export temporary credentials for a single run, which avoids mounting anything and suits tools that do not support SSO profiles:
#!/usr/bin/env bash
set -euo pipefail
aws sso login --profile dev
eval "$(aws configure export-credentials --profile dev --format env)"
docker compose up -d --force-recreate api
docker compose exec -T api aws sts get-caller-identity --query Arn --output text
export-credentials prints short-lived AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN; Compose passes them through if the service lists them with empty values (AWS_SESSION_TOKEN:), which forwards the host value.
- Handle expiry explicitly in the bootstrap or task runner, so developers see a clear prompt instead of an SDK error:
#!/usr/bin/env bash
set -euo pipefail
if ! aws sts get-caller-identity --profile dev >/dev/null 2>&1; then
echo "AWS SSO session expired; opening browser to sign in"
aws sso login --profile dev
fi
- Delete long-lived keys from
.envfiles and deactivate the IAM users behind them, after confirming nothing else uses them.
Expected output
$ docker compose exec -T api aws sts get-caller-identity --query Arn --output text
arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_Developer_3f1c/[email protected]
$ docker compose exec -T api aws s3 ls s3://acme-dev-uploads | head -2
2026-09-12 10:14:22 18231 invoices/2026-09-12.pdf
$ grep -cE '^AWS_ACCESS_KEY_ID=AKIA' .env || echo "no long-lived keys in .env"
no long-lived keys in .env
The container assumes the developer's SSO role, API calls succeed with short-lived credentials, and no permanent keys remain in local configuration.
CloudTrail now records each call under the developer's own identity rather than a shared IAM user, which makes audits and incident investigations straightforward, and access ends automatically when the SSO session expires or when the person leaves and is removed from the identity provider. That is the security case for doing this even when a pasted key "works".
Prevention
Block long-lived keys in commits and env files with the secret scanner's AWS rules, as in blocking committed secrets with a gitleaks pre-commit hook.
Prefer emulators for most local work so AWS credentials are needed only for tasks that genuinely touch real accounts; see running S3 and SQS locally with LocalStack.
Check the session in
make doctorand print the login command when it has expired.
Platform caveats
macOS (Docker Desktop):
${HOME}/.awsresolves to/Users/<name>/.aws, which Docker Desktop shares by default. If the home directory is excluded from file sharing, the mount is empty.
WSL2: run
aws sso logininside WSL so the cache is in the Linux home directory that containers mount; a login on the Windows side writes to a different directory.
Rootless or non-root containers: mount to the container user's home and set
HOMEaccordingly, or the SDK looks in/root/.aws.
Dev containers: the AWS CLI Feature plus a
mountsentry for~/.awsachieves the same result inside a dev container.
Rollback
Remove the mount and environment block; containers fall back to whatever credentials are in their environment:
#!/usr/bin/env bash
set -euo pipefail
git checkout HEAD~1 -- compose.yaml
docker compose up -d --force-recreate api
Frequently Asked Questions
Why does the container say "Unable to locate credentials" when the host works?
The container cannot see ~/.aws or the SSO token cache, and there is no metadata endpoint locally. Mount ~/.aws read-only and set AWS_PROFILE, or export temporary credentials into the container's environment.
What does "The SSO session ... has expired" mean?
The cached SSO token is past its lifetime. Run aws sso login --profile dev on the host; mounted containers pick up the new token without restarting.
Is mounting ~/.aws into containers safe?
It gives the container the same access the developer has, for as long as the session lasts. Mount it read-only, only into services that need AWS, and prefer emulators for services that do not.
Can CI use the same approach?
CI should use OIDC federation to assume a role directly (for example aws-actions/configure-aws-credentials with role-to-assume), which issues short-lived credentials without any stored keys.