Fix Docker ENV Variables from AI Tools in 12 Minutes

Solve runtime environment variable issues in AI-generated Dockerfiles with proper ARG/ENV usage and build-time configuration.

Problem: AI-Generated Dockerfiles Break Your Environment Variables

You used ChatGPT or Claude to generate a Dockerfile, but your app can't read environment variables at runtime. Database connections fail with "undefined" and API keys are missing.

You'll learn:

  • Why AI tools misuse ARG vs ENV
  • How to fix build-time vs runtime variable access
  • Proper patterns for secrets and configs

Time: 12 min | Level: Intermediate


Why This Happens

AI code generators often confuse ARG (build-time only) with ENV (runtime available). They'll create Dockerfiles that work during docker build but fail when containers actually run.

Common symptoms:

  • process.env.DATABASE_URL returns undefined in Node.js
  • Python's os.getenv('API_KEY') returns None
  • Works locally with .env file but not in container
  • Build succeeds but app crashes on startup

Root cause: Variables defined with ARG disappear after the image is built. Your app needs ENV to access them at runtime.


Solution

Step 1: Identify the Problem Pattern

Check your AI-generated Dockerfile for this common mistake:

# ❌ AI-generated (broken at runtime)
FROM node:22-alpine
ARG DATABASE_URL
ARG API_KEY

COPY . /app
WORKDIR /app
RUN npm install
CMD ["node", "server.js"]

Why it fails: ARG variables only exist during build steps (RUN). When CMD executes, they're gone.

# Test it - see the variables are missing
docker build -t myapp .
docker run myapp printenv | grep DATABASE_URL
# (no output - variable doesn't exist)

Step 2: Fix Runtime Configuration Variables

Use ENV for values needed by your running application:

# ✅ Fixed version
FROM node:22-alpine

# Runtime variables (available to app)
ENV NODE_ENV=production
ENV PORT=3000

COPY . /app
WORKDIR /app
RUN npm install --production

# App reads these at runtime
CMD ["node", "server.js"]

Pass values when running container:

# Override defaults or add new variables
docker run -e DATABASE_URL="postgresql://..." \
           -e API_KEY="sk-..." \
           myapp

Expected: Your app now sees process.env.DATABASE_URL and process.env.API_KEY.


Step 3: Handle Build-Time Dependencies

Use ARG for values needed during image build only:

# ✅ Proper ARG usage
FROM node:22-alpine

# Build-time only (for npm install from private registry)
ARG NPM_TOKEN

# Make available during build
RUN echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc && \
    npm install && \
    rm .npmrc  # Don't bake secrets into image

# Runtime variables
ENV NODE_ENV=production

CMD ["node", "server.js"]

Build with ARG:

docker build --build-arg NPM_TOKEN="npm_xyz123" -t myapp .

Why this works: ARG is perfect for install-time credentials that shouldn't persist in the final image.


Step 4: Combine ARG and ENV for Flexibility

Best pattern - allow defaults with override capability:

# ✅ Production-ready pattern
FROM python:3.12-slim

# Accept build-time argument
ARG APP_ENV=production

# Convert to runtime environment variable
ENV APP_ENV=${APP_ENV}
ENV PYTHONUNBUFFERED=1

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

# App reads APP_ENV at runtime
CMD ["python", "main.py"]

Usage:

# Use default (production)
docker build -t myapp .

# Override for staging
docker build --build-arg APP_ENV=staging -t myapp:staging .

# Further override at runtime if needed
docker run -e APP_ENV=development myapp

Step 5: Handle Secrets Properly

Never use ENV for secrets in production:

# ❌ Never do this - secrets visible in image
ENV API_KEY=sk-1234567890abcdef

# ✅ Use runtime injection instead
# (no API_KEY in Dockerfile at all)

Secure approaches:

# 1. Environment file
docker run --env-file .env.production myapp

# 2. Docker secrets (Swarm/Compose)
docker service create --secret db_password myapp

# 3. Kubernetes secrets
kubectl create secret generic api-keys \
  --from-literal=API_KEY=sk-xyz

Verification

Test Environment Variable Access

Create test script:

// test-env.js
console.log('DATABASE_URL:', process.env.DATABASE_URL ? '✓ Set' : '✗ Missing');
console.log('API_KEY:', process.env.API_KEY ? '✓ Set' : '✗ Missing');
console.log('NODE_ENV:', process.env.NODE_ENV);

Run container:

docker run -e DATABASE_URL="postgresql://localhost/test" \
           -e API_KEY="test-key" \
           myapp node test-env.js

You should see:

DATABASE_URL: ✓ Set
API_KEY: ✓ Set
NODE_ENV: production

Verify Secrets Aren't Baked In

# Check image doesn't contain secrets
docker history myapp --no-trunc | grep -i "api_key\|password\|secret"
# (should return nothing)

If it fails:

  • Error: "environment variable not set" - Check you're using -e flag when running
  • Secrets visible in history - Rebuild without ENV for secrets, use runtime injection
  • Works locally but not in K8s - Check pod spec has env or envFrom configuration

What You Learned

  • ARG = build-time only (npm tokens, build flags)
  • ENV = runtime available (app config, non-sensitive settings)
  • Secrets must be injected at runtime, never baked into images
  • AI tools often default to ARG when they should use ENV

Pattern to remember:

ARG BUILD_DEPENDENCY_TOKEN  # Install-time credentials
ENV APP_SETTING=default     # Config with default
# (no secrets in Dockerfile)  # Runtime injection only

Limitations:

  • ENV variables are visible in docker inspect - not suitable for secrets
  • ARG values appear in build cache - avoid sensitive data
  • Windows containers use slightly different syntax for variable expansion

Quick Reference Card

ScenarioUseExample
Private npm/pip tokenARGARG NPM_TOKEN
RUN npm install
Default app configENVENV PORT=3000
Database URLRuntime -edocker run -e DATABASE_URL=...
API keys/passwordsRuntime secret--env-file or K8s secrets
Build variationARGENVARG ENV=prod
ENV APP_ENV=${ENV}

Common AI Tool Mistakes:

  1. ChatGPT/Claude often generate: ARG DATABASE_URL (wrong - app can't see it)
  2. GitHub Copilot suggests: ENV API_KEY=hardcoded (wrong - security risk)
  3. They should use: Runtime injection via -e or --env-file

Pro tip: When asking AI for Dockerfiles, specify: "Use ENV for runtime config, ARG only for build dependencies, and show how to inject secrets at runtime"


Tested on Docker Engine 25.0, Docker Compose v2.24, Node.js 22.x, Python 3.12, macOS & Ubuntu