Introduction

docker exec -it works only when two assumptions are true: your current shell can provide interactive stdin, and the target process can attach a pseudo-terminal. In local terminals that is usually fine. In CI jobs, remote automation, background shells, or detached service contexts, it often is not. The result is the familiar the input device is not a TTY or tty not available style failure.

Symptoms

  • docker exec -it fails in CI but works locally
  • Running the same command without -t succeeds
  • The container is healthy, but interactive shell attachment fails
  • The issue appears when the command is piped, scripted, or run over a non-interactive session

Common Causes

  • The current shell does not have an interactive TTY to pass through
  • -t is used in a pipeline or job runner where stdin is not attached
  • The command should be non-interactive and only needs -i or no TTY flags at all
  • The target container exited or is restarting while the exec command runs

Step-by-Step Fix

  1. 1.Check whether your current shell is interactive
  2. 2.If stdin is not a real TTY, -t will fail before the container is even the problem.
bash
tty
test -t 0 && echo "interactive" || echo "not interactive"
  1. 1.Retry with the minimum flag set
  2. 2.Remove -t first. Many scripted commands need stdin but do not need a pseudo-terminal.
bash
docker exec -i my-container sh -c 'env | sort | head'
docker exec my-container sh -c 'ls -lah /app'
  1. 1.**Use -it only for real interactive sessions**
  2. 2.Keep the pseudo-terminal only when you are actually opening an interactive shell.
bash
docker exec -it my-container /bin/sh
docker exec -it my-container bash
  1. 1.Adjust CI and automation commands explicitly
  2. 2.In CI, prefer non-interactive forms and avoid copying local terminal habits into job scripts.

```bash # Good for CI docker exec my-container php artisan migrate --force

# Better than -it in pipelines docker exec -i my-container mysql -uroot -psecret < schema.sql ```

Prevention

  • Use -it only for human-driven interactive shells
  • In automation, choose docker exec, docker exec -i, or docker exec -t intentionally
  • Add simple shell interactivity checks in CI debugging scripts
  • Keep local and CI command examples separate in docs and runbooks