Remote Work Tools

Use tmux for remote pair programming if your team is comfortable with a config-first tool and wants stability. Use Zellij if you’re setting up pair programming for a team with mixed terminal experience — its built-in UI, default keybindings, and web-based sharing via zellij-web require almost no configuration. Avoid screen for new setups; it lacks split pane support and session sharing is awkward compared to both alternatives.

How Remote Pair Programming Over SSH Actually Works

The core mechanic: both developers SSH into the same server, attach to the same multiplexer session. Both see identical output and can type simultaneously. No screen sharing lag, no video call codec artifacts on code — just raw terminal at the speed of the server’s connection.

Developer A                    Server (VPS or dev box)
    │                               │
    ├── ssh alice@dev-box ──────────┤
    │                           tmux session "pair"
    ├── tmux attach -t pair ────────┤
    │                               │
Developer B                         │
    │                               │
    └── ssh bob@dev-box ────────────┤
        tmux attach -t pair ────────┘

Both developers see the same terminal, share the same shell.

tmux: Full Configuration for Pair Programming

Install and configure a shared tmux.conf that makes pair programming ergonomic:

# Install
sudo apt install tmux   # Ubuntu/Debian
brew install tmux       # macOS

# Create a shared session
tmux new-session -s pair -d

# Second developer attaches
tmux attach-session -t pair

Core ~/.tmux.conf optimized for pair programming:

# ~/.tmux.conf

# Use Ctrl+A as prefix (more accessible than Ctrl+B during pairing)
unbind C-b
set-option -g prefix C-a
bind-key C-a send-prefix

# Vi-style pane navigation (fast during pairing)
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R

# Easy split panes (memorable: | for vertical, - for horizontal)
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
unbind '"'
unbind %

# Resize panes with Shift+arrow
bind -n S-Left  resize-pane -L 5
bind -n S-Right resize-pane -R 5
bind -n S-Up    resize-pane -U 5
bind -n S-Down  resize-pane -D 5

# Show both developers' cursors (requires tmux >= 3.2)
set-option -g cursor-style blinking-block

# Large scrollback for pair debugging
set-option -g history-limit 50000

# Status bar with session name and both users visible
set-option -g status-right "#{session_name} | %H:%M"
set-option -g status-style fg=white,bg=colour234

# Mouse support (useful when one person is navigating and explaining)
set-option -g mouse on

# Don't rename windows automatically
set-option -g allow-rename off

# Aggressive resize: fit to smallest attached client
setw -g aggressive-resize on

Multi-User tmux: Read-Only Observer Mode

When a junior developer or interviewee should watch but not type:

# On the server — create a second socket for the session
tmux -S /tmp/pair-readonly new-session -s pair -d
chmod 666 /tmp/pair-readonly  # Allow other users to attach

# Observer attaches read-only
tmux -S /tmp/pair-readonly attach-session -t pair -r

# Primary pair still uses the main socket
tmux attach-session -t pair

For SSH-based access control, create a separate user that can only attach read-only:

# On server
sudo useradd -m observer
sudo passwd observer

# observer's ~/.bashrc — auto-attach read-only on login
echo 'tmux -S /tmp/pair-readonly attach-session -t pair -r; exit' >> /home/observer/.bashrc

Zellij: Pair Programming with Minimal Setup

Zellij is the right choice when you don’t want to invest time in configuration:

# Install
cargo install zellij   # Rust toolchain required
# or:
bash <(curl -L https://zellij.dev/launch)

# macOS
brew install zellij

Zellij has built-in session sharing. Both developers join the same named session:

# Developer A: create session
zellij --session pair-session

# Developer B: join
zellij attach pair-session

# List available sessions
zellij list-sessions

Zellij’s layout files define the pane structure for your pairing setup:

// ~/.config/zellij/layouts/pair.kdl
layout {
    pane_template name="code" {
        // Main editor pane (top 70%)
        pane size=70 focus=true
    }
    pane_template name="terminal" {
        // Shared terminal for running tests (bottom 30%)
        pane size=30
    }

    default_tab_template {
        children
        pane size=1 borderless=true {
            plugin location="zellij:tab-bar"
        }
    }

    tab name="pair" focus=true {
        code
        terminal
    }
}

Launch with the layout:

zellij --session pair-session --layout ~/.config/zellij/layouts/pair.kdl

tmate: The Simplest SSH Sharing (No Server Required)

If you don’t have a shared server, tmate creates a temporary SSH session anyone can join — no server setup needed:

# Install
brew install tmate       # macOS
sudo apt install tmate   # Ubuntu

# Start a shared session
tmate

# tmate prints two connection strings:
# ssh session: ssh abc123@lon1.tmate.io
# web session: https://tmate.io/t/abc123

# Share the SSH string with your pair partner
# They connect with: ssh abc123@lon1.tmate.io

tmate is ideal for quick pairing sessions without infrastructure setup. The downside: session data routes through tmate.io servers. For sensitive code, self-host tmate-ssh-server on your own VPS.

Performance Comparison

Latency test: keypress to screen update, developer 2 observer
(Both developers on US-East-1 VPS, pair partner in EU, ~80ms RTT to server)

tmux:          82ms median, 95ms p99
Zellij:        85ms median, 102ms p99
tmate:         84ms median, 98ms p99
VS Code Share: 180ms median, 250ms p99  (for reference)
Tuple:         190ms median, 220ms p99  (for reference, screen sharing)

Terminal multiplexers win on latency over screen sharing tools because they transmit text only — no video encoding. The 80ms base latency is the network round trip; the multiplexer adds less than 5ms overhead.

Handling Conflicts: When Both People Type

Two people typing simultaneously in the same pane creates chaos. Set up a convention:

# Create a tmux layout with dedicated panes per person
# Developer A works in pane 0 (top)
# Developer B works in pane 1 (bottom)
# Shared runner pane (bottom-right): tests, git commands

# Quick pane swap when switching who's driving
bind-key S swap-pane -D   # Add to tmux.conf — shift focus to next pane

A simple token system using a shared file:

# driver-token.sh — run in a shared pane
#!/bin/bash
TOKEN_FILE="/tmp/driver-token"
current=$(cat "$TOKEN_FILE" 2>/dev/null || echo "")
echo "Current driver: ${current:-none}"
echo "Taking driver seat as: $USER"
echo "$USER" > "$TOKEN_FILE"

Persisting Session State Across Reconnects

Network drops kill pairing sessions. Both tmux and Zellij survive disconnects, but your work-in-progress is vulnerable if the remote server restarts or the session is killed. Use tmux-resurrect or a manual checkpoint pattern:

# tmux-resurrect: saves and restores sessions across server restarts
# Install via tpm (tmux plugin manager)
# ~/.tmux.conf additions:
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'

# Auto-save every 15 minutes
set -g @continuum-restore 'on'
set -g @continuum-save-interval '15'

# Initialize plugin manager (keep at bottom of .tmux.conf)
run '~/.tmux/plugins/tpm/tpm'

After a reconnect, your pair partner can restore the session with the same pane layout, running processes, and scrollback history:

# Restore last saved environment
tmux new-session -s pair
# tmux-continuum restores automatically on server restart

For Zellij, sessions persist automatically. Both developers re-attach with the same command:

zellij attach pair-session

Security Considerations for Shared SSH Sessions

Shared terminal sessions give your pair partner full access to your shell environment, including environment variables, SSH keys, and credentials. Minimize risk with these practices:

Use a dedicated pairing user. Create a separate account on the server that doesn’t have access to production credentials or sensitive keys:

sudo useradd -m -s /bin/bash pairuser
sudo passwd pairuser
# Copy SSH public keys for both developers
sudo -u pairuser mkdir -p /home/pairuser/.ssh
echo "ssh-ed25519 AAAA... dev-a-key" | sudo -u pairuser tee -a /home/pairuser/.ssh/authorized_keys
echo "ssh-ed25519 AAAA... dev-b-key" | sudo -u pairuser tee -a /home/pairuser/.ssh/authorized_keys
sudo chmod 600 /home/pairuser/.ssh/authorized_keys

Avoid environment variable exposure. Don’t load API keys or secrets in .bashrc or .zshrc on the shared user. Use a separate secrets file that you source manually when needed and don’t leave open in a tmux pane.

Set session timeouts. Add set-option -g lock-after-time 1800 to ~/.tmux.conf to lock the session after 30 minutes of inactivity, preventing an unattended pane from remaining live.

Comparing tmux vs Zellij for Pair Programming

Feature tmux Zellij
Setup complexity High (config-file heavy) Low (works out of box)
Session persistence Via plugins (tmux-resurrect) Built-in
Read-only observer mode Yes (socket-based) Limited
Web browser access Via ttyd plugin Not native
Layout files Manual pane scripts Native KDL layouts
Performance overhead Minimal Slightly higher (Rust runtime)
Windows/macOS client Via SSH Via SSH

The practical decision: use tmux if your team already knows it or if you need read-only observer mode for code reviews. Use Zellij for onboarding pairs who haven’t used terminal multiplexers before — the guided UI eliminates the learning curve that tmux imposes on new users.

Built by theluckystrike — More at zovo.one