Guide for using tmux to run and manage long-running processes programmatically. Use when AI agents need to start background processes, capture their output, and manage them via the tmux CLI. Ideal for dev servers, build processes, and other commands that need to run independently of the agent's lifecycle.
TMUX allows agents to run long-running commands in background sessions that persist beyond the agent's execution. Sessions can be queried, controlled, and managed via CLI commands.
Create a detached session running a command:
tmux new-session -d -s <session-name> "<command>"
Examples:
tmux new-session -d -s devserver "npm run dev"
tmux new-session -d -s build "make build"
tmux new-session -d -s python "python3 server.py"
Best practices for session names:
devserver, test-runner, watch-buildSend input to a running session:
tmux send-keys -t <session-name> "<command>" C-m
The C-m sends a carriage return (equivalent to pressing Enter).
Examples:
# Send a restart message
tmux send-keys -t devserver "echo 'restarting...'" C-m
# If 'rs' is a shell alias for restart
tmux send-keys -t devserver "rs" C-m
Capture the current pane content:
tmux capture-pane -t <session-name> -p
Options:
-p: Print to stdout (instead of internal buffer)-S -: Capture entire scrollback history-e: Include escape sequences (for colors)-N <lines>: Capture specific number of lines from bottomExamples:
# Capture visible content
tmux capture-pane -t devserver -p
# Capture full history
tmux capture-pane -t devserver -S - -p
# Capture last 50 lines
tmux capture-pane -t devserver -N 50 -p
tmux list-sessions
# or
tmux ls
Output format: <session-name>: <windows> (created <timestamp>) [attached]
Check if a specific session exists:
tmux has-session -t <session-name>
Returns exit code 0 if exists, 1 if not. Useful in scripts.
Terminate a session:
tmux kill-session -t <session-name>
Kill all sessions:
tmux kill-server
Caution: kill-server terminates all tmux sessions system-wide.
Create a new window in an existing session:
tmux new-window -t <session-name> -n <window-name> "<command>"
List windows in a session:
tmux list-windows -t <session-name>
By default, commands target pane 0 of the current window. To target specific panes:
tmux send-keys -t <session-name>:<window>.<pane> "<command>" C-m
tmux capture-pane -t <session-name>:<window>.<pane> -p
Example:
# Captures pane 2 of window 1
tmux capture-pane -t devserver:1.2 -p
Set environment variables for a session:
tmux new-session -d -s mysession "ENV_VAR=value npm run dev"
# Start a dev server
tmux new-session -d -s devserver "npm run dev"
# Wait a bit, then check output
sleep 5
tmux capture-pane -t devserver -p
# Later, when done
tmux kill-session -t devserver
# Only start if session doesn't exist
if ! tmux has-session -t devserver; then
tmux new-session -d -s devserver "npm run dev"
fi
# Send command and immediately capture output
tmux send-keys -t devserver "npm test" C-m
sleep 10
tmux capture-pane -t devserver -N 100 -p | grep -A 20 "Tests:"
Important Note: When you kill a tmux session using tmux kill-session, the child processes started within that session are not automatically terminated. They continue running in the background, still holding ports and consuming resources.
To properly stop a long-running process:
Send Ctrl+C to the session (recommended):
tmux send-keys -t <session-name> C-c
Kill the process directly by PID:
# Find the PID first
ps aux | grep <process-name>
# Then kill it
kill <PID>
Kill all processes by name:
pkill <process-name>
Always verify that processes have been terminated:
# Check if the process is still running
ps aux | grep <process-name>
# Check if the port is still in use
lsof -i :<port>
# Try to connect to verify
curl http://localhost:<port>/health
# Start a server in tmux
tmux new-session -d -s webserver "npm run dev"
# Later, when you want to stop it:
# DON'T just: tmux kill-session -t webserver
# INSTEAD do:
# Step 1: Send Ctrl+C
tmux send-keys -t webserver C-c
# Step 2: Verify the process is stopped
ps aux | grep node
lsof -i :8080
# Step 3: Kill the session
tmux kill-session -t webserver
This behavior occurs because when you kill a tmux session, it terminates the shell session but doesn't automatically send termination signals to child processes. The processes are children of the shell, not of tmux itself, so they continue running independently.
If creating a session that exists, tmux will error. Check first:
tmux has-session -t <name> && echo "exists" || echo "free"
Ensure the session is actually producing output. Some processes buffer output unless they detect a TTY. Forcing PTY allocation is not directly available via tmux CLI.
Check if the process hung inside the session. Use tmux capture-pane to see the last output. If necessary, use kill-session to terminate.
For programmatic use, consider creating ~/.tmux.conf:
# Disable status bar for cleaner output capture
set -g status off
# Increase scrollback buffer for more history
set -g history-limit 10000
This is optional but helps when capturing large amounts of output.