iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

docker exec

docker exec runs a command inside a running container. Daily-driver for debugging: open a shell, tail a log, run a one-off migration, inspect filesystem.

exec, shells, file ops, debugging

EXAMPLE
# 1) Open a shell
docker exec -it <container> sh                     # alpine / minimal
docker exec -it <container> bash                   # debian / ubuntu
docker exec -u root -it <container> sh             # as root (even if image runs as non-root)

# 2) One-off command
docker exec <container> ls -la /app
docker exec <container> cat /etc/os-release
docker exec <container> env
docker exec <container> ps aux

# 3) Run with environment variables
docker exec -e DEBUG=true <container> /app/bin/script
docker exec -e LOG_LEVEL=debug -e VERBOSE=1 <container> npm run task

# 4) Workdir
docker exec -w /app <container> npm run lint
docker exec -w /var/log <container> tail -f app.log

# 5) Detach + interactive flags
# -i : keep STDIN open (interactive)
# -t : allocate a TTY (terminal)
# -d : detached (run + return immediately)

docker exec -d <container> /app/bin/long-running-task

# 6) Real debugging patterns

# Inspect filesystem
docker exec -it web sh -c 'ls /app && cat /app/config.yml'

# Test connectivity from inside
docker exec -it web sh -c 'wget -qO- http://db:5432'
docker exec -it web sh -c 'nc -zv db 5432'
docker exec -it web nslookup db                    # in non-Alpine; alpine: apk add bind-tools first

# Check process count + memory
docker exec web ps -ef | head
docker exec web cat /proc/1/status | grep VmRSS

# Run a DB migration in the running app container
docker exec -it web npm run migrate
docker exec -it web ./manage.py migrate
docker exec -it web rake db:migrate

# Run a one-off Rails console / Django shell / Node REPL
docker exec -it web rails console
docker exec -it web python manage.py shell
docker exec -it web node

# 7) Copy files in / out (docker cp, not exec — but worth knowing)
docker cp ./local.json web:/app/config/local.json
docker cp web:/app/logs/app.log ./app.log
docker cp web:/app/dist ./dist                     # whole directory

# 8) Common gotchas

# Permission denied: container's user can't read a path
#   → re-run with -u root for the debug command, then fix permissions in image

# 'OCI runtime exec failed' — typically the container exited mid-command
#   → check `docker ps` (is it still up?); check container logs

# Shell not available: distroless / scratch images
docker exec -it distroless-app sh    # ERROR: shell not in image
# Workaround:
#   - Run a debug sidecar with shared PID namespace
#   - Or use kubectl debug (Kubernetes equivalent — ephemeral debug container)
#   - Or temporarily build a debug variant of your image

# 9) Compose syntax
docker compose exec web sh
docker compose exec -e DEBUG=true web npm run task
docker compose run --rm web npm run migrate         # 'run' starts a fresh container instead of exec

# 'exec' vs 'run':
#   exec : reuses the RUNNING container, current state, current network
#   run  : new container from the same image, fresh state

# 10) Debugging a stopped container
# `exec` only works on RUNNING containers. For stopped:
#   - docker logs <container>          → check last output
#   - docker inspect <container>       → check exit code + restart policy
#   - docker commit <container> debug-img  → snapshot the state
#   - docker run --rm -it debug-img sh → poke around

# 11) Real workflows

# Quick CPU / memory snapshot
docker stats --no-stream <container>

# Tail logs without exec
docker logs -f --tail=100 web

# Network namespace tools
docker exec -it web sh -c 'apk add curl iproute2 || apt-get update && apt-get install -y curl iproute2'
docker exec -it web ss -tlnp

# Database REPL inside the DB container
docker exec -it pg psql -U postgres -d myapp
docker exec -it mysql mysql -u root -p
docker exec -it redis redis-cli
docker exec -it mongo mongosh

# 12) Security — exec is privileged enough to be careful
# - In production: restrict who can run docker on the host (docker group = effectively root)
# - Log every exec session if it touches sensitive containers
# - Kubernetes equivalent: kubectl exec — restrict via RBAC
# - For 'investigative access' instead of full shell: deploy a sidecar with read-only mounts

# 13) When you reach for exec a lot
# Maybe your app needs:
#   - Better logs (structured JSON to stdout)
#   - Health endpoints (HTTP /health)
#   - Admin endpoints / commands (CLI in the binary)
#   - Observability (Prometheus metrics, distributed tracing)
# Reduce the need to shell into prod containers.

# 14) Aliases worth adding
alias dex='docker exec -it'
alias dexb='docker exec -it bash'    # adjust for sh/bash
alias dlog='docker logs -f --tail=100'
alias dps='docker ps'
alias dimg='docker images'

Why it matters

docker exec -it <c> sh is your default debugging move. For distroless images or stopped containers, use docker run from a snapshot, docker logs, or kubectl debugexec only works on a live one with a shell.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
docker exec -it api sh
docker exec api ls /app
Try it Yourself »

Discussion

Loading…