Automated Tmux Session Management: Attach to Existing or Create New Sessions on SSH Login


61 views

html

When working with remote servers through SSH, maintaining persistent tmux sessions is crucial for workflow continuity. However, existing solutions often fall short when you need to:

  • Create a new session when none exists
  • Create another session when existing ones are all attached
  • Reuse the first available unattached session when possible

The common pattern ssh -t 'tmux a || tmux || /bin/bash' has limitations:

# This always tries to attach first, which isn't what we want
# when we need a fresh session
ssh -t user@server "tmux attach || tmux new"

Here's a bash function that implements intelligent session selection:

function tmux_connect() {
    # List all tmux sessions
    sessions=$(tmux list-sessions 2>/dev/null)
    
    if [[ -z "$sessions" ]]; then
        # No sessions exist - create new
        tmux new-session
    else
        # Find unattached sessions
        unattached=$(echo "$sessions" | grep -v "(attached)")
        
        if [[ -n "$unattached" ]]; then
            # Use the first unattached session
            session_name=$(echo "$unattached" | head -n 1 | cut -d: -f1)
            tmux attach -t "$session_name"
        else
            # All sessions are attached - create new
            tmux new-session
        fi
    fi
}

To use this with SSH, add it to your remote server's shell configuration:

# ~/.bashrc or ~/.zshrc on remote server
alias tmx='tmux_connect'  # Makes it available as a command

# Then use with SSH:
ssh -t user@server "source ~/.bashrc && tmux_connect"

For more complex scenarios, here's an enhanced version that limits maximum sessions:

function tmux_connect_adv() {
    max_sessions=5
    sessions=$(tmux list-sessions 2>/dev/null)
    session_count=$(echo "$sessions" | wc -l)
    
    if [[ -z "$sessions" ]]; then
        tmux new-session
    else
        unattached=$(echo "$sessions" | grep -v "(attached)")
        
        if [[ -n "$unattached" ]]; then
            session_name=$(echo "$unattached" | head -n 1 | cut -d: -f1)
            tmux attach -t "$session_name"
        elif [[ $session_count -lt $max_sessions ]]; then
            tmux new-session
        else
            echo "Maximum session limit ($max_sessions) reached"
            tmux attach -t $(echo "$sessions" | head -n 1 | cut -d: -f1)
        fi
    fi
}

For seamless integration, create a dedicated script:

#!/bin/bash
# ~/bin/tmux-connector

# All the session logic here
function tmux_connect() {
    # ... previous implementation ...
}

# Execute the function
tmux_connect

Then update your SSH config:

# ~/.ssh/config
Host myserver
    HostName server.example.com
    User myuser
    RequestTTY yes
    RemoteCommand ~/bin/tmux-connector

For those who prefer pure tmux solutions:

ssh -t user@server "tmux list-sessions | grep -v attached | head -n 1 | cut -d: -f1 | xargs -I {} tmux attach -t {} || tmux new-session"

When working with remote servers via SSH, many developers want an intelligent tmux behavior that:

  • Creates a new session when none exists
  • Creates another session when existing ones are all attached
  • Automatically attaches to the first available unattached session

The common solution ssh -t 'tmux a || tmux || /bin/bash' has limitations:

# Problematic behavior:
1. Always tries to attach first (tmux a)
2. Only creates new session if attach fails
3. Doesn't check for unattached sessions

Here's a bash script that implements the desired logic:

#!/bin/bash

# Find unattached sessions
unattached_session=$(tmux list-sessions -F '#{session_name}:#{?session_attached,attached,unattached}' 2>/dev/null | \
    grep ':unattached$' | \
    head -n 1 | \
    cut -d: -f1)

if [ -n "$unattached_session" ]; then
    # Attach to first unattached session
    exec tmux attach -t "$unattached_session"
else
    # Create new session with incremental numbering
    session_num=1
    while tmux has-session -t "session-$session_num" 2>/dev/null; do
        ((session_num++))
    done
    exec tmux new -s "session-$session_num"
fi

To use this as your default SSH command:

# In your ~/.ssh/config
Host your-server
    HostName server.example.com
    User youruser
    RequestTTY yes
    RemoteCommand /path/to/your/tmux_wrapper.sh

For more control over session naming and behavior:

#!/bin/bash

MAX_SESSIONS=5  # Prevent creating unlimited sessions

# Custom session naming based on date/time
new_session_name() {
    echo "dev-$(date +%Y%m%d-%H%M%S)"
}

# Main logic
unattached=$(tmux list-sessions -F '#{session_id}:#{?session_attached,attached,unattached}' | \
    awk -F: '$2 == "unattached" {print $1; exit}')

if [ -n "$unattached" ]; then
    exec tmux attach -t "$unattached"
else
    current_sessions=$(tmux list-sessions 2>/dev/null | wc -l)
    if [ "$current_sessions" -ge "$MAX_SESSIONS" ]; then
        echo "Maximum sessions ($MAX_SESSIONS) reached"
        exec bash  # Fallback to regular shell
    else
        exec tmux new -s "$(new_session_name)"
    fi
fi

The solution should account for:

  • No tmux server running
  • Permission issues
  • Broken pipe scenarios
  • Terminal size adjustments

Here's a more robust version:

#!/bin/bash

cleanup() {
    # Reset terminal settings if something goes wrong
    stty sane
}
trap cleanup EXIT

# Check if tmux is available
if ! command -v tmux >/dev/null 2>&1; then
    exec bash
fi

# Main logic with error handling
if output=$(tmux list-sessions -F '#{session_id}:#{?session_attached,attached,unattached}' 2>&1); then
    unattached_session=$(echo "$output" | \
        grep ':unattached$' | \
        head -n 1 | \
        cut -d: -f1)
    
    if [ -n "$unattached_session" ]; then
        exec tmux attach -t "$unattached_session"
    else
        exec tmux new
    fi
else
    # If list-sessions failed (no server running)
    exec tmux new
fi