Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

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

Sponsored

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:

  1. Security risk — Secrets baked into an image can be extracted by anyone who pulls the image.
  2. 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):

  1. -e flags on docker run (or environment in Compose)
  2. --env-file loaded at runtime
  3. ENV in Dockerfile (build-time defaults)
  4. Host environment variables (only if explicitly exported, e.g., -e MYVAR or --env-file — Docker never leaks host vars automatically)

The step-by-step flow

  1. Define default env vars in your Dockerfile using ENV for safe defaults (e.g., ENV NODE_ENV=production).
  2. Override at runtime with docker run -e NODE_ENV=development when you need a different value.
  3. Use .env files for multiple vars: create a file, one KEY=VALUE per line, then pass it with --env-file .env.
  4. Verify by running docker exec <container> env or 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-file with 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 Dockerfile with ENV — 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-file and forgetting that quotes become part of the variable value in Docker's parser.
  • Using ENV for secrets expecting them to be hidden — docker history shows every ENV instruction in plain text.

Variations

  1. Use docker run --env-file .env for loading many variables from a file without exposing them in the command line.
  2. In Docker Compose, set environment directly in the YAML or use an env_file key for per-service env var files.
  3. 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_URL to a web app container so it connects to the correct database in dev, staging, or prod without rebuilding the image.
  • Setting LOG_LEVEL=debug on a container for troubleshooting without redeploying, then switching back to LOG_LEVEL=info.
  • Injecting API_KEY as 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: -e flags > --env-file > ENV in Dockerfile. Host env vars are never automatically passed.
  • Never hardcode secrets in a Dockerfile — use --env-file or external secret managers for production.
  • Use ENV in Dockerfile for safe defaults only; override at runtime with -e or --env-file.
  • Verify env vars inside a running container with docker exec <container> env or by reading them in your app's logs.
  • For multiple variables, --env-file keeps your command line clean and your secrets out of shell history.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.