Know the difference between exec and run! Each has its place in your debugging toolkit.
The key difference
exec: Runs commands in an existing containerrun: Creates a new container
# Exec: enters running container
docker compose exec web bash
# Run: starts new container
docker compose run web bash
When to use exec
Use exec for debugging running services:
# Debug a running web server
docker compose exec web bash
# Check logs inside container
docker compose exec web tail -f /var/log/app.log
# Run database queries
docker compose exec db psql -U postgres
# Check process list
docker compose exec web ps aux
# Test connectivity from inside
docker compose exec web curl http://api:3000/health
Important: Container must be running!
# This fails if web is stopped
docker compose exec web bash
# Error: No container found for web_1
When to use run
Use run for one-off tasks:
# Run database migrations
docker compose run migrate npm run migrate:up
# Run tests
docker compose run --rm test npm test
# Execute scripts
docker compose run --rm app python manage.py createsuperuser
# Start interactive session with overrides
docker compose run --rm -e DEBUG=true web bash
Key differences
1. Container lifecycle
# exec: uses existing container
docker compose up -d web
docker compose exec web echo "Existing PID: $$"
# Output: Existing PID: 1
# run: creates new container
docker compose run web echo "New PID: $$"
# Output: New PID: 1 (different container!)
2. Port mapping
services:
web:
image: nginx
ports:
- "8080:80"
# exec: uses existing port mapping
docker compose exec web curl localhost:80 # Works
# run: NO port mapping by default
docker compose run web curl localhost:80 # Works
# But from host: curl localhost:8080 # Doesn't work!
# run with ports:
docker compose run --service-ports web # Now ports are mapped
3. Dependencies
services:
web:
image: myapp
depends_on:
- db
- redis
# exec: dependencies already running
docker compose exec web bash # db and redis are up
# run: doesn't start dependencies by default
docker compose run web bash # db and redis NOT started!
# run with dependencies:
docker compose run --deps web bash # Starts db and redis first
Useful flags
For exec:
# Run as different user
docker compose exec -u root web bash
docker compose exec -u 1000 web bash
# Set working directory
docker compose exec -w /app web ls
# Set environment variable
docker compose exec -e DEBUG=true web npm start
# Disable TTY
docker compose exec -T web cat /etc/hosts > hosts.txt
# Run in detached mode
docker compose exec -d web long-running-script.sh
For run:
# Remove container after exit
docker compose run --rm web bash
# Run with service ports
docker compose run --service-ports web
# Run with dependencies
docker compose run --deps web bash
# Override entrypoint
docker compose run --entrypoint /bin/sh web
# Set name
docker compose run --name my-debug-container web bash
# Run in detached mode
docker compose run -d web background-job.sh
Real-world debugging scenarios
Scenario 1: Debug production issue
# 1. Check running containers
docker compose ps
# 2. Enter the problematic container
docker compose exec web bash
# 3. Inside container: check processes
ps aux | grep node
# 4. Check environment
env | grep NODE_
# 5. Test internal connectivity
curl http://api:3000/health
# 6. Review logs
tail -f /app/logs/error.log
Scenario 2: Run maintenance tasks
# Database backup (new container)
docker compose run --rm db pg_dump -U postgres mydb > backup.sql
# Clear cache (existing container)
docker compose exec redis redis-cli FLUSHALL
# Run migrations (new container with cleanup)
docker compose run --rm migrate npm run migrate:up
# Seed database (new container)
docker compose run --rm --env SEED_USERS=100 seeder
Choose wisely: exec for running containers, run for fresh starts!