How to fix stdin: is not a tty in SSH

SSHBEGINNERLOWUpdated May 28, 2026ยท 290 views

The "stdin is not a tty" message appears when SSH runs a command without allocating a terminal, typically with piped input or remote scripts. Force a TTY with the -t flag when one is genuinely needed.

Quick fix

Fixes stdin: is not a tty

ssh -T user@host 'your-command'
  1. Decide whether you actually need a TTY
  2. Use the -t flag to force terminal allocation
  3. Run scripts remotely with the right interpreter
  4. Guard TTY-only commands in shell startup files
ssh โ€” error
ssh -T user@host 'your-command'
stdin: is not a tty

What this error means

This message means SSH detected that standard input (stdin) is not connected to a terminal device, so it did not allocate a pseudo-terminal (PTY) for the remote session. It usually appears as a warning when you run a command over SSH with redirected input (a pipe, file, or here-document) or when a remote shell startup script tries to do something that only makes sense in an interactive terminal. In most cases the command still executes correctly and the message is harmless; it only becomes a real problem when a remote program genuinely requires a TTY (for example, an interactive prompt or a tool that calls `tcgetattr`).

Typical symptoms

  • Message appears: "stdin: is not a tty" when running SSH commands
  • Warning: "Pseudo-terminal will not be allocated because stdin is not a terminal"
  • Piped SSH commands (echo ... | ssh) print the warning
  • Running remote scripts via SSH shows the message
  • Interactive programs misbehave or exit when executed through SSH with redirected input
  • The message shows up in CI/CD logs even though the command otherwise succeeds

Common causes

  1. SSH is invoked with redirected/piped input (using | or <) so it does not allocate a TTY, and a remote command or startup script expected one
  2. A shell startup file (.bashrc, .profile) runs a TTY-only command such as 'mesg y' unconditionally, which fails in non-interactive sessions
  3. A remote command requires an interactive terminal but the SSH session is non-interactive (scripts, automation, cron)
  4. A here-document or script is fed to SSH without an explicit interpreter, so default command handling triggers the warning
  5. Git or other tooling runs over SSH in a CI/CD environment with no terminal attached

How to fix "stdin: is not a tty"

1Decide whether you actually need a TTY

The message is often just a harmless warning. If your remote command runs fine and produces correct output, you can ignore it. Only force a TTY when a remote program genuinely needs one (interactive prompts, top, editors, tools that call tcgetattr).

To silence the warning for a non-interactive command without allocating a TTY, you can explicitly disable PTY allocation:

bash
ssh -T user@host 'your-command'
2Use the -t flag to force terminal allocation

When a remote command truly requires an interactive terminal, force pseudo-terminal allocation with -t:

bash
ssh -t user@host 'your-command'

If SSH itself has no local TTY (for example, you are already inside a pipe or a script), a single -t is not enough. Use -tt to force allocation regardless of the local stdin state:

bash
ssh -tt user@host 'interactive-command'
3Run scripts remotely with the right interpreter

When piping a local script to a remote shell, name the interpreter explicitly so there is no ambiguity, and do NOT force a TTY (a PTY would merge stdout/stderr and echo input, corrupting script output):

bash
ssh user@host 'bash -s' < /path/to/local_script.sh

This streams the local file into the remote bash over stdin and runs without a terminal, which is exactly what a non-interactive script wants. Reserve -t/-tt for scripts that must drive an interactive program.

4Guard TTY-only commands in shell startup files

If .bashrc or .profile runs a command that needs a terminal (like mesg y or stty), wrap it in a TTY check so it only runs in interactive sessions:

bash
# Original (prints "stdin: is not a tty" in non-interactive SSH)
mesg y

# Fixed (only runs when stdin is a terminal)
if [ -t 0 ]; then
    mesg y
fi

This lets your startup files work in both interactive and non-interactive SSH sessions. This is the single most common real fix when the warning appears on every non-interactive connection.

5Handle piped input without forcing a TTY

When you pipe data into a remote command, specify a command that consumes stdin and leave the TTY off. Forcing -t here is wrong: with a PTY, the remote stdin becomes the terminal rather than your pipe, so the piped data may not reach the command.

bash
# Triggers the warning (implicit command + redirected stdin)
echo 'data' | ssh user@host

# Correct: name a command that reads stdin (no -t)
echo 'data' | ssh user@host 'cat > /tmp/file.txt'

Do NOT try to feed a sudo password this way. A command like echo 'password' | ssh -t user@host 'sudo -S cmd' does not work (with -t the pipe is no longer the remote stdin) and exposes the password in your shell history and process list. Instead, configure passwordless privilege for the specific command on the remote host with a tightly scoped sudoers rule:

bash
# On the remote host, e.g. /etc/sudoers.d/deploy (edit via 'sudo visudo -f')
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp

Then run it over SSH with no password and no TTY:

bash
ssh user@host 'sudo systemctl restart myapp'

If an interactive password prompt is genuinely required, run ssh -t user@host 'sudo cmd' and type the password at the prompt rather than piping it.

6Fix Git-over-SSH in CI/CD

In automation, this warning around Git usually means the SSH key is not loaded, not that a TTY is missing. Configure the agent and a non-interactive host-key policy instead of forcing a terminal:

bash
# Start the agent and load the deploy key
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

# Avoid the interactive host-key prompt safely by pinning the key
ssh-keyscan github.com >> ~/.ssh/known_hosts

Git invokes ssh itself and does not need a TTY for git clone/fetch/push, so adding -t is unnecessary and can break the transport. Pinning the host key with ssh-keyscan is preferred over StrictHostKeyChecking=no, which disables host verification.

Advanced notes

Understanding TTY allocation: A TTY (teletypewriter) is a terminal interface for interactive communication. By default ssh allocates a pseudo-terminal (PTY) only when its own stdin is a terminal. When stdin is redirected (a pipe, file, or here-document), SSH skips PTY allocation because the session is non-interactive.

The -t, -tt, and -T flags: -t requests a PTY but is suppressed when SSH has no local TTY; -tt forces a PTY regardless of local stdin; -T explicitly disables PTY allocation, which is the right choice for non-interactive commands and for silencing the warning. Note that a forced PTY combines stdout and stderr into one stream and echoes input, so avoid it for commands whose output you parse.

Why piping into a PTY fails: When a PTY is allocated, the remote command's stdin is the terminal, not your pipe. That is why constructs like echo x | ssh -t host 'cmd' do not deliver x to cmd. Drop -t for piped data, or use a non-interactive design (sudoers NOPASSWD, key-based auth) instead of piping secrets.

Distro and management-panel differences: The warning frequently traces back to startup scripts injected by management panels (such as cPanel) or default /etc/profile.d snippets on RHEL/CentOS that call mesg/stty. The if [ -t 0 ] guard is the durable fix.

Containers and rootless contexts: In Docker/Kubernetes exec, TTY behavior is controlled by explicit flags (docker exec -it, kubectl exec -it); the equivalent of -t must be requested deliberately because there is no attached terminal by default.

Frequently asked questions

What does "stdin: is not a tty" mean?
This message means SSH detected that standard input (stdin) is not connected to a terminal device, so it did not allocate a pseudo-terminal (PTY) for the remote session. It usually appears as a warning when you run a command over SSH with redirected input (a pipe, file, or here-document) or when a remote shell startup script tries to do something that only makes sense in an interactive terminal. In most cases the command still executes correctly and the message is harmless; it only becomes a real problem when a remote program genuinely requires a TTY (for example, an interactive prompt or a tool that calls tcgetattr).
How do I fix "stdin: is not a tty"?
The "stdin is not a tty" message appears when SSH runs a command without allocating a terminal, typically with piped input or remote scripts. Force a TTY with the -t flag when one is genuinely needed.
What causes "stdin: is not a tty"?
SSH is invoked with redirected/piped input (using | or <) so it does not allocate a TTY, and a remote command or startup script expected one A shell startup file (.bashrc, .profile) runs a TTY-only command such as 'mesg y' unconditionally, which fails in non-interactive sessions A remote command requires an interactive terminal but the SSH session is non-interactive (scripts, automation, cron)
Is the "stdin: is not a tty" error serious?
It is a low-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading