The Error
You run docker compose up and get hit with a wall of warnings:
WARN[0000] The "DB_PASSWORD" variable is not set. Defaulting to a blank string.
WARN[0000] The "DB_HOST" variable is not set. Defaulting to a blank string.
WARN[0000] The "REDIS_URL" variable is not set. Defaulting to a blank string.
Your .env file exists. You can see it. You've checked the variable names three times. But Docker Compose acts like it doesn't exist. Meanwhile your database container starts with a blank password and promptly fails โ at 2 AM, in production, naturally.
Root Causes
Docker Compose has strict rules about where and how it reads .env files. Break any one of them and it silently skips your file entirely:
- Wrong directory: The
.envfile must be in the same directory where you rundocker compose, not wheredocker-compose.ymllives (if those differ). - Wrong filename: The file must be named exactly
.envโ not.env.local,.env.production, orenv(without the dot). - Syntax errors: Quoted values, extra spaces around
=, or Windows-style line endings (\r\n) cause variables to parse incorrectly or get skipped altogether. - Mixing up
env_fileand variable substitution:env_fileinjects variables into the container. The.envfile provides values for variable substitution insidecompose.ymlitself. These are completely different mechanisms. - File permissions: On Linux, if the
.envfile isn't readable by the current user, Docker Compose ignores it without a word.
Fix 1: Verify File Location and Name
Start here. Confirm you're in the right directory:
# Check where you are
pwd
# List hidden files in the current directory
ls -la | grep .env
# Should show something like:
# -rw-r--r-- 1 user user 245 May 14 02:13 .env
If the .env file is in a subdirectory or named differently, either move it or point Docker Compose at it directly with --env-file:
docker compose --env-file ./config/.env.production up
Fix 2: Check the .env File Syntax
Syntax is picky. Open the file and look for these specific traps:
# Bad โ spaces around = sign
DB_PASSWORD = mysecretpassword
# Bad โ quoted values (Docker Compose does NOT strip quotes)
DB_PASSWORD="mysecretpassword"
# Bad โ inline comments after value
DB_PASSWORD=mysecretpassword # production password
# Good
DB_PASSWORD=mysecretpassword
That second example trips people up constantly. Docker Compose passes quoted values literally โ quote characters and all. So DB_PASSWORD="mysecretpassword" becomes the string "mysecretpassword" with actual quote marks in it. Your database auth will fail in a confusing way.
Windows line endings are another quiet killer, especially if the file was created on Windows or synced through Git without a proper .gitattributes:
# Check for carriage returns
cat -A .env | head -5
# Bad: DB_PASSWORD=secret^M$
# Good: DB_PASSWORD=secret$
# Fix Windows line endings
sed -i 's/\r//' .env
Fix 3: Understand env_file vs .env Substitution
Two mechanisms share similar names. They do different things. Here's the split:
Mechanism 1 โ Variable substitution in compose.yml (uses .env in the project root):
# docker-compose.yml
services:
db:
image: postgres
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD} # โ substituted from .env at parse time
Mechanism 2 โ env_file directive (injects variables directly into the container):
# docker-compose.yml
services:
db:
image: postgres
env_file:
- .env # โ this file's variables go INTO the container environment
Using ${DB_PASSWORD} syntax in your compose file? Docker Compose needs .env in the project root to substitute those values before containers start. Using env_file instead? The file just needs to be readable at runtime.
Fix 4: Debug What Docker Compose Actually Sees
Skip the guessing. Ask Docker Compose directly:
# Print the fully-resolved compose config (all variables substituted)
docker compose config
# If DB_PASSWORD still shows as blank or empty, it wasn't loaded
You can also find which .env file Docker Compose is actually picking up:
# Docker Compose v2 shows this in verbose output
docker compose --verbose config 2>&1 | grep -i env
One more useful check โ print every variable the container will see:
docker compose run --rm your_service env | sort
Fix 5: Pass Variables Explicitly as a Fallback
Need things running right now while you sort out the root cause? Bypass the file entirely:
# Pass inline (not ideal for secrets)
DB_PASSWORD=temporaryfix docker compose up
# Or export first
export DB_PASSWORD=temporaryfix
docker compose up
Shell environment variables take precedence over .env values anyway, so this always works regardless of file issues.
Verification
After applying a fix, run these three checks before declaring victory:
# 1. Run config and grep for your variable
docker compose config | grep DB_PASSWORD
# Should show the actual value, not blank
# 2. No more WARN lines on startup
docker compose up 2>&1 | grep WARN
# Should be empty (or at least no variable warnings)
# 3. Confirm the container got the right value
docker compose run --rm db env | grep DB_PASSWORD
Prevention
Build these habits now and you won't debug this at 2 AM again:
- Run
docker compose configbefore every deploy. It catches substitution errors before any container starts. Takes five seconds. - Add
.envto.gitignore, but commit a.env.examplewith all required keys and empty values. New team members immediately know what variables are needed without reading the docs. - Be explicit about env files in CI/CD. Use
--env-fileand name the file. Don't rely on implicit.envdetection in pipelines โ what works locally often doesn't in CI. - Generate strong random values for secrets like DB_PASSWORD rather than typing something manually. ToolCraft's Password Generator runs entirely in the browser โ nothing gets sent to a server, which matters when you're generating database credentials.
- Validate your compose file YAML if you're seeing unexpected parse behavior. The YAML โ JSON Converter on ToolCraft spots malformed YAML fast.
Quick Reference: .env File Rules
# Location: same directory as where you run 'docker compose'
# Name: exactly '.env' (or specify with --env-file)
# Syntax:
# KEY=value โ correct
# KEY = value โ spaces not allowed around =
# KEY="value" โ quotes are literal
# KEY=value # note โ inline comments not supported
# export KEY=value โ 'export' keyword not supported
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DB_PASSWORD=use-a-real-random-password-here

