Run a Command in a Running Docker Container with exec
You will finish with a reliable way to inspect or run one command in an already running Docker container, while keeping the host shell, container shell and command exit status distinct. The examples match Docker Community Edition CLI 29.8.1 and the installed docker-container-exec(1) manual.
The route
Jump straight to the step you need, or tick off Done means at the end.
- 1. Confirm the command and choose a target
- 2. Run a harmless command without a shell
- 3. Use a shell only when shell syntax is required
- 4. Select the user and working directory explicitly
- 5. Add environment values without confusing scopes
- 6. Choose interactive and detached modes deliberately
- 7. Read failures by their exit status
- 8. Treat privileged exec as a security boundary change
Allow about ten minutes. You need a running container and permission to use the Docker daemon. The examples read state or start short-lived processes inside the container. They do not restart a service, change an image or edit a container's persistent configuration.
1. Confirm the command and choose a target
List running containers first. This is an ordinary Docker client command, although access to the daemon may require membership of the Docker group or elevated privileges on your host:
$ docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}'
NAMES STATUS IMAGE
web Up 12 minutes example/web:1.2
Use the exact container name or ID in place of CONTAINER_NAME. A container must be running. A stopped container has no process in which to start the command, and an exec process only exists while the container's primary process, PID 1, is running.
Checkpoint: record one value from docker ps, then confirm Docker can inspect it:
$ docker inspect --format '{{.State.Status}}' CONTAINER_NAME
running
Replace CONTAINER_NAME in every later example. Do not copy the placeholder literally.
2. Run a harmless command without a shell
The command after the container name is passed directly to the container. Start with an executable whose result is easy to recognise:
$ docker container exec CONTAINER_NAME printf '%s\n' 'container command ok'
container command ok
docker exec is an alias for docker container exec. The long form makes scripts and notes clearer, so it is used here. printf is the command inside the container; the outer shell handles the quoting before Docker sends the resulting arguments.
This form does not invoke a shell inside the container. That is usually safer and less confusing when you already know the executable and its arguments. It also avoids accidentally relying on a shell, shell aliases or shell startup files that the image may not contain.
Checkpoint: capture the status immediately after the command:
$ docker container exec CONTAINER_NAME printf '%s\n' 'status check'
status check
$ printf 'exit status: %s\n' "$?"
exit status: 0
Docker returns the contained command's exit status when it runs normally. Status 0 means that the command completed successfully, not that every operation in the container is healthy.
3. Use a shell only when shell syntax is required
Use /bin/sh -c when you genuinely need a pipeline, variable expansion or conditional logic. Quote the whole script so the host shell does not consume it first:
$ docker container exec CONTAINER_NAME /bin/sh -c 'printf "workdir: %s\n" "$PWD"; id -u'
workdir: /
0
The exact output depends on the image. The installed manual says that commands run as root by default, so id -u normally prints 0. That is a container identity, not permission to become root on the host.
Do not put untrusted input into a string passed to sh -c. Shell metacharacters can turn data into another command. If the task is just to print a value or inspect a file, pass arguments directly instead of introducing a shell.
4. Select the user and working directory explicitly
When root is not appropriate, choose a container username or numeric UID with --user. The accepted forms include user, user:group, uid and uid:gid:
$ docker container exec --user 1000:1000 CONTAINER_NAME id
uid=1000 gid=1000 groups=1000
The UID must exist or be meaningful for the image and command. If you omit --user, the command runs as root inside the container according to the local manual. That default is convenient for diagnosis but is a poor assumption for routine application actions.
Set a working directory with --workdir when a relative path matters:
$ docker container exec --workdir /app CONTAINER_NAME pwd
/app
The directory must exist in the container. If it does not, Docker reports an error before the requested command can run. Use an absolute path when a script must not depend on the image's default directory.
5. Add environment values without confusing scopes
Pass a value for this exec process with --env:
$ docker container exec --env RUN_MODE=check CONTAINER_NAME /bin/sh -c 'printf "%s\n" "$RUN_MODE"'
check
This does not permanently change the container's environment or its image. It applies to the process started by this exec request and its children. Use --env-file FILE for a file of environment variables when that is easier to review, but treat the file as sensitive if it contains tokens or passwords. Do not put credentials into shell history or paste them into shared terminals.
6. Choose interactive and detached modes deliberately
For a command that reads from your terminal, keep standard input open with --interactive and allocate a pseudo-terminal with --tty:
$ docker container exec --interactive --tty CONTAINER_NAME /bin/sh
/ # printf '%s\n' 'inside container'
inside container
/ # exit
Use --tty for a human session, not for a script whose output will be parsed. The prompt and terminal control characters can make logs difficult to read. The exit shown above closes the shell process; it does not stop the container. If you need to leave a program running, use the program's own backgrounding or a suitable supervisor rather than assuming a detached Docker client will manage it.
--detach starts the requested command in the background and returns without attaching to its output:
$ docker container exec --detach CONTAINER_NAME /bin/sh -c 'sleep 30'
7d9b2c1e...
The returned identifier is for the exec process. Detached work is still tied to the container's lifetime: it will not be restarted if the container is restarted, and it ends when the container's primary process ends. Avoid detached commands for important work unless you have a separate way to monitor completion.
7. Read failures by their exit status
Check a failure with a command that is safe and intentional:
$ docker container exec CONTAINER_NAME definitely-not-a-command
docker: Error response from daemon: ...
$ printf 'exit status: %s\n' "$?"
exit status: 127
Status 127 means that the contained command could not be found. Status 126 means that the command was found but could not be invoked, such as a non-executable path. Other non-zero values normally come from the contained command itself. The wording varies by Docker and image version, so use the numeric status and the full error text when diagnosing.
Paused-container behaviour is version-specific. The installed September 2026 manpage says that exec waits until the container is unpaused, so a command that appears to hang may be waiting on container state. The current Docker reference documents a paused-container error instead. Check your local docker version and the daemon error before deciding that a request is stuck. Inspect state with docker inspect --format '{{.State.Status}}' CONTAINER_NAME from another terminal. Unpausing a production container changes service state and needs the operator responsible for it.
8. Treat privileged exec as a security boundary change
Do not add --privileged to make an error disappear. The option gives the exec process extended Linux capabilities. It changes what that process may do within the container and can weaken the isolation you are relying on. Use it only for a documented, time-limited administrative task, and record who authorised it and what command ran.
There is no undo for capabilities already used by a command. For ordinary inspection, prefer the default capability set and an explicit non-root --user. If a command needs a capability that was not granted when the container was created, recreate the container with a reviewed security design rather than treating privileged exec as a routine repair.
Done means
- You selected a container reported as
running. - You ran a direct command and checked its exit status.
- You used
/bin/sh -conly where shell syntax was necessary. - You chose the user, working directory, environment and terminal mode explicitly when they mattered.
- You can distinguish status 126, status 127, a paused container and a command's own non-zero result.
- You did not use
--privilegedunless the security impact was authorised and understood.