Quick fix
# One-liner: non-interactive execution - drop -t
docker exec <container> sh -c "your command"
What you will run into
Running docker exec -it ... in a script, CI (Jenkins/GitHub Actions), or pipeline reports the input device is not a TTY; the same family includes container is not running and exec: "bash": executable file not found in $PATH.
Running the same command manually in a terminal works fine - so it always gets treated as voodoo.
Actually the rule is simple: interactive commands (-t) can't run in non-terminal environments.
Why this happens
-t(--tty) requires allocating a TTY pseudo-terminal; a script/CI's stdin isn't a terminal, and the docker CLI refuses to start;docker compose execincludes-tby default, so compose scenarios hit it more easily;container is not running: running exec against a stopped container;executable file not found: minimal images like alpine have nobash(onlysh).
-t means "make the container process believe it is in a terminal": the docker CLI allocates a pseudo-terminal (PTY) for it.
A PTY can only be set up when the client's stdin is itself a terminal; in site docs the stdin is a pipe or file, there is no terminal to allocate, and the CLI refuses before starting.
docker compose exec enables -t by default, which is why compose is the most common place to hit this.
Deterministic takeaway: the input device is not a TTY = the current process has no terminal — drop -t (keep only -i when you need stdin); in scripts, test [ -t 0 ] to decide whether to add -t instead of hard-coding it.
Step by step
1. Non-interactive commands: drop -t — Required
docker exec <c> sh -c "command"
Keep only -i when stdin piping is needed:
echo data | docker exec -i <c> sh -c "cat"
(Both measured working, exit 0.)
2. Compose scenario — Required
docker compose exec -T <service> command
-T = disable TTY. Old v1 can add export COMPOSE_INTERACTIVE_NO_CLI=1; new versions use -T.
3. Container not running — Required
docker start <c>
First docker logs <c> to see why it exited (for exit-code-1 containers, investigate the startup failure before blindly starting).
4. Target command missing in the image — Required
docker exec <c> ls /bin /usr/bin
alpine uses sh; for bash, switch to ubuntu/debian images, or docker exec <c> sh -c "command".
How to confirm it works
docker exec <c> sh -c "echo noninteractive-ok"
Normal output = success. Measured: echo hello | docker exec -i <c> sh -c "cat" -> hello (exit 0); at repro time docker exec -it <c> sh </dev/null -> the input device is not a TTY (exit 1).
Other environments
| Environment | Notes |
|---|---|
| Script/CI/pipeline | Drop -t, keep only -i (when stdin is needed) |
| docker compose | exec -T disables TTY |
| Stopped container | container is not running; docker start / check logs first |
| alpine minimal image | No bash; use sh -c |
Symptom
Running docker exec -it ... / docker compose exec ... in a script, CI (Jenkins/GitHub Actions), or pipeline reports:
the input device is not a TTY
Same-family symptoms: docker exec into a stopped container reports Error response from daemon: container <id> is not running; running docker exec <c> bash on minimal images like alpine reports exec: "bash": executable file not found in $PATH. Signs: interactive commands run in an environment without a terminal; the same command works manually in a terminal - so it always gets treated as voodoo.
Cause
-t (--tty) requires allocating a TTY pseudo-terminal; a script/CI's stdin isn't a terminal, so the docker CLI refuses to start; docker compose exec includes -t by default, making compose scenarios easier to hit.
Solution
- Non-interactive commands: drop
-t:docker exec <c> sh -c "command"; keep only-iwhen stdin piping is needed:echo data | docker exec -i <c> sh -c "cat"(both measured working). - Compose scenario:
docker compose exec -T <service> command(-T = disable TTY); old v1 can addexport COMPOSE_INTERACTIVE_NO_CLI=1, but new versions use-T. - Detect TTY: in scripts,
[ -t 0 ]checks whether stdin is a terminal; decide conditionally whether to add-t. - Container not running:
docker start <c>first, ordocker logs <c>to see why it exited (investigate exit-code-1 containers' startup failure before blindly starting). - Target command missing:
docker exec <c> ls /bin /usr/binfirst to see what's in the image; alpine usessh; for bash switch to ubuntu/debian images, ordocker exec <c> sh -c "command". - Interactive entry into a container (real terminal):
docker exec -it <c> sh, keep -it unchanged.
Verification Evidence
- Local measurement (2026-08-29, WSL Ubuntu, docker 29.1.3, alpine:3.20):
- Repro:
docker exec -it <c> sh </dev/null(non-TTY) ->the input device is not a TTY(exit 1), same as compose#7306; - Fix 1:
echo hello | docker exec -i <c> sh -c "cat"-> hello (exit 0); - Fix 2:
docker exec <c> sh -c "echo noninteractive-ok"-> normal (exit 0); - Form 2: after stopping the container,
docker exec ...->container is not running;docker startrecovers.
Evidence details kept locally (2026-08-29)
Related canonicals
Read in order: precondition -> same root / same domain / related:
- Precondition (rule out first):
- Cannot connect to the Docker daemon (precondition: exec connection errors -> check the daemon first)
Applicable tools (1)
This problem is linked from the tool pages below — reachable from either side.
dockerSource & evidence
← Coding Agents & Terminal domain | Problem Catalog Experience | Facing another issue? Go to the Helpdesk