Suggest an editImprove this articleRefine the answer for “How to get inside a running Docker container?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`docker exec -it <container> <shell>`** spawns a new shell inside a running container. ```bash docker exec -it web sh # most images (Alpine, distroless-debug, etc.) docker exec -it web bash # if bash is installed (Ubuntu, Debian) docker exec -it -u root web sh # force root inside docker exec -it -e FOO=bar -w /app web sh # custom env + working dir ``` **Key:** exec spawns a NEW process; it does NOT connect to PID 1. The shell runs alongside your app. Distroless and `FROM scratch` images have no shell — exec into them with the binary you actually need or use `--entrypoint sh` on `docker run` for debugging.Shown above the full answer for quick recall.Answer (EN)Image**Getting a shell inside a running container** is the bread-and-butter debugging move. The command is `docker exec`, the magic flags are `-it`, and the gotcha is that not every image has a shell. ## Theory ### TL;DR - `docker exec -it <name> <cmd>` runs `<cmd>` as a NEW process inside the container, not in addition to PID 1. - `-i` keeps stdin open; `-t` allocates a TTY. Both are needed for interactive shells. - Most Linux base images have `/bin/sh`. Some (Ubuntu, Debian) also have `/bin/bash`. Distroless and `FROM scratch` have neither. - The exec'd process inherits the container's namespaces (it sees the same filesystem, network, processes) but is parented by the daemon, not by your app's PID 1. - `docker exec` is for running things in a container that is **already running**. To get a shell when starting a container, use `docker run -it <image> sh` instead. ### Quick example ```bash # Most common: shell into a running container $ docker exec -it web sh / # ls / bin dev etc home lib proc root run sbin sys tmp usr var / # exit # Run a one-off command and exit $ docker exec web ls /etc/nginx # Returns the listing without dropping into a shell. # As root, in a specific dir, with an env var $ docker exec -it -u root -w /etc/nginx -e DEBUG=1 web sh ``` The `-it` combo is so common it gets memorized as one flag. Without it: `-i` only = stdin works but no TTY-aware tools (like vi, less); `-t` only = TTY but you cannot type back; neither = the shell exits immediately. ### Picking the right shell | Image base | Shell available | |---|---| | `alpine`, anything Alpine-based | `/bin/sh` (busybox ash) | | `debian`, `ubuntu`, `node`, `python` | `/bin/sh` and `/bin/bash` | | `nginx:1.27-alpine` etc. | `/bin/sh` | | `distroless` (gcr.io/distroless/...) | None — exec is impossible | | `FROM scratch` | None — only your binary | | `distroless:debug` variants | `/busybox/sh` | When in doubt, try `sh` first. If that fails, try `bash`. If both fail, the image probably has no shell. ### Common flags worth knowing ```bash # Force user (default: image's USER, often root) docker exec -u 1000:1000 web id # Set working directory docker exec -w /app web ls # Pass an environment variable for this exec only docker exec -e DEBUG=1 web env | grep DEBUG # Run detached (background, do not wait for exit) docker exec -d web touch /tmp/done # Pass --privileged to lift capability restrictions (for debugging) docker exec --privileged -it web sh ``` ### Common mistakes **Forgetting `-i` or `-t`** ```bash # WRONG: shell exits immediately because there is no TTY $ docker exec web sh $ ``` With no TTY, `sh` reads from a closed stdin and dies. Always `-it` for interactive. **Trying `bash` on Alpine** ```bash $ docker exec -it nginx-alpine bash OCI runtime exec failed: ... "bash": executable file not found in $PATH ``` Alpine ships with busybox `sh`, not `bash`. Use `sh` instead. **Trying to exec into a `FROM scratch` image** ```bash $ docker exec -it tiny-go-app sh OCI runtime exec failed: ... "sh": executable file not found ``` The image has no shell. To debug, either rebuild with a shell layer, or run `docker run --entrypoint /bin/sh -it <image>` (which still fails if no shell is in the image), or build a separate `*-debug` variant. The standard pattern: distroless `:debug` tags include busybox; use them for debugging only. **Confusing `docker exec` with `docker attach`** ```bash # attach: connects to PID 1's stdio. Killing the shell may stop the container. $ docker attach web # exec: spawns a NEW process. Killing it leaves the container running. $ docker exec -it web sh ``` `docker exec` is what you usually want. `attach` is for cases where you need to see PID 1's stdout/stderr live (sometimes for interactive REPLs as PID 1). ### Real-world usage - **Debugging a misbehaving service:** `docker exec -it web sh` and poke around. - **Running migrations against a DB container:** `docker exec -it db psql -U postgres -f /tmp/migrate.sql` (or `docker compose exec`). - **Triggering a one-off task:** `docker exec api npm run cache:flush`. - **Checking config inside a running container:** `docker exec web cat /etc/nginx/nginx.conf`. ### Follow-up questions **Q:** Why does my exec'd shell die when the container restarts? **A:** The container's restart kills all processes inside, including yours. Exec sessions are not preserved across container restarts. **Q:** Can I exec into a stopped container? **A:** No. `docker exec` requires a running container. For a stopped one, you can `docker start -ai <name>` to revive it with stdio attached, or commit it to a new image and run that with a shell as the entrypoint. **Q:** What is the difference between `docker exec` and `docker compose exec`? **A:** `docker compose exec <service>` is the Compose-aware wrapper — it knows the service name, project context, and connects to the right container automatically. Underneath, it calls `docker exec`. Same flags work. **Q:** How do I run a shell as a different user? **A:** `docker exec -u root -it web sh` (or any UID/username). Useful when the container's default `USER` is non-root and you need to install/inspect something privileged. **Q:** (Senior) When should you exec vs. just rebuild the image? **A:** Exec for diagnosing — check files, env, network state in the live state. Rebuild for fixes — anything you change inside via exec is lost on next restart. The temptation to exec-fix-and-pretend-it-is-good is a real anti-pattern; always reflect the fix in the Dockerfile or config. ## Examples ### Walking around inside a Postgres container ```bash $ docker exec -it db sh / # whoami postgres / # psql psql (16.4) Type "help" for help. postgres=# \l # database list postgres=# \q / # exit ``` Exec gives you the shell; from there it is just a normal shell session. ### Run a one-off backup without an interactive shell ```bash $ docker exec db pg_dump -U postgres app > backup.sql # Backup file lands on your host. Container keeps running. ``` No `-it` needed because there is no interaction; `pg_dump` writes to stdout, the host shell redirects to file. ### Compose: exec into a service by name ```bash $ docker compose exec api sh /app # cat /etc/hosts # /etc/hosts auto-generated by Compose with all sibling service names 127.0.0.1 localhost 172.18.0.3 api 172.18.0.2 db 172.18.0.4 redis ``` Compose's exec is the one you will reach for daily during dev — it knows your project's services without typing container IDs.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.