When running Docker containers with interactive terminal flags, you might encounter the cryptic error: the input device is not a TTY
. This occurs specifically when combining pipe input with the -it
flags:
$ echo 'hi there' | docker run -it ubuntu cat
the input device is not a TTY
The -t
flag requests a pseudo-terminal (PTY) allocation from Docker, while -i
keeps STDIN open. The conflict arises because:
- Pipes provide non-interactive STDIN (just a data stream)
- PTY expects an interactive terminal device
- Docker enforces this distinction strictly
Working Scenario (Non-TTY):
# Using only -i for pipe input
$ echo 'data' | docker run -i alpine cat
data
Interactive Scenario (TTY Required):
# Proper TTY allocation for interactive shells
$ docker run -it alpine sh
/ #
The error occurs at the kernel level when Docker's PTY allocation fails because:
- The pipe creates a non-terminal file descriptor
- libcontainer attempts to allocate a PTY
- The kernel rejects the allocation (ENOTTY)
For complex cases requiring both pipe input and TTY features:
# Using named pipes for advanced scenarios
$ mkfifo mypipe
$ echo "data" > mypipe & docker run -it --rm alpine sh -c 'cat < /mypipe'
Check terminal allocation status with:
$ docker run -it alpine sh -c 'ls -l /proc/self/fd/0'
lrwx------ 1 root root 64 Jan 1 00:00 /proc/self/fd/0 -> /dev/pts/0
When piping input to docker run -it
, you encounter "the input device is not a TTY" because of conflicting terminal allocation requirements. Let's dissect exactly what happens at the OS level.
TTY (TeleTYpewriter) refers to terminal interfaces in Unix-like systems. The -t
flag requests a pseudo-terminal (pty), which expects:
- Interactive session capabilities (line editing, signal handling)
- Terminal control characters (Ctrl+C, Ctrl+D processing)
- Full duplex communication
When you pipe input (echo 'hi' | docker run...
):
# This works because STDIN comes from pipe (non-TTY)
$ echo 'hi' | docker run -i ubuntu cat
hi
# This fails because -t requires TTY but gets pipe
$ echo 'hi' | docker run -it ubuntu cat
the input device is not a TTY
The Docker daemon performs this check:
- When
-t
is present, Docker callsisatty()
on STDIN - Pipes return false for
isatty()
- Docker enforces TTY allocation only for actual terminals
Option 1: Remove -t
when piping (best for automation)
# For non-interactive pipe scenarios
echo "data" | docker run -i alpine cat
Option 2: Force pseudo-TTY allocation (for edge cases)
# Using script to emulate TTY behavior
echo "data" | script -q /dev/null docker run -it alpine cat
For complex cases requiring both piped input and TTY features:
# Create temporary file for input
echo "input" > tmpfile && \
docker run -it --mount type=bind,src=$(pwd)/tmpfile,dst=/input.txt alpine sh -c "cat /input.txt && exec sh"
Check STDIN type with this diagnostic container:
docker run --rm -it alpine sh -c 'ls -l /proc/self/fd/0 && tty'
Example outputs:
- Pipe input:
lr-x------ 1 root root 64 May 10 00:00 /proc/self/fd/0 -> 'pipe:[12345]'
- TTY input:
lrwx------ 1 root root 64 May 10 00:00 /proc/self/fd/0 -> /dev/pts/0