The 'secret is not defined' error in Docker Compose occurs when a service references a secret that hasn't been declared in the top-level secrets section of your compose file. Fix it by defining the secret at the top level with either a file source or external reference.
The "secret is not defined" error indicates that Docker Compose found a reference to a secret in a service definition, but that secret hasn't been declared in the top-level `secrets:` section of your compose file. Docker Compose secrets work in two parts: 1. **Top-level declaration**: You must define all secrets at the root level of your compose file, specifying where the secret value comes from (a file, environment variable, or external source) 2. **Service-level reference**: Services then reference which secrets they need access to When you reference a secret in a service (like `secrets: [my_secret]`) without first declaring it at the top level, Docker Compose cannot resolve where that secret value should come from, resulting in this error. This error commonly occurs when: - Copying service definitions without their corresponding secret declarations - Migrating from Docker Swarm where secrets were created externally - Forgetting to add the top-level secrets block after adding secrets to a service - Typos in secret names between the top-level declaration and service reference
The most common fix is adding the missing top-level secrets: block. Docker Compose requires secrets to be defined at both the service level AND the top level:
services:
myapp:
image: myapp:latest
secrets:
- my_secret # Service-level: which secrets to use
secrets: # Top-level: where secrets come from
my_secret:
file: ./my_secret.txt # Path to file containing the secretThe secret file should contain the raw secret value (no quotes needed):
# Create the secret file
echo "my-super-secret-value" > ./my_secret.txt
# Verify Docker Compose config is valid
docker-compose configInside your container, the secret will be available at /run/secrets/my_secret.
Ensure the secret name matches exactly between the service reference and top-level declaration:
Incorrect (names don't match):
services:
web:
secrets:
- db_password # Using underscores
secrets:
db-password: # Using hyphens - MISMATCH!
file: ./db_password.txtCorrect:
services:
web:
secrets:
- db_password # Matches exactly
secrets:
db_password: # Same name
file: ./db_password.txtCommon naming issues:
- db_password vs db-password (underscore vs hyphen)
- mySecret vs mysecret (case sensitivity)
- db_pass vs db_password (abbreviation differences)
For local development with Docker Compose, file-based secrets are the simplest approach:
services:
db:
image: postgres:15
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
- db_user
app:
image: myapp
secrets:
- db_password
- api_key
depends_on:
- db
secrets:
db_password:
file: ./secrets/db_password.txt
db_user:
file: ./secrets/db_user.txt
api_key:
file: ./secrets/api_key.txtCreate the secrets directory and files:
mkdir -p ./secrets
echo "postgres-password-123" > ./secrets/db_password.txt
echo "postgres" > ./secrets/db_user.txt
echo "api-key-abc123" > ./secrets/api_key.txt
# Don't commit secrets to git
echo "secrets/" >> .gitignoreDocker Compose can also read secrets from environment variables (available in Compose V2):
services:
app:
image: myapp
secrets:
- db_password
secrets:
db_password:
environment: DB_PASSWORD # Read from host environment variableThen set the environment variable before running:
export DB_PASSWORD="my-secret-password"
docker-compose upOr in a .env file:
# .env
DB_PASSWORD=my-secret-passwordThis approach is useful for:
- CI/CD pipelines where secrets are injected as environment variables
- Avoiding secret files on disk
- Dynamic secret values
If you're using Docker Swarm, secrets can be created externally and referenced in your compose file:
# First, create the secret in Docker Swarm
docker swarm init # If not already in swarm mode
echo "my-secret-value" | docker secret create my_secret -Then reference it as external in your compose file:
services:
app:
image: myapp
secrets:
- my_secret
secrets:
my_secret:
external: true # Secret must already exist in SwarmImportant: External secrets ONLY work in Docker Swarm mode. If you see this error with external: true:
- Ensure you've initialized Swarm: docker swarm init
- Verify the secret exists: docker secret ls
- For local development, use file-based secrets instead
Build-time secrets (used during docker-compose build) require a different setup than runtime secrets:
For runtime secrets (used when container runs):
services:
app:
image: myapp
secrets:
- api_key
secrets:
api_key:
file: ./api_key.txtFor build-time secrets (used during image build):
services:
app:
build:
context: .
secrets:
- npm_token # Build-time secret
secrets:
npm_token:
file: ./npm_token.txtIn your Dockerfile, mount the build secret:
# syntax=docker/dockerfile:1
FROM node:18
# Mount and use the secret during build
RUN --mount=type=secret,id=npm_token \
NPM_TOKEN=$(cat /run/secrets/npm_token) npm install
COPY . .Build with BuildKit enabled:
DOCKER_BUILDKIT=1 docker-compose buildUse docker-compose config to validate your compose file and see the resolved configuration:
# Validate the compose file
docker-compose config
# If you see the error, the config is invalid
# ERROR: secret "mysecret" is not defined
# After fixing, you should see the full resolved config
docker-compose configThe output should show your secrets properly defined:
services:
app:
image: myapp
secrets:
- source: my_secret
target: /run/secrets/my_secret
secrets:
my_secret:
file: /full/path/to/my_secret.txtThis command helps catch configuration errors before attempting to start containers.
### Understanding Docker Secrets Architecture
Docker secrets are designed to securely pass sensitive data to containers. Here's how they work:
File-based secrets (Compose standalone)
- Secret content is stored in a file on your host
- Docker mounts the file into the container at /run/secrets/<secret_name>
- The mount is read-only with restrictive permissions
- Works without Docker Swarm
External secrets (Swarm mode)
- Secrets are stored encrypted in the Swarm raft log
- Distributed automatically to nodes that need them
- More secure for production multi-node deployments
- Requires docker swarm init
### Secret Syntax Variations
Docker Compose supports both short and long syntax for secrets:
Short syntax:
services:
app:
secrets:
- my_secretLong syntax (more control):
services:
app:
secrets:
- source: my_secret
target: /custom/path/secret # Custom path in container
uid: '103' # Owner UID
gid: '103' # Owner GID
mode: 0440 # File permissions### Reading Secrets in Applications
Applications need to read from /run/secrets/<name> instead of environment variables:
Node.js:
const fs = require('fs');
const dbPassword = fs.readFileSync('/run/secrets/db_password', 'utf8').trim();Python:
with open('/run/secrets/db_password') as f:
db_password = f.read().strip()Bash:
DB_PASSWORD=$(cat /run/secrets/db_password)### Many Images Support *_FILE Environment Variables
Official images like PostgreSQL, MySQL, and MariaDB support a *_FILE convention:
services:
db:
image: postgres:15
environment:
# Instead of POSTGRES_PASSWORD directly
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_passwordThis pattern is more secure than passing secrets as environment variables.
### Secrets vs Environment Variables
| Aspect | Secrets | Environment Variables |
|--------|---------|----------------------|
| Storage | File at /run/secrets | Process memory |
| Visibility | Not in docker inspect | Visible in docker inspect |
| Logging | Not logged | May appear in logs |
| Best for | Passwords, API keys | Non-sensitive config |
### Troubleshooting Common Issues
1. Secret file not found:
# Check file exists and path is correct
ls -la ./secrets/
# Use absolute path if relative doesn't work
secrets:
my_secret:
file: /home/user/project/secrets/my_secret.txt2. Secret empty in container:
# Check file isn't empty
cat ./secrets/my_secret.txt
# Ensure no trailing newlines if app is sensitive to them
echo -n "secret-value" > ./secrets/my_secret.txt3. Permission denied reading secret:
# Check container user can read /run/secrets
docker-compose exec app ls -la /run/secrets/
# Use long syntax to set permissions
secrets:
- source: my_secret
mode: 0444 # World-readable### Security Best Practices
1. Never commit secrets to git - Add to .gitignore
2. Use .env.example - Document required secrets without values
3. Rotate secrets regularly - Recreate secret files periodically
4. Limit access - Only mount secrets to services that need them
5. Use external secrets in production - Swarm secrets or HashiCorp Vault
Error response from daemon: client version X.XX is too new. Maximum supported API version is X.XX
How to fix 'client version is too new' in Docker
dial tcp: i/o timeout
How to fix 'dial tcp: i/o timeout' in Docker
image 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