Docker Fundamentals · Module 08 of 12

Runtime configuration and environment

Separate image contents from environment-specific configuration using environment variables, files, mounts and safe secret-handling principles.

Learning objectives

By the end of this module, you should be able to:

  • Pass runtime environment variables without rebuilding an image.
  • Explain why secrets should not be baked into image layers.
  • Use read-only configuration mounts where appropriate.

Build once, configure at runtime

A reusable image should not need to be rebuilt just because an environment name, feature flag or service endpoint changes. docker run -e, --env-file and Compose environment settings inject runtime configuration.

Environment variables are configuration, not a secret vault

Environment variables are convenient but can be visible through process/container inspection and operational tooling. Treat credentials and private keys with stronger secret-management controls appropriate to your platform. The key principle is to keep secrets out of image layers and source repositories.

Configuration can also be mounted

Read-only bind mounts or volumes can provide configuration files without baking them into the image. This is useful when the application expects file-based configuration, certificates or policy files.

Worked examples

See the idea in practice.

Inject environment values

docker run --rm -e APP_ENV=training -e LOG_LEVEL=info alpine:3.20 env | grep -E 'APP_ENV|LOG_LEVEL'
  • The image remains unchanged.
  • Different containers can receive different runtime values.

Use an env file

printf 'APP_ENV=lab\nLOG_LEVEL=debug\n' > app.env
docker run --rm --env-file app.env alpine:3.20 env
  • --env-file is convenient for non-secret lab configuration.
  • Do not commit sensitive production env files to source control.
Hands-on lab

Lab — Run the same image with different configuration

Goal: Prove that one immutable image can behave differently based on runtime environment without rebuilding.

STEP 1

Create two environment files

mkdir -p ~/docker-fundamentals/config && cd ~/docker-fundamentals/config
printf 'APP_ENV=development\nMESSAGE=hello-dev\n' > dev.env
printf 'APP_ENV=production\nMESSAGE=hello-prod\n' > prod.env
STEP 2

Run development configuration

docker run --rm --env-file dev.env alpine:3.20 sh -c 'echo "$APP_ENV $MESSAGE"'
STEP 3

Run production configuration

docker run --rm --env-file prod.env alpine:3.20 sh -c 'echo "$APP_ENV $MESSAGE"'
STEP 4

Compare image identity

docker image inspect alpine:3.20 --format '{{.Id}}'

Verify

  • Both runs use the same image.
  • Output differs because runtime configuration differs.

Expected outcome

  • One run prints development hello-dev; the other prints production hello-prod.

If it fails

  • If shell expansion happens on the host unexpectedly, pay attention to single versus double quoting.
  • Avoid placing real credentials in demonstration env files.
Real-world connection

Organizations promote the same application image from test to production while injecting environment-specific endpoints and secrets at deployment time.

Avoid these traps

Common mistakes

  • Rebuilding an image for every environment-specific URL or flag.
  • Baking API keys into Dockerfile ENV values.
  • Assuming environment variables are invisible to administrators and inspection tooling.
Knowledge check

Can you explain it without looking back?

Open each answer only after you have tried to answer the question yourself.

1Why separate runtime configuration from the image?

So the same tested image can be promoted between environments without rebuilding it.

2Why avoid baking secrets into image layers?

Image layers can be distributed, cached and inspected, making embedded secrets difficult to contain or remove reliably.

3What does --env-file do?

It loads environment variables from a file into the container at runtime.