Coding Agents & Terminal · recorded 2026-08-29 · applies to 1 tool(s) ✓ Verified

Problem: docker exec reports the input device is not a TTY / container is not running / executable file not found —— Trove problem library

Verified 2026-08-29docker exec reports not a TTY — drop -t for non-interactive runs: `docker exec <container> sh -c "cmd"`.

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 exec includes -t by 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 no bash (only sh).

-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

EnvironmentNotes
Script/CI/pipelineDrop -t, keep only -i (when stdin is needed)
docker composeexec -T disables TTY
Stopped containercontainer is not running; docker start / check logs first
alpine minimal imageNo 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

  1. Non-interactive commands: drop -t: 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).
  2. Compose scenario: docker compose exec -T <service> command (-T = disable TTY); old v1 can add export COMPOSE_INTERACTIVE_NO_CLI=1, but new versions use -T.
  3. Detect TTY: in scripts, [ -t 0 ] checks whether stdin is a terminal; decide conditionally whether to add -t.
  4. Container not running: docker start <c> first, or docker logs <c> to see why it exited (investigate exit-code-1 containers' startup failure before blindly starting).
  5. Target command missing: docker exec <c> ls /bin /usr/bin first to see what's in the image; alpine uses sh; for bash switch to ubuntu/debian images, or docker exec <c> sh -c "command".
  6. 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 start recovers.

Evidence details kept locally (2026-08-29)

Related canonicals

Read in order: precondition -> same root / same domain / related:

Applicable tools (1)

This problem is linked from the tool pages below — reachable from either side.

docker

Source & evidence

Recorded Verified ✓ VerifiedApplies to 1 tool(s)
Entries contain locally measured or reviewable-source solutions; unmeasured items are explicitly labeled.

Source: https://github.com/docker/compose/issues/7306

← Coding Agents & Terminal domain | Problem Catalog Experience | Facing another issue? Go to the Helpdesk