Manage env vars in containers
Learn how to manage environment variables in Docker containers: set, pass, and override env vars for configurable, secure deployments.
Focus: manage environment variables in containers
You’ve built a Docker image, run a container, and maybe even written a Dockerfile. But when your app needs to talk to a database, store an API key, or switch between development and production modes, you need a way to pass configuration into the container without hardcoding secrets into the image. That's where environment variables come in — and managing them correctly is the difference between a brittle container and a portable, secure deployment.
The problem this lesson solves
Hardcoding configuration values — like database URLs, API keys, or feature flags — directly into your Dockerfile or application code creates two major problems:
- Security risk — Secrets baked into an image can be extracted by anyone who pulls the image.
- Inflexibility — You’d need a different image for every environment (dev, staging, prod).
Environment variables let you keep the same image but change its behavior at runtime. Without them, your containers are rigid and unsafe. This lesson shows you how to set, pass, and override environment variables in Docker containers using multiple techniques, so your apps stay configurable and your secrets stay secret.
Core concept / mental model
Think of a Docker container as a stripped-down virtual machine that runs your application. Environment variables are like sticky notes posted on the container’s wall — your app can read them at startup to know what database to connect to, which log level to use, or whether to enable debug mode.
Pro tip: Environment variables are not part of the image. They are injected at runtime. This means the same image can behave differently in development, staging, and production simply by passing different env vars.
Why not use a custom config file?
You could put a config file into the image, but then you’d need a different image per environment. Environment variables decouple configuration from the image, following the twelve-factor app methodology. Docker provides several ways to inject them:
- -e or --env flag on docker run
- ENV instruction in Dockerfile (for default values)
- --env-file to load many variables from a file
- Docker Compose environment and env_file keys
Each method has its place, as we'll see in the compare section.
How it works step by step
When a container starts, Docker populates its environment from several sources in a specific priority order (highest wins):
-eflags ondocker run(orenvironmentin Compose)--env-fileloaded at runtimeENVinDockerfile(build-time defaults)- Host environment variables (only if explicitly exported, e.g.,
-e MYVARor--env-file— Docker never leaks host vars automatically)
The step-by-step flow
- Define default env vars in your
DockerfileusingENVfor safe defaults (e.g.,ENV NODE_ENV=production). - Override at runtime with
docker run -e NODE_ENV=developmentwhen you need a different value. - Use
.envfiles for multiple vars: create a file, oneKEY=VALUEper line, then pass it with--env-file .env. - Verify by running
docker exec <container> envor reading the variable inside your app.
Hands-on walkthrough
Let's practice with a simple Python script that reads environment variables. We'll build an image, run it with different env vars, and see how to override defaults.
1. Create a minimal app
Create a file named app.py:
import os
name = os.getenv("NAME", "World")
print(f"Hello, {name}!")
2. Write a Dockerfile with default env var
FROM python:3.11-alpine
ENV NAME="World"
COPY app.py .
CMD ["python", "app.py"]
3. Build and run without overriding
docker build -t env-demo .
docker run env-demo
# Output: Hello, World!
4. Override the default with -e
docker run -e NAME="Alice" env-demo
# Output: Hello, Alice!
5. Use an env file for multiple variables
Create dev.env:
NAME="Bob"
DEBUG=true
Run with the file:
docker run --env-file dev.env env-demo
# Output: Hello, Bob!
Compare options / when to choose what
| Method | When to use | Priority | Security |
|---|---|---|---|
ENV in Dockerfile |
Default values that should be baked into the image | Lowest (overridable) | Secrets? No — avoid hardcoding secrets |
-e on docker run |
Quick overrides for single vars | Highest | Good for one-off secrets (use with caution) |
--env-file |
Many vars, especially secrets | High (same as -e) |
Better: keep file out of version control |
Docker Compose environment |
Multi-service apps | Same as -e |
Use with .env file for secrets |
Docker Compose env_file |
Load vars from a file per service | Same as --env-file |
Recommended for secrets in dev |
Pro tip: For production, never store secrets in Dockerfiles. Use
--env-filewith a file that's outside version control, or use a secret management tool like Docker secrets (Swarm) or HashiCorp Vault.
Troubleshooting & edge cases
1. Variable not passed to the container
Symptom: App can't find DATABASE_URL.
Fix: Verify the variable is set at runtime:
docker run -e DATABASE_URL="postgres://..." my-app
Or check inside the container:
docker exec <container> env | grep DATABASE_URL
2. Host environment variables are NOT automatically passed
Docker does not leak your host's environment variables into the container by default. You must explicitly pass them with -e or --env-file.
Wrong: docker run my-app assumes $HOME is available. Wrong.
Correct: docker run -e HOME=$HOME my-app (passes the host's $HOME).
3. Env file parsing issues
Symptom: Values with spaces or quotes break.
Fix: In .env files, don't wrap values in quotes unless they are part of the value. Docker's parser is simpler than shell's.
# Correct:
NAME=Bob
# Also correct:
NAME="Bob" # quotes become part of the value: sets NAME to "Bob"
# Best practice: no quotes unless needed
4. Overriding ENV in Dockerfile with -e doesn't work?
It does. If you set ENV NAME=World and then run with -e NAME=Alice, NAME will be Alice inside the container because runtime vars override build-time defaults.
What you learned & what's next
You now know how to manage environment variables in containers — from setting defaults in a Dockerfile to overriding them at runtime with -e and --env-file. You've seen the priority order, how to avoid common pitfalls, and when to use each method. You can now:
- Explain why env vars decouple configuration from images
- Set both defaults and overrides using multiple techniques
- Debug missing or misconfigured variables
- Choose the right approach for dev vs. prod
Next step: Learn how to manage multiple containers together with Docker Compose — where environment variables become even more powerful for orchestrating services like web apps, databases, and caches.
Practice recap
Create a new Python app that reads three environment variables: MODE, PORT, and DB_HOST. Write a Dockerfile with sensible defaults, then run the container with -e to override just MODE. Next, create a .env file to override all three and use --env-file to run a second container. Compare the outputs.
Common mistakes
- Hardcoding secrets like database passwords in
DockerfilewithENV— anyone who can pull the image can read them. - Assuming host environment variables automatically leak into the container — they don't; you must explicitly pass them with
-e. - Putting quotes around values in
--env-fileand forgetting that quotes become part of the variable value in Docker's parser. - Using
ENVfor secrets expecting them to be hidden —docker historyshows everyENVinstruction in plain text.
Variations
- Use
docker run --env-file .envfor loading many variables from a file without exposing them in the command line. - In Docker Compose, set
environmentdirectly in the YAML or use anenv_filekey for per-service env var files. - For production secret management, consider Docker Swarm secrets, Kubernetes Secrets, or external tools like HashiCorp Vault instead of plain env files.
Real-world use cases
- Passing
DATABASE_URLto a web app container so it connects to the correct database in dev, staging, or prod without rebuilding the image. - Setting
LOG_LEVEL=debugon a container for troubleshooting without redeploying, then switching back toLOG_LEVEL=info. - Injecting
API_KEYas an env var from a CI/CD pipeline secret store into a container during automated testing.
Key takeaways
- Environment variables decouple configuration from the image, enabling the same image to run differently across environments.
- The highest-priority source wins:
-eflags >--env-file>ENVin Dockerfile. Host env vars are never automatically passed. - Never hardcode secrets in a Dockerfile — use
--env-fileor external secret managers for production. - Use
ENVin Dockerfile for safe defaults only; override at runtime with-eor--env-file. - Verify env vars inside a running container with
docker exec <container> envor by reading them in your app's logs. - For multiple variables,
--env-filekeeps your command line clean and your secrets out of shell history.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.