Env Vars & Config
Pass configuration into containers safely using environment variables, env files, and Docker secrets - without baking secrets into your images.
Docker Environment Variables & Container Configuration
Your database URL, API keys, port numbers, feature flags, none of these should be hardcoded into your image. The moment you do that, you need a different image for development, staging, and production. Instead, pass configuration in at runtime using environment variables. Same image, different config, any environment.
Passing Environment Variables with -e
The -e flag sets a single environment variable when starting a container:
docker run -e NODE_ENV=production my-app
# Multiple variables, one -e flag per variable
docker run \
-e NODE_ENV=production \
-e PORT=3000 \
-e LOG_LEVEL=warn \
my-app
You can also forward a variable directly from your host shell, just omit the =value part. This is useful in CI/CD pipelines where secrets are already available as environment variables on the runner.
export API_KEY=abc123
# Docker reads $API_KEY from your shell and passes it in
docker run -e API_KEY my-app
Using an Env File with --env-file
When you have more than a few variables, managing them with individual -e flags gets messy. Use a .env file instead:
# .env
NODE_ENV=production
PORT=3000
DATABASE_URL=postgres://user:pass@db:5432/myapp
REDIS_URL=redis://cache:6379
LOG_LEVEL=info
JWT_SECRET=my-super-secret-key
docker run --env-file .env my-app
Never commit
.envfiles with real secrets to version control. Add.envto your.gitignoreimmediately. Use a.env.examplefile with placeholder values to document what's needed without exposing real credentials.
Viewing Environment Variables in a Running Container
# Print all env vars from inside the container
docker exec my-app env
# Or check from the host without exec-ing in
docker inspect my-app --format='{{range .Config.Env}}{{println .}}{{end}}'
This is useful for verifying your variables were passed correctly, and a good reminder that env vars are visible to anyone who can run docker inspect.
Setting Defaults in Your Dockerfile
Use ENV in your Dockerfile to define sensible defaults that can be overridden at runtime:
ENV PORT=3000
ENV LOG_LEVEL=info
When someone runs your image without specifying these variables, the defaults apply. When they do specify them, their values win:
# Override the image's default LOG_LEVEL
docker run -e LOG_LEVEL=debug my-app
ARG vs ENV
These two Dockerfile instructions are easy to mix up:
ARG | ENV | |
|---|---|---|
| Available during build | ✓ | ✓ |
| Available at runtime | ✗ | ✓ |
Visible in docker inspect | ✗ | ✓ |
| Use for | Build customization | Runtime config defaults |
ARG APP_VERSION=1.0.0
RUN echo "Building version $APP_VERSION"
# APP_VERSION does NOT exist inside the running container
ENV PORT=3000
# PORT IS available inside the running container
How to Handle Secrets Safely
Environment variables are convenient, but they're not the most secure option for sensitive values because they show up in docker inspect output and in process listings. For production, consider better alternatives:
| What You're Storing | Better Approach |
|---|---|
| TLS certificates | Volume-mounted files |
| SSH keys | Volume-mounted files |
| Production DB passwords | Docker Secrets (Swarm) or K8s Secrets |
| API keys / tokens | Secrets manager (Vault, AWS SSM) fetched at runtime |
If environment variables are your only option (common in development), at minimum: never log them, never bake them into the image, and use --env-file so they don't appear in your shell history.
Docker Secrets
In production Docker Swarm environments, Docker Secrets keep sensitive values out of environment variables entirely:
# Create a secret
echo "my-db-password" | docker secret create db_password -
# Use it in a service
docker service create \
--name my-app \
--secret db_password \
my-app:latest
The secret is mounted as a file at /run/secrets/db_password inside the container, not an environment variable, so it won't appear in docker inspect.
Quick Reference
| Command | What It Does |
|---|---|
docker run -e KEY=VALUE | Set a single env var |
docker run -e KEY | Forward $KEY from your shell |
docker run --env-file .env | Load all vars from a file |
docker exec my-app env | Print all env vars in container |
docker inspect my-app | View env vars from host |
Key Takeaways
- Never hardcode config into your image, instead use environment variables so the same image works across dev, staging, and production
-e KEY=VALUEsets a single variable;--env-file .envloads many at onceENVin a Dockerfile sets defaults that can always be overridden at runtime with-eARGis build-time only, it's not available inside the running container but theENVis available- Environment variables are semi-public as they appear in
docker inspectoutput; don't treat them as truly secret in production - For real production secrets, use Docker Secrets, Kubernetes Secrets, or a dedicated secrets manager like Vault or AWS SSM
Frequently Asked Questions
What's the difference between ARG and ENV in a Dockerfile?
ARG is only available during the image build process, and it can't be read by your running application. ENV sets a variable that persists into the running container and can be overridden at runtime with -e. Use ARG for build customization (version numbers, build flags) and ENV for runtime configuration defaults.
Is it safe to put secrets in environment variables?
For local development, it's acceptable. For production, it's not ideal because environment variables show up in docker inspect, process listings, and crash dumps. Use Docker Secrets, Kubernetes Secrets, or a secrets manager for anything sensitive in production.
Can I see what environment variables a container is running with?
Yes, run docker inspect <container> and look for the Env section, or run docker exec <container> env to print them from inside. This means anyone with Docker access on that host can see those values.
What happens if the same variable is set in both --env-file and -e?
The -e flag wins. It takes precedence over values in the env file, so you can use --env-file as a base and selectively override specific variables with -e.
Should I commit my .env file to Git?
Never commit a .env file that contains real credentials. Instead, commit a .env.example file with all the variable names but placeholder values, this documents what's needed without exposing anything sensitive.
How do I pass environment variables in Docker Compose?
In your docker-compose.yml, use the environment key for inline values or env_file to load from a file. It's the same concepts as -e and --env-file in docker run.