docker exec runs a command inside a running container. It’s how you open a shell to look around, run a database client, check a config file or trigger a maintenance task — without restarting anything.

Open a shell

docker exec -it myapp bash
  • -i keeps STDIN open (interactive).
  • -t allocates a terminal (so you get a prompt, colours and line editing).

Many images — Alpine-based ones especially — don’t include bash. Use sh:

docker exec -it myapp sh

If you’re not sure which exists, try bash and fall back:

docker exec -it myapp sh -c 'command -v bash >/dev/null && exec bash || exec sh'

Type exit (or press Ctrl+D) to leave. The container keeps running — exec’d processes are separate from the container’s main process.

Run a single command

No -it needed when you just want output:

docker exec myapp ls -la /app
docker exec myapp cat /etc/os-release
docker exec nginx nginx -t                    # test Nginx config
docker exec nginx nginx -s reload             # reload it
docker exec myapp env                         # environment variables

Commands with pipes, redirects or && must run inside a shell in the container:

docker exec myapp sh -c 'ps aux | grep node'
docker exec myapp sh -c 'echo "debug=true" >> /app/config.ini'

Without sh -c, the pipe would run on the host.

Databases

docker exec -it postgres psql -U postgres
docker exec -it mariadb mariadb -u root -p
docker exec -it redis redis-cli
docker exec -it mongo mongosh

Dump a database straight to a file on the host:

docker exec postgres pg_dump -U postgres appdb > appdb.sql
docker exec mariadb sh -c 'mariadb-dump -u root -p"$MARIADB_ROOT_PASSWORD" appdb' > appdb.sql

Restore by piping in (-i but not -t):

docker exec -i postgres psql -U postgres appdb < appdb.sql

Using -t with redirected input gives “the input device is not a TTY”.

As another user

Containers often run as an unprivileged user; exec uses the same user by default. To act as root (for installing a debugging tool, say):

docker exec -u root -it myapp sh
docker exec -u 1000 myapp id
docker exec -u www-data wordpress wp plugin list    # WP-CLI as the web user

Working directory and environment

docker exec -w /app myapp ls
docker exec -e DEBUG=1 myapp node scripts/check.js

Docker Compose

Use the service name rather than the container name:

docker compose exec app sh
docker compose exec db psql -U app
docker compose exec -T db pg_dump -U app app > backup.sql     # -T disables the TTY

docker compose run --rm app <command> is different: it starts a new container for the service, which is right for one-off tasks like migrations when the service isn’t running.

When there’s no shell at all

Distroless and scratch images contain only the application — no shell, no ls. Options:

  • docker debug (Docker Desktop and recent Docker versions with a subscription) attaches a toolbox shell.
  • Run a tools container in the same namespaces:
docker run --rm -it --pid container:myapp --network container:myapp nicolaka/netshoot

You can now see the app’s processes and network from a container that has tools.

  • Copy files out for inspection: docker cp myapp:/app/config.json .

Container not running?

docker exec only works on running containers:

Error response from daemon: container … is not running

Look at why it stopped with docker logs myapp and docker ps -a. To explore the image instead, start a new container with a shell as the entrypoint:

docker run --rm -it --entrypoint sh myimage:tag

Don’t treat exec as configuration management

Changes made inside a container with exec — installed packages, edited files — are lost when the container is recreated (for example on the next image update). Use exec for inspection and one-off tasks; make permanent changes in the Dockerfile, environment variables, or mounted volumes.

Troubleshooting

  • OCI runtime exec failed: exec: "bash": executable file not found — no bash in the image; use sh.
  • the input device is not a TTY — you used -t in a script or with redirected input; drop the -t.
  • permission denied inside — you’re running as the image’s non-root user; add -u root if appropriate.

More commands in the Docker commands cheat sheet, and for logs see Docker logs.

  • Docker
  • Containers
  • Troubleshooting