Docker Compose Services Won't Start? Here's Your Debugging Playbook
You've just added a new service to your docker-compose.yml, run docker compose up -d, and... nothing. The container exits immediately, restarts in an infinite loop, or just sits there refusing to work. No helpful error message. Just frustration.
Sound familiar? Every developer who works with Docker Compose has been there. The good news is that container failures follow predictable patterns. Once you know where to look, you can diagnose most issues in under five minutes.
This guide will walk you through a systematic debugging approach, from the most common causes to the edge cases that make you question your life choices.
The First Thing You Should Do
Before diving into logs and configuration files, run this single command:
docker compose ps -a
The -a flag is crucial—it shows all containers, including the ones that exited. Without it, you'd only see running containers, which isn't helpful when debugging failures.
The output tells you the current state of each service. But the real gold is in the exit codes:
| Exit Code | What It Means |
|---|---|
| 0 | Container finished successfully (normal for one-shot tasks) |
| 1 | Application error—your code crashed |
| 137 | Container was killed (OOM or docker stop) |
| 139 | Segmentation fault—usually a bug in the application or library |
| 143 | Container received SIGTERM—clean shutdown |
If you see exit code 137, your container ran out of memory. Exit code 1? Your application threw an unhandled error. This single command often points you directly to the problem.
Step 2: Actually Read the Logs
Logs are your primary debugging tool, but most developers use them incorrectly. Here's the right way:
# View logs for a specific service
docker compose logs api
# Follow logs in real time (most useful)
docker compose logs -f api
# Show only the last 100 lines (when logs are noisy)
docker compose logs --tail=100 api
# Include timestamps (for timing issues)
docker compose logs -t api
Sometimes a container exits so fast it produces no logs. In that case, run the service in the foreground:
docker compose up api
This runs the service attached to your terminal, so you'll see every output line before it exits.
The 10 Most Common Causes (And How to Fix Them)
1. Image Pull Failures
Your service can't start because Docker can't find or pull the image.
Symptoms: Container stays in "creating" state or exits immediately with image-related error.
# Check if the image exists locally
docker compose images
# Force pull images
docker compose pull
Fix: Verify the image name and tag. If using a private registry, make sure you're logged in:
docker login registry.example.com
2. Port Conflicts
Another process is already using the port you're trying to bind.
Symptoms: Error message like Bind for 0.0.0.0:3000 failed: port is already allocated
# Find what's using the port
lsof -i :3000
# Or use ss
ss -tlnp | grep 3000
Fix: Either stop the conflicting process or change the host port in your compose file:
services:
api:
ports:
- "3001:3000" # Changed host port to 3001
3. Volume Mount Errors
A bind mount references a directory that doesn't exist on the host.
Symptoms: Error response from daemon: invalid mount config for type "bind": bind source path does not exist
Fix: Docker won't create bind mount directories for you. Create them before starting:
mkdir -p ./data ./config ./logs
docker compose up -d
4. Dependency Failures
A service depends on another service that isn't healthy yet.
Symptoms: Service exits immediately after startup, even though dependencies are running.
services:
app:
depends_on:
db:
condition: service_healthy
Debug: Check the health status:
# Check health check logs
docker inspect $(docker compose ps -q db) --format '{{json .State.Health}}' | python3 -m json.tool
Fix: Increase health check retries or start_period:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 10s
retries: 10
start_period: 30s # Give the service time to initialize
5. OOM Killed (Out of Memory)
The container ran out of memory and was killed by the kernel.
Symptoms: Exit code 137, or container restarts repeatedly with no logs.
# Check if a container was OOM killed
docker inspect $(docker compose ps -q api) --format '{{.State.OOMKilled}}'
You can also check kernel logs:
dmesg | grep -i "killed process"
Fix: Increase the memory limit:
services:
api:
deploy:
resources:
limits:
memory: 1G # Increase from default
6. Entrypoint or Command Errors
The container's entrypoint or command is wrong, or a shell script has issues.
Symptoms: Container exits with code 127 (command not found) or immediately after startup.
# Override the command to get a shell
docker compose run --rm --entrypoint sh api
Common issues:
- The binary doesn't exist in the image
- Shell script is missing executable permissions
- Windows-style line endings (CRLF) in shell scripts
The CRLF issue is sneaky—it works on Windows but fails on Linux:
# Convert CRLF to LF in entrypoint scripts
sed -i 's/\r$//' entrypoint.sh
Or prevent it by configuring Git:
git config --global core.autocrlf false
7. Environment Variable Issues
Missing or incorrect environment variables cause crashes at startup.
Symptoms: Application logs show "environment variable not set" or similar errors.
# Check what environment variables are set
docker compose config | grep -A 50 "api:"
# Or inside the container
docker compose run --rm api env | sort
Fix: Make required variables explicit:
services:
api:
environment:
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL must be set}
SECRET_KEY: ${SECRET_KEY:?SECRET_KEY must be set}
The :? syntax causes Docker Compose to fail with a clear error message if the variable isn't set.
8. Network Issues
The container can't reach a service it needs.
Symptoms: Connection timeouts, DNS resolution failures, "connection refused" errors.
# Check network connectivity
docker compose exec api ping db
# Check DNS resolution
docker compose exec api nslookup db
# Check if the target port is open
docker compose exec api nc -zv db 5432
Fix: Ensure all services that need to communicate are on the same network:
services:
api:
networks:
- backend
db:
networks:
- backend
networks:
backend:
Important: Never use localhost for inter-container communication. Use the service name instead. This is the #1 networking mistake.
9. File Permission Issues
The container process can't read or write to mounted volumes.
Symptoms: Permission denied errors in logs, or application can't write data.
# Check what user the container runs as
docker compose exec api id
# Check permissions on the mounted directory
docker compose exec api ls -la /app/data
Fix: Set correct ownership or run the container as the right user:
services:
api:
user: "1000:1000"
volumes:
- ./data:/app/data
Or fix permissions on the host:
sudo chown -R 1000:1000 ./data
10. Compose File Syntax Errors
A typo or indentation error in the compose file.
Symptoms: Docker Compose fails to parse the file, or services don't start.
# Validate the compose file syntax
docker compose config -q
If there are errors, show the full output:
docker compose config
Common syntax problems:
- Incorrect indentation (YAML requires spaces, not tabs)
- Using tabs instead of spaces
- Missing quotes around values with special characters
- Incorrect variable interpolation syntax
The Nuclear Option: Start Fresh
When nothing else works, sometimes the cleanest approach is to start from scratch:
# Stop everything and remove all resources
docker compose down -v --remove-orphans
# Remove all cached images for this project
docker compose down --rmi all
# Clean up Docker system (optional)
docker system prune -f
# Start fresh
docker compose up --build
This is especially useful after updating Docker, after changing base images, or when you've tried everything else and just want a clean slate.
Your Debug Checklist
When a service won't start, go through this checklist in order:
# 1. Validate compose file syntax
docker compose config -q
# 2. Check service status and exit codes
docker compose ps -a
# 3. Read service logs
docker compose logs --tail=50 <service>
# 4. Check container state details
docker inspect $(docker compose ps -q <service>) --format '{{json .State}}'
# 5. Check if image exists
docker compose images
# 6. Check port conflicts
docker compose ps --format "table {{.Name}}\t{{.Ports}}"
# 7. Check volume mounts exist
docker compose config --volumes
# 8. Check environment variables
docker compose run --rm <service> env
# 9. Get a shell in the container
docker compose run --rm --entrypoint sh <service>
# 10. Check Docker daemon logs (last resort)
sudo journalctl -u docker --since "10 minutes ago"
FAQ
Q: My container keeps restarting. How do I debug it?
Use docker compose logs -f <service> to follow logs in real-time. If it restarts too quickly, run it in the foreground with docker compose up <service> to catch the initial output.
Q: Why does my container work locally but fail in CI?
Check for differences in environment variables, volume paths, and available resources (memory/CPU). CI environments often have resource limits that your local machine doesn't.
Q: How do I debug a container that exits immediately with no logs?
Run it interactively:
docker compose run --rm --entrypoint sh <service>
Then try running your command manually to see what happens.
Q: What's the difference between docker compose up and docker compose run?
up creates and starts all services defined in the compose file. run creates a one-off container for a specific service, which is useful for debugging or running one-time commands.
Conclusion
Docker Compose startup failures are frustrating, but they're not mysterious. Most issues fall into a handful of categories: port conflicts, missing volumes, OOM kills, network issues, or configuration errors.
The key is to be systematic. Start with docker compose ps -a to see exit codes, then docker compose logs to see what happened. If those don't reveal the problem, inspect the container state and work through the common causes.
And when all else fails—the nuclear option exists for a reason. Sometimes a clean slate is the fastest path to a working system.
Found this helpful? Bookmark the debug checklist above for quick reference.
Top comments (0)