DEV Community

Sohana Akbar
Sohana Akbar

Posted on

The .env Handshake: Sharing Secrets Safely Between Host and Container

You just built a cool Node.js app that uses an API key. You put it in .env on your laptop. Works perfectly.

Then you Dockerize it. You write the Dockerfile, build the image, run the container… and crash. Error: Missing API_KEY.

So you Google around and find a "quick fix": you add COPY .env ./.env to your Dockerfile. 😱

Congratulations. You just put your production keys inside a Docker image.

Now anyone who pulls that image (or docker history on it) can see your secrets. Let's fix that forever.

The Golden Rule of .env & Docker
Never bake secrets into an image. Always inject them at runtime.

Think of a Docker image like a compiled binary. You can share it, scan it, or push it to Docker Hub. If a secret is inside, it's there forever.

The container running from that image? That's where secrets belong.

The 3 Safe Ways to Share .env (Host β†’ Container)
Let's assume you have a .env file on your host (never committed to Git, thanks to .gitignore). Here's how to get it into your container.

Method 1: The --env-file Flag (Easiest & Best for Dev)
This is your daily driver for local development. It reads your host's .env and injects each variable into the container.

bash
docker run --env-file .env my-app-image
Inside your container, your code can just do process.env.API_KEY (Node), os.getenv("API_KEY") (Python), or getenv("API_KEY") (Go). It just works.

βœ… Pros: Simple, no changes to Dockerfile or code.
❌ Cons: The file path is relative to where you run docker run. Great for dev, less ideal for production orchestration.

Method 2: Docker Compose env_file (Best for Local Services)
If you use docker-compose.yml (and you should for multi-container apps), this is clean:

docker-compose.yml

yaml
services:
api:
build: .
env_file:
- .env
# ... ports, volumes, etc.
Then run docker compose up. Compose reads .env from the same directory (by default) and gives it to the container.

βœ… Pros: Declarative, works great with docker compose up.
⚠️ Warning: Docker Compose also has an automatic .env file for Compose itself (variable substitution). Don't confuse the two.

Method 3: Host Volume Mount (Not for Secrets, but for Live Reload)
Sometimes you want to share .env as a file inside the container (e.g., for tools that read .env directly, not env vars).

yaml
services:
app:
volumes:
- ./.env:/.env
But be careful: changes to .env on your host instantly change inside the container. That's great for development config reload, but do not do this in production (unless you want a container that self-destructs on config change).

The Dangerous Way (What to NEVER do)
dockerfile

πŸ”΄ NEVER DO THIS πŸ”΄

COPY .env ./.env
Why it's evil:

The .env file is now part of every layer of your image.

Anyone with docker history my-image can see its contents (base64 encoded but trivially decoded).

If you push to a public registry, your keys are public forever.

Pro Tips for Students & Junior Devs

  1. Use .dockerignore (Your Safety Net) Create a .dockerignore file next to your Dockerfile:

text
.env
.env.*
*.pem
*.key
This ensures that even if you accidentally try to COPY . ., the secrets won't be included.

  1. Use Different Files for Different Environments text .env.development # (committed, defaults) .env.staging # (never committed, real staging keys) .env.production # (never committed, real prod keys) Then run:

bash
docker run --env-file .env.staging my-app

  1. For Production: Use a Secrets Manager On your laptop (host) for dev, .env files are fine. But for real production:

Docker Swarm secrets

Kubernetes Secrets (base64 encoded, but better with external KMS)

HashiCorp Vault, AWS Secrets Manager, or Doppler

The pattern is the same: inject at runtime, never bake.

Real-World Example: Node.js App
Your local .env (never committed):

text
PORT=3000
API_KEY=sk_live_abc123xyz
DB_PASSWORD=hunter2
Dockerfile (clean, no secrets):

dockerfile
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["node", "index.js"]
Run it safely:

bash
docker build -t my-app .
docker run --env-file .env -p 3000:3000 my-app
Inside index.js:

javascript
const apiKey = process.env.API_KEY; // βœ… works perfectly
Wrapping Up
Method Safe? Best for...
COPY .env inside image ❌ NEVER Nothing. Seriously.
--env-file flag βœ… Yes Dev, quick testing
Compose env_file βœ… Yes Local dev with multiple services
Volume mount .env ⚠️ Careful Config hot-reload (dev only)
Your checklist before you sleep tonight:

.env is in .gitignore

.env is in .dockerignore

You never COPY .env in a Dockerfile

You use --env-file or docker compose locally

Now go build something awesome without leaking your keys. πŸ³πŸ”

Found this helpful? Follow me for more "dangerous Docker mistakes and how to fix them" articles. Questions? Drop them in the comments – but don't post your actual API key! πŸ˜…

Top comments (0)