Skip to content

Docker Environment Variables

Configuration that varies between environments (dev, staging, prod) should be passed to containers via environment variables — not baked into the image.

Terminal window
# Single variable
docker run -e NODE_ENV=production my-app
# Multiple variables
docker run \
-e NODE_ENV=production \
-e PORT=3000 \
-e DATABASE_URL=postgresql://db:5432/prod \
my-app
# From a file
docker run --env-file .env my-app
.env
NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://db:5432/myapp
JWT_SECRET=super-secret-key
Terminal window
docker run --env-file .env my-app

Never commit .env files with real secrets. Commit .env.example with placeholder values instead.

# Set a default value (can be overridden at runtime)
ENV NODE_ENV=production
ENV PORT=3000
# Or on one line
ENV NODE_ENV=production PORT=3000

ARG vs ENV:

  • ARG — available only during build time (docker build --build-arg NAME=value)
  • ENV — available at build time AND at runtime in the container
ARG APP_VERSION=1.0.0
ENV APP_VERSION=${APP_VERSION} # promote ARG to ENV for runtime access
services:
api:
image: my-app
environment:
NODE_ENV: production
PORT: 3000
DATABASE_URL: postgresql://db:5432/myapp

Or reference host environment variables:

services:
api:
environment:
- NODE_ENV # inherits from host shell
- DATABASE_URL # inherits from host shell
- JWT_SECRET=${MY_SECRET_VAR} # maps a differently-named host var

Using an env file in Compose:

services:
api:
env_file:
- .env
- .env.production # overrides .env for production

For sensitive values in production, use dedicated secret management:

Docker Swarm Secrets:

Terminal window
echo "my-db-password" | docker secret create db-password -
docker service create \
--secret db-password \
my-app

Inside the container the secret is mounted at /run/secrets/db-password.

Kubernetes Secrets:

env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password

Cloud Secret Managers:

  • Azure Key Vault → inject via App Service config or Kubernetes CSI driver
  • AWS Secrets Manager → IAM role + SDK or Kubernetes External Secrets
  • GCP Secret Manager → Workload Identity + --set-secrets in Cloud Run
const port = parseInt(process.env.PORT ?? '3000', 10);
const dbUrl = process.env.DATABASE_URL;
if (!dbUrl) throw new Error('DATABASE_URL is required');

When a variable is defined in multiple places, Compose applies this priority (highest first):

  1. Values set with docker compose run -e VAR=value
  2. Shell environment variables
  3. environment: in docker-compose.yml
  4. env_file: files
  5. ENV in the Dockerfile
  • Never hardcode secrets in Dockerfile or docker-compose.yml
  • Never pass secrets as ARG — they appear in docker history
  • Use --env-file with files outside source control for local dev
  • In CI/CD, inject secrets through the pipeline’s secret store (GitHub Actions Secrets, GitLab CI Variables)
  • In production, use a dedicated secret manager rather than .env files on servers
  • Scan images for secrets with tools like trufflehog or gitleaks