Docker and Kubernetes course menu
Docker Environment Variables, .env Files and Secrets
Configure containers with -e and --env-file, understand ENV and ARG, keep secrets out of images, and use mounted secret files.
On this page
The same image should run in development, testing and production, so settings must live outside it. The usual way is environment variables.
Set variables#
-e sets a variable for one container:
docker run --rm -e APP_ENV=production -e PORT=9000 alpine:3.24 sh -c 'echo "$APP_ENV on port $PORT"'production on port 9000Use a file#
--env-file loads many at once. The format is one KEY=value per line:
cat > .env <<'EOF'
APP_ENV=staging
LOG_LEVEL=debug
# Comments are allowed
EOF
docker run --rm --env-file .env alpine:3.24 sh -c 'env | grep -E "APP_ENV|LOG_LEVEL" | sort'APP_ENV=staging
LOG_LEVEL=debugAdd .env to both .gitignore and .dockerignore. Commit a .env.example with fake values, so that others know which settings exist.
Defaults in the image#
ENV in a Dockerfile sets a default, and -e overrides it:
cat > Dockerfile <<'EOF'
FROM alpine:3.24
ENV COLOUR=blue
CMD ["sh", "-c", "echo $COLOUR"]
EOF
docker build -q -t colour . > /dev/null
docker run --rm colour
docker run --rm -e COLOUR=green colourblue
greenDo not bake in secrets#
Anything in a Dockerfile can be read from the image, even after a later step removes it:
cat > Dockerfile <<'EOF'
FROM alpine:3.24
ARG API_KEY
RUN echo "building"
ENV TOKEN=abc123
EOF
docker build -q --build-arg API_KEY=super-secret -t leaky . > /dev/null
docker history --no-trunc --format '{{.CreatedBy}}' leaky | grep -o 'API_KEY=[a-z-]*'
docker inspect --format '{{.Config.Env}}' leakyAPI_KEY=super-secret
API_KEY=super-secret
[PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin TOKEN=abc123]Both values can be read by anyone who has the image.
Environment variables are not perfect#
Run-time variables are better, but they can still be seen by anyone who can inspect the container:
docker run -d --name app -e DB_PASSWORD=hunter2 alpine:3.24 sleep 30 > /dev/null
docker inspect --format '{{.Config.Env}}' app | grep -o 'DB_PASSWORD=[a-z0-9]*'
docker rm -f app > /dev/nullDB_PASSWORD=hunter2Secret files#
A better way is to mount a secret as a read-only file, which the app reads at start-up. Many official images support a _FILE variable for this:
mkdir secrets
echo "s3cret-value" > secrets/db_password
chmod 600 secrets/db_password
docker run --rm -v "$PWD/secrets/db_password":/run/secrets/db_password:ro alpine:3.24 sh -c 'echo "Password length: $(wc -c < /run/secrets/db_password)"'Password length: 13For example, postgres accepts POSTGRES_PASSWORD_FILE=/run/secrets/db_password. Compose has a secrets section that does this mounting for you. In Kubernetes, you use Secrets. In the cloud, use a service such as AWS Secrets Manager.
Secrets during a build#
If a build needs a token, for example to install private packages, BuildKit secrets keep it out of the layers:
RUN --mount=type=secret,id=npm_token sh -c 'NPM_TOKEN=$(cat /run/secrets/npm_token) npm ci'docker build --secret id=npm_token,src=.npm_token -t app .Common mistakes#
Practice#
Practice exercise
Create prod.env with MODE=prod and WORKERS=4, and run an Alpine container that uses it, overriding WORKERS to 8 with -e. Print both values.
Show solution
printf 'MODE=prod\nWORKERS=4\n' > prod.env
docker run --rm --env-file prod.env -e WORKERS=8 alpine:3.24 sh -c 'echo "$MODE $WORKERS"'prod 8Key takeaway#
Next: Getting Started with Compose.
Finished this lesson?
Related lessons
Recommended Docker and Kubernetes resources
- Docker Deep Dive by Nigel Poulton
A practical guide to how Docker works, for readers who have finished the basics.
Some links are affiliate links. If you buy through them, Laviyx Learn may earn a commission at no extra cost to you. As an Amazon Associate, we earn from qualifying purchases.