Q: Your organization had an API key accidentally committed to GitHub because a developer used `.env` files interchangeably between Docker Compose, Dockerfiles, and runtime environments. Another team baked database passwords into an image using `ENV DB_PASSWORD=secret`. Development teams are confused about variable precedence, layer caching impacts, and runtime visibility. You must establish a clear configuration architecture distinguishing build-time arguments, runtime variables, and secret injection.
Design a secure, 12-factor application configuration hierarchy. Distinguish between build-time `ARG`, runtime `ENV`, `.env` file interpolation, Docker Compose environments, and external secrets vaults.
Want to master this scenario in a live sandbox? KodeKloud's Docker Certified Associate (DCA) Hands-On Lab Course covers this exact problem with hands-on terminal drills.
🛠️ Production Runbook & Step-by-Step Resolution
Map the Configuration Scope and Security Matrix
Establish strict architectural boundaries: `ARG` is build-time only (accessible during `docker build`, baked into metadata). `ENV` is build-time AND runtime (persists in running containers and image config). `.env` is Compose interpolation only (expands variables inside `compose.yaml`). Secrets managers inject credentials at runtime outside the image.
<!-- Configuration Scope Matrix -->
Mechanism Build Phase Runtime Phase Image Metadata? Security Level
ARG YES NO YES (Plaintext) Low (No secrets!)
ENV YES YES YES (Plaintext) Low (No secrets!)
.env file NO (Compose) NO NO Medium (Compose syntax only)
Secret Mount YES NO NO High (BuildKit RAM)
Vault/K8s NO YES NO High (Runtime In-Memory)
Implement Safe Build-Time Arguments (ARG) for Dynamic Tooling
Use `ARG` strictly for non-sensitive parameters such as package versions, compiler flags, and target environments.
# Dockerfile
ARG NODE_VERSION=20.11.0
FROM node:${NODE_VERSION}-alpine
ARG BUILD_COMMIT=unknown
ENV APP_COMMIT=${BUILD_COMMIT} # Safe: Git SHA is not sensitive
WORKDIR /app
Configure 12-Factor Runtime Environment Injection
Inject non-sensitive runtime configurations (port numbers, log levels, service URLs) via container runtime flags or environment files without hardcoding in the image.
# docker-compose.yml
services:
api:
image: my-api:latest
environment:
- PORT=8080
- LOG_LEVEL=info
- DB_HOST=postgres.internal
env_file:
- .env.production # Non-sensitive environment configuration
Enforce External Vaults for Sensitive Credentials
For API keys, private certificates, and passwords, never use `ENV` or `.env`. Inject credentials as in-memory files at runtime using Docker Compose secrets, HashiCorp Vault Agent, or AWS Secrets Manager.
secrets:
- db_password
secrets:
db_password:
file: /secure/vault/db_password.txt
- W
- e
- e
- l
- i
- m
- i
- n
- a
- t
- e
- d
- c
- r
- e
- d
- e
- n
- t
- i
- a
- l
- l
- e
- a
- k
- a
- g
- e
- a
- c
- r
- o
- s
- s
- 1
- 5
- 0
- r
- e
- p
- o
- s
- i
- t
- o
- r
- i
- e
- s
- b
- y
- e
- s
- t
- a
- b
- l
- i
- s
- h
- i
- n
- g
- a
- s
- t
- r
- i
- c
- t
- c
- o
- n
- f
- i
- g
- u
- r
- a
- t
- i
- o
- n
- h
- i
- e
- r
- a
- r
- c
- h
- y
- .
- `
- A
- R
- G
- `
- i
- s
- s
- t
- r
- i
- c
- t
- l
- y
- r
- e
- s
- e
- r
- v
- e
- d
- f
- o
- r
- b
- u
- i
- l
- d
- v
- e
- r
- s
- i
- o
- n
- s
- ,
- `
- E
- N
- V
- `
- i
- s
- u
- s
- e
- d
- e
- x
- c
- l
- u
- s
- i
- v
- e
- l
- y
- f
- o
- r
- n
- o
- n
- -
- s
- e
- n
- s
- i
- t
- i
- v
- e
- r
- u
- n
- t
- i
- m
- e
- d
- e
- f
- a
- u
- l
- t
- s
- ,
- a
- n
- d
- p
- r
- o
- d
- u
- c
- t
- i
- o
- n
- p
- a
- s
- s
- w
- o
- r
- d
- s
- a
- r
- e
- p
- r
- o
- h
- i
- b
- i
- t
- e
- d
- f
- r
- o
- m
- D
- o
- c
- k
- e
- r
- f
- i
- l
- e
- s
- ,
- i
- n
- j
- e
- c
- t
- e
- d
- i
- n
- s
- t
- e
- a
- d
- a
- t
- r
- u
- n
- t
- i
- m
- e
- a
- s
- i
- n
- -
- m
- e
- m
- o
- r
- y
- s
- e
- c
- r
- e
- t
- s
- f
- r
- o
- m
- H
- a
- s
- h
- i
- C
- o
- r
- p
- V
- a
- u
- l
- t
- .