The 'Missing required environment variable' error in Docker Compose occurs when using the `${VAR?error}` or `${VAR:?error}` syntax to require a variable that isn't set in your environment. This is resolved by setting the required variable, providing a default value, or using an .env file.
The "Missing required environment variable" error indicates that Docker Compose encountered variable interpolation syntax that explicitly requires a variable to be set, but that variable was not found in the environment. Docker Compose supports special syntax for requiring environment variables: - `${VARIABLE?error}` - Fails if VARIABLE is unset (but allows empty string) - `${VARIABLE:?error}` - Fails if VARIABLE is unset OR empty When Docker Compose parses your `docker-compose.yml` file and finds this syntax with an undefined variable, it immediately stops and displays this error. This is actually a safety feature that prevents your services from starting with missing configuration. This error commonly appears when: 1. You've cloned a project that expects certain environment variables to be set 2. The `.env` file is missing or incomplete 3. You're running Docker Compose in a CI/CD environment without proper variable configuration 4. The Compose file was written to require specific variables for security or configuration purposes Unlike variables without the `?` modifier (which silently substitute an empty string), required variables provide explicit validation that critical configuration is present before containers start.
First, check the error message to find which variable is required:
# Run docker-compose to see the error
docker-compose config
# Example output:
# ERROR: Missing required environment variable "DATABASE_URL"
# Or: required variable DATABASE_URL is missing a value: Database connection requiredThe error message tells you exactly which variable needs to be set. Note the variable name for the next steps.
You can also search your compose file for required variable syntax:
# Find required variables in your compose file
grep -E '\$\{[^}]+\?' docker-compose.ymlThe most direct fix is to set the required variable. You have several options:
Option 1: Set in shell before running docker-compose
# Export the variable
export DATABASE_URL="postgres://user:pass@localhost:5432/mydb"
# Then run docker-compose
docker-compose upOption 2: Set inline with the command
DATABASE_URL="postgres://user:pass@localhost:5432/mydb" docker-compose upOption 3: Add to your shell profile (~/.bashrc, ~/.zshrc)
echo 'export DATABASE_URL="postgres://user:pass@localhost:5432/mydb"' >> ~/.bashrc
source ~/.bashrcVerify the variable is set:
echo $DATABASE_URLDocker Compose automatically reads a .env file from the project directory. Create or update it:
# Create .env file in your project directory
cat > .env << 'EOF'
# Required environment variables
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
API_KEY=your-api-key-here
REDIS_URL=redis://localhost:6379
EOFIf a .env.example file exists, use it as a template:
cp .env.example .env
# Then edit .env to fill in actual valuesVerify the .env file is being read:
# This command shows the resolved configuration
docker-compose configImportant: The .env file must be in the same directory as your docker-compose.yml file, or specify it with --env-file:
docker-compose --env-file ./config/.env upIf the variable is optional in some environments, modify the compose file to provide a default:
Before (requires variable):
services:
app:
environment:
- DATABASE_URL=${DATABASE_URL?Database URL is required}After (with default value):
services:
app:
environment:
# Use :- for default when unset or empty
- DATABASE_URL=${DATABASE_URL:-postgres://localhost:5432/default}
# Use - for default only when unset
- LOG_LEVEL=${LOG_LEVEL-info}Variable substitution syntax:
| Syntax | Behavior |
|--------|----------|
| ${VAR} | Substitutes value or empty string |
| ${VAR:-default} | Uses default if unset or empty |
| ${VAR-default} | Uses default if unset |
| ${VAR:?error} | Fails if unset or empty |
| ${VAR?error} | Fails if unset |
For CI/CD pipelines, ensure required variables are available:
GitHub Actions:
# .github/workflows/deploy.yml
jobs:
deploy:
runs-on: ubuntu-latest
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.API_KEY }}
steps:
- uses: actions/checkout@v4
- name: Deploy with Docker Compose
run: docker-compose up -dGitLab CI:
# .gitlab-ci.yml
deploy:
variables:
DATABASE_URL: $DATABASE_URL # From CI/CD settings
script:
- docker-compose up -dCreate .env from secrets in CI:
# Create .env file from environment
cat > .env << EOF
DATABASE_URL=${DATABASE_URL}
API_KEY=${API_KEY}
EOF
docker-compose up -dThere's a subtle difference between ${VAR?error} and ${VAR:?error}:
- `?` - Only fails if variable is completely unset
- `:?` - Fails if variable is unset OR empty
If you're getting errors with a variable you've set, check if it's empty:
# Check if variable is set and show its value
echo "DATABASE_URL='$DATABASE_URL'"
# In .env file, ensure no spaces around =
# Correct:
DATABASE_URL=postgres://localhost/db
# Wrong (empty value):
DATABASE_URL=
DATABASE_URL=
DATABASE_URL=""If the compose file uses :? and you need to allow empty values, change to ?:
# Fails if empty or unset
- DEBUG_MODE=${DEBUG_MODE:?Debug mode required}
# Only fails if unset (allows empty)
- DEBUG_MODE=${DEBUG_MODE?Debug mode required}When using multiple compose files, each may have different required variables:
# Check which variables are needed in each file
grep -E '\$\{[^}]+\?' docker-compose.yml docker-compose.override.yml docker-compose.prod.yml
# Ensure all required variables are set for the files you're using
docker-compose -f docker-compose.yml -f docker-compose.prod.yml configCreate environment-specific .env files:
# Development
docker-compose --env-file .env.dev up
# Production
docker-compose --env-file .env.prod -f docker-compose.yml -f docker-compose.prod.yml upOr use profiles to conditionally include services:
services:
app:
# Always runs
environment:
- DATABASE_URL=${DATABASE_URL:?Required}
debug-tools:
profiles: ["debug"]
# Only validates when profile is active
environment:
- DEBUG_TOKEN=${DEBUG_TOKEN:?Required for debug}### Understanding Variable Interpolation Syntax
Docker Compose uses shell-style variable interpolation with several modifiers:
| Syntax | Behavior |
|--------|----------|
| ${VAR} | Substitute value, empty string if unset |
| ${VAR:-default} | Use default if VAR is unset or empty |
| ${VAR-default} | Use default if VAR is unset |
| ${VAR:+replacement} | Use replacement if VAR is set and non-empty |
| ${VAR+replacement} | Use replacement if VAR is set |
| ${VAR:?error} | Fail with error if VAR is unset or empty |
| ${VAR?error} | Fail with error if VAR is unset |
### Known Bugs and Quirks
Bug: Required variables not throwing errors when not last (Issue #10011)
There's a known bug where required variables only throw errors when they're the last part of an interpolation:
# This throws an error if MY_VAR is unset
path: ${MY_VAR?Required}
# This may silently continue with empty string!
path: ${MY_VAR?Required}/subdirWorkaround: Put required variables last or test separately.
Required variables evaluated on unrelated services (Issue #6978)
Docker Compose validates ALL variable references when parsing, even for services you're not running:
# This fails even if you're only starting 'web'
docker-compose up web
# If 'worker' service has ${WORKER_SECRET:?Required}
# and WORKER_SECRET is not setWorkaround: Use profiles to isolate services with different requirements.
### .env File vs --env-file Flag vs env_file Directive
These three mechanisms serve different purposes:
1. .env file (automatic loading)
- Auto-loaded from project directory
- Used for Compose file variable interpolation
- Example: image: ${REGISTRY}/app:${TAG}
2. --env-file flag
- Overrides the .env file location
- Still used for Compose file interpolation
- docker-compose --env-file ./config/.env up
3. env_file directive (in services)
- Passes variables TO containers at runtime
- Not used during Compose file parsing
- Different from the .env file!
services:
app:
env_file:
- ./runtime.env # These go INTO the container
environment:
- FROM_COMPOSE=${HOST_VAR} # This is interpolated from .env### Docker Compose with Bake (BuildKit)
As of Docker 28.3.0+, the BAKE feature may cause different behavior with environment variables during builds:
# If seeing issues with BAKE enabled
DOCKER_BUILDKIT=0 docker-compose build
# Or set in Docker daemon config
# ~/.docker/daemon.json
{
"features": {
"buildkit": false
}
}### Security Best Practices
1. Never commit .env files with secrets
# .gitignore
.env
.env.local
.env.*.local2. Provide .env.example as a template
# .env.example (committed to repo)
DATABASE_URL=postgres://user:pass@host:5432/db
API_KEY=your-api-key-here3. Use required syntax for critical variables
environment:
- DATABASE_URL=${DATABASE_URL:?Database URL must be set}
- JWT_SECRET=${JWT_SECRET:?JWT secret is required}4. Use Docker secrets for production
services:
app:
secrets:
- db_password
secrets:
db_password:
external: trueimage operating system "linux" cannot be used on this platform
How to fix 'image operating system linux cannot be used on this platform' in Docker
manifest unknown: manifest unknown
How to fix 'manifest unknown' in Docker
cannot open '/etc/passwd': Permission denied
How to fix 'cannot open: Permission denied' in Docker
Error response from daemon: failed to create the ipvlan port
How to fix 'failed to create the ipvlan port' in Docker
toomanyrequests: Rate exceeded for anonymous users
How to fix 'Rate exceeded for anonymous users' in Docker Hub