The 'environment variable has empty value' warning in Docker or Docker Compose appears when a referenced environment variable is not set or defaults to a blank string. This is typically resolved by setting the variable in your shell, using a .env file, or providing a default value in your configuration.
The "environment variable has empty value" warning (or the related "variable is not set. Defaulting to a blank string" warning) indicates that Docker Compose is trying to substitute an environment variable in your docker-compose.yml file, but that variable either doesn't exist in your shell environment or has no value assigned. When you use `${VARIABLE_NAME}` syntax in a docker-compose.yml file, Docker Compose looks for that variable in: 1. Your current shell environment 2. The `.env` file in your project root 3. Values passed via `--env-file` flag If the variable isn't found in any of these locations, Docker Compose substitutes an empty string and shows this warning. While your containers may still start, the empty value could cause runtime issues depending on how the variable is used. This is different from the `env_file:` directive, which passes variables INTO containers. The warning occurs during Compose file parsing when Docker is trying to build the configuration before any containers start. Common scenarios where this occurs: - Using `${DATABASE_URL}` in docker-compose.yml without setting it - Cloning a project that expects certain environment variables to be set - CI/CD pipelines where secrets weren't properly injected - Typos in variable names (case-sensitive on Linux)
First, identify exactly which variable is causing the warning. The warning message tells you the variable name:
# The warning looks like this:
# WARN[0000] The "DATABASE_URL" variable is not set. Defaulting to a blank string.
# Check if the variable exists in your environment
echo $DATABASE_URL
# Or check all environment variables
env | grep DATABASE_URL
# View what Docker Compose sees
docker-compose configIf the echo command produces no output or the grep returns nothing, the variable is not set in your current shell.
Export the variable in your current shell session:
# Set and export the variable
export DATABASE_URL="postgres://user:pass@localhost:5432/mydb"
# Verify it's set
echo $DATABASE_URL
# Now run docker-compose
docker-compose upTo make this permanent, add the export to your shell profile:
# For bash (add to ~/.bashrc or ~/.bash_profile)
echo 'export DATABASE_URL="postgres://user:pass@localhost:5432/mydb"' >> ~/.bashrc
source ~/.bashrc
# For zsh (add to ~/.zshrc)
echo 'export DATABASE_URL="postgres://user:pass@localhost:5432/mydb"' >> ~/.zshrc
source ~/.zshrcNote: Never put actual secrets in shell profile files. Use a secrets manager for production.
Docker Compose automatically reads a .env file in the same directory as your docker-compose.yml:
# Create .env file in your project root
cat > .env << 'EOF'
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
REDIS_URL=redis://localhost:6379
API_KEY=your-api-key-here
NODE_ENV=development
EOFImportant rules for .env files:
- No spaces around the = sign
- No quotes needed (unless value contains spaces)
- Comments start with #
- File must be named exactly .env (not env or .env.local)
Verify Docker Compose can read it:
# Show the resolved configuration
docker-compose config
# Or specify a different env file
docker-compose --env-file ./config/.env upProvide fallback values directly in your docker-compose.yml to prevent warnings:
services:
app:
image: myapp
environment:
# Use :- for default when unset or empty
- DATABASE_URL=${DATABASE_URL:-postgres://localhost:5432/dev}
# Use - for default only when unset (allows empty values)
- OPTIONAL_VAR=${OPTIONAL_VAR-default_value}
# Require the variable (fail if not set)
- REQUIRED_VAR=${REQUIRED_VAR:?Error: REQUIRED_VAR must be set}Default value syntax:
- ${VAR:-default} - Use default if VAR is unset OR empty
- ${VAR-default} - Use default only if VAR is unset (empty is allowed)
- ${VAR:?error} - Fail with error message if VAR is unset or empty
- ${VAR?error} - Fail with error message only if VAR is unset
If your variable value contains special characters like $, they may be interpreted as variable references:
# Problem: $ in password gets interpreted
PASSWORD="p@ss$word123" # $word gets expanded to empty stringFix in .env file - use single quotes:
# In .env file (values aren't typically quoted, but this varies)
PASSWORD=p@ss$word123
# Or if your version supports it:
PASSWORD='p@ss$word123'Fix in docker-compose.yml - escape the dollar sign:
services:
app:
environment:
# Use $$ to escape a literal $ sign
- PASSWORD=p@ss$$word123Fix in shell - use single quotes:
export PASSWORD='p@ss$word123'When running docker-compose with sudo, environment variables from your user session are not passed through:
# This loses your environment variables
sudo docker-compose up # Variables not available!
# Option 1: Use -E to preserve environment
sudo -E docker-compose up
# Option 2: Pass variables explicitly
sudo DATABASE_URL="$DATABASE_URL" docker-compose up
# Option 3: Add yourself to docker group (recommended)
sudo usermod -aG docker $USER
# Log out and back in, then:
docker-compose up # No sudo needed!The best solution is to add your user to the docker group so you don't need sudo at all.
Use docker-compose config to see exactly how your variables are being resolved:
# Show the fully resolved configuration
docker-compose config
# Check which variables are resolved
docker-compose config | grep -A5 environment
# Verbose output for debugging (Compose V2)
docker compose config --environmentIf you see empty strings where you expect values, double-check:
1. Variable spelling (case-sensitive)
2. .env file location (must be in project root)
3. Shell environment (env | grep VAR_NAME)
4. File permissions on .env (ls -la .env)
### Understanding Docker Compose Variable Substitution
Docker Compose has two completely separate mechanisms for environment variables:
1. Compose File Substitution (causes this warning)
- Syntax: ${VARIABLE} in docker-compose.yml
- Resolved BEFORE containers start
- Sources: shell environment, .env file, --env-file flag
- Used for: image names, port mappings, volume paths, etc.
2. Container Environment (does NOT cause this warning)
- Configured via environment: or env_file: directives
- Passed INTO containers at runtime
- Not available during Compose file parsing
Example showing both:
services:
db:
# Substitution: TAG comes from shell/.env
image: postgres:${POSTGRES_TAG:-15}
# Container environment: these go inside the container
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD:-secret}
# env_file: loads vars into container, NOT for substitution
env_file:
- ./db.env### The Difference Between .env and env_file
This is a common source of confusion:
| Feature | .env file | env_file: directive |
|---------|-------------|----------------------|
| Purpose | Compose file substitution | Container environment |
| Location | Project root (next to docker-compose.yml) | Anywhere (path specified) |
| When parsed | Before containers start | When container starts |
| Can use in docker-compose.yml | Yes (${VAR} syntax) | No |
| Can use in container | Only if passed via environment: | Yes |
### CI/CD Pipeline Considerations
In CI/CD environments, you typically need to:
# GitHub Actions example
jobs:
deploy:
steps:
- name: Set up environment
run: |
echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" >> .env
echo "API_KEY=${{ secrets.API_KEY }}" >> .env
- name: Deploy
run: docker-compose up -dOr export variables directly:
steps:
- name: Deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.API_KEY }}
run: docker-compose up -d### Silencing Warnings for Optional Variables
If a variable is truly optional and you don't want warnings:
services:
app:
environment:
# Provide empty default to silence warning
- OPTIONAL_FEATURE=${OPTIONAL_FEATURE:-}
# Or use a meaningful default
- LOG_LEVEL=${LOG_LEVEL:-info}### Variable Interpolation in Different Contexts
Be aware that variable expansion behavior differs:
# Shell (double quotes expand variables)
echo "Value: $HOME" # Expands $HOME
echo 'Value: $HOME' # Literal $HOME
# .env file (usually no quotes, no expansion)
HOME_DIR=/home/user # Literal value
# docker-compose.yml (uses Compose substitution)
image: myapp:${TAG} # Uses Compose's substitution
command: echo $$HOME # $$ escapes to literal $ for container### Debugging Tips
1. Check what Compose sees:
docker compose config --environment2. Verify .env is being read:
docker compose --verbose up 2>&1 | head -203. Check shell vs Compose:
# What's in your shell?
echo $MY_VAR
# What does Compose resolve?
docker compose config | grep MY_VARdockerfile parse error line 5: unknown instruction: RRUN
How to fix 'unknown instruction' Dockerfile parse error in Docker
Error response from daemon: manifest for nginx:nonexistent not found: manifest unknown: manifest unknown
How to fix 'manifest for image:tag not found' in Docker
Error response from daemon: invalid reference format: repository name must be lowercase
How to fix 'repository name must be lowercase' in Docker
Error response from daemon: No such image
How to fix 'No such image' in Docker
Error response from daemon: Container is not running
How to fix 'Container is not running' when using docker exec