When executing non-interactive commands via SSH like:
ssh user@host sudo systemctl restart nginx
SSH deliberately avoids allocating a pseudo-terminal (TTY) by default. This design choice stems from fundamental UNIX philosophy and has practical implications.
TTY allocation changes several behaviors:
- Line buffering vs. block buffering
- Signal handling differences (SIGINT propagation)
- Escape sequence interpretation
- Terminal control character processing
Consider these examples with -t
flag:
# Without TTY (clean output)
ssh user@host 'ls -1 | wc -l'
# With TTY (potential formatting issues)
ssh -t user@host 'ls -1 | wc -l'
The forced TTY version might introduce:
- Unexpected carriage returns
- ANSI color codes in pipelines
- Buffering delays in scripts
TTY becomes necessary for:
# Interactive sudo sessions
ssh -t user@host 'sudo visudo'
# Screen/tmux management
ssh -t user@host 'tmux attach'
# Any command requiring terminal control
ssh -t user@host 'top'
Instead of global aliasing ssh -t
, consider these targeted approaches:
# ~/.ssh/config
Host specific-server
HostName server.example.com
RequestTTY force
# For sudo commands requiring TTY
ssh user@host 'sudo -S command <>'
TTY allocation adds overhead:
- Extra roundtrips during connection setup
- Additional encryption/decryption cycles
- Increased memory usage per session
- Default to non-TTY for scripts and automation
- Use
-t
only when terminal features are required - Configure TTY per-host in SSH config when appropriate
- Consider
-tt
(force TTY) for stubborn applications
Diagnose TTY-related issues with:
# Check if TTY is allocated
ssh user@host 'tty'
# Test command with different TTY modes
ssh user@host 'echo $TERM'
ssh -t user@host 'echo $TERM'
When you run a simple SSH command like:
ssh user@host sudo reboot
You might notice that interactive prompts don't work properly, and some programs behave differently. This is because SSH doesn't allocate a pseudo-terminal (pty) by default. The -t
flag forces pseudo-terminal allocation, but why isn't this the default?
There are several important technical reasons for this design:
- Output Buffering: Without a pty, output is sent immediately in raw mode, which is better for automated scripts
- Signal Handling: PTY allocation changes how signals like SIGINT are handled
- Resource Efficiency: Avoiding unnecessary pty allocation saves system resources
- Pipeline Compatibility: Non-interactive commands work better in pipes and scripts without a pty
Here are common cases where you should use ssh -t
:
# Running interactive sudo commands
ssh -t user@host sudo visudo
# Using screen/tmux
ssh -t user@host tmux attach
# Interactive debugging sessions
ssh -t user@host gdb ./program
If you alias ssh
to always use -t
, you might encounter:
# Example of broken pipe behavior with -t
ssh -t user@host cat largefile.txt | head -n 10
The pipeline might not work as expected because the pty adds extra formatting and buffering.
For script usage, consider these patterns:
# For non-interactive commands
ssh user@host 'sudo apt-get update -y'
# For commands needing input but not full interactivity
ssh user@host 'sudo debconf-set-selections <<< "package package/license string accept"'
# For truly interactive sessions
ssh -t user@host
If you're writing scripts that need dynamic PTY handling:
#!/bin/bash
# Detect if stdin is a terminal
if [ -t 0 ]; then
PTY_OPT="-t"
fi
ssh $PTY_OPT user@host "$@"