Tag: tmux

  • Persistent tmux sessions

    Persistent tmux sessions

    How to start a tmux session after login in a server using ssh, so that if the connection is lost, when I login back, I can go back to the same tmux session.

    Short answer (manual):

    1. SSH into the server: ssh user@server
    2. Start a named tmux session: tmux new -s mysession
    3. Work inside tmux. If your SSH connection drops, reconnect and reattach: ssh user@server tmux attach -t mysession
    4. Useful commands:
      • List sessions: tmux ls
      • Detach from inside tmux: Ctrl-b then d
      • Create or attach in one step: tmux new -As mysession
      • Kill a session: tmux kill-session -t mysession

    Automatic attach/create on login:

    Put this in your ~/.bash_profile (or ~/.profile) so SSH logins automatically attach to (or create) a session named “main”:

    if [[ $- == *i* ]] && [ -n "$SSH_CONNECTION" ] && [ -z "$TMUX" ]; then
      exec tmux new -As main
    fi
    

    Explanation of the checks:

    • [[ $- == *i* ]] ensures the shell is interactive.
    • [ -n "$SSH_CONNECTION" ] ensures this runs only for SSH logins (not local scripts).
    • [ -z "$TMUX" ] avoids nesting tmux if you’re already inside one.
    • exec replaces your login shell with tmux (so Ctrl-d exits the session as expected).

    Caveats / tips:

    • Install tmux if it’s not present (apt/yum/pacman/etc).
    • Using tmux new -As name is handy: it attaches if the session exists, otherwise creates it.
    • If you want the option not to start tmux on some SSH connections, either remove the auto-start or use a different login method.
    • Sessions persist on the server when your client disconnects; reattaching resumes the same environment (processes continue running unless they depend on a client-held resource).

    That’s all you need to reliably get back to the same tmux session after reconnecting.

    Long answer (auto-resume optional):

    • a recommended automatic login snippet for bash/zsh
    • a safer variant that avoids interfering with scp/rsync or non-interactive sessions
    • a systemd-user approach to keep a tmux session running even when no one is logged in
    • tips, caveats and helpful extras
    1. Automatic attach/create on SSH login — simple solution Add this to the end of your ~/.bash_profile (or ~/.profile, or ~/.zprofile for zsh). It will exec tmux so your login shell is replaced by tmux (Ctrl-D will then log you out):
    if [[ $- == *i* ]] && [ -n "$SSH_CONNECTION" ] && [ -z "$TMUX" ]; then
      exec tmux new -As main
    fi
    

    What the checks do:

    • [[ $- == *i* ]] — run only for interactive shells
    • [ -n "$SSH_CONNECTION" ] — run only for SSH sessions (not local)
    • [ -z "$TMUX" ] — don’t start tmux if you’re already inside one
    • exec replaces the shell with tmux (so closing tmux exits the session cleanly)
    1. Safer automatic startup (recommended) The simple snippet above is fine for many, but it can break non-interactive SSH uses (scp, rsync, git over SSH) and other tools. Use a slightly stricter check to run only when logging into a TTY and not during scp/rsync:
    # Put this in ~/.bash_profile or ~/.profile
    if [[ $- == *i* ]] && [ -n "$SSH_CONNECTION" ] && [ -z "$TMUX" ] && [ -t 1 ]; then
      # Choose a session name; optionally include hostname
      session="main-$(hostname -s)"
      exec tmux new -As "$session"
    fi
    

    Notes:

    • [ -t 1 ] ensures stdout is a TTY (avoids running for scp/rsync/git that use no TTY)
    • Using hostname in the session name lets you use the same dotfiles across multiple servers and keep sessions distinct
    1. Variant: offer choice instead of automatically replacing shell If you prefer to be prompted (or want an option to bypass tmux), you can use this approach:
    if [[ $- == *i* ]] && [ -n "$SSH_CONNECTION" ] && [ -z "$TMUX" ] && [ -t 1 ]; then
      session="main-$(hostname -s)"
      echo "Attaching to tmux session '$session' (press Ctrl-C to skip)"
      sleep 1
      tmux new -As "$session"
    fi
    

    This runs tmux but does not exec it; pressing Ctrl-C returns you to the normal shell.

    1. systemd user service (start tmux at boot / independent of login) If you want a tmux session to exist even when nobody is logged in (useful for long-running daemons you manage interactively), create a systemd –user unit that starts a tmux session for your user on boot. Example (~/.config/systemd/user/tmux@.service):
    [Unit]
    Description=tmux session %i
    
    [Service]
    Type=oneshot
    RemainAfterExit=yes
    ExecStart=/usr/bin/env tmux new-session -d -s %i
    ExecStop=/usr/bin/env tmux kill-session -t %i
    
    [Install]
    WantedBy=default.target
    

    Enable / start: systemctl --user enable --now tmux@main.service

    This creates a detached tmux session named “main” that persists while the system/user systemd is running. You can then attach from any SSH session with tmux attach -t main. This is a different model (tmux session lives independent of your shells).

    1. Extra useful tips and tools
    • Use tmux-resurrect / tmux-continuum to save/restore layout, pane commands and scrollback across reboots.
    • If you want multiple named sessions (per project), change session name logic in your login script (e.g., derive name from working directory).
    • Avoid nesting tmux: always check $TMUX.
    • If you use zsh, put the snippet in ~/.zprofile or ~/.zshrc with equivalent conditions.
    • To reattach manually: tmux attach -t main or tmux new -As main
    • To list sessions: tmux ls
    • Make sure tmux is installed: apt install tmux (Debian/Ubuntu), dnf install tmux, pacman -S tmux, etc.
    1. Caveats and troubleshooting
    • exec tmux replaces the login shell. That’s desirable for an always-on workflow, but if you rely on running commands automatically from .bash_profile or .bashrc for non-interactive tasks, verify behavior first.
    • If you need scp/git-over-ssh to continue to work, ensure [ -t 1 ] or similar check so automatic tmux startup doesn’t run for non-interactive connections.
    • If two people attach to the same session simultaneously, they will share the same terminal. This is normal and useful for pair-editing but can be surprising.
    • If your tmux session dies unexpectedly on disconnect, inspect server logs and tmux logs. Usually processes in tmux continue unless they get SIGHUP from some external cause.
    1. Example: final recommended ~/.bash_profile snippet Here’s a single snippet to copy-paste into ~/.bash_profile; it is safe for most environments:
    # Auto-attach to tmux on SSH interactive login
    if [[ $- == *i* ]] && [ -n "$SSH_CONNECTION" ] && [ -z "$TMUX" ] && [ -t 1 ]; then
      session="main-$(hostname -s)"
      # Use exec to replace login shell; remove exec if you want to be able to Ctrl-C out
      exec tmux new -As "$session"
    fi
    

    Snippets for bash, zsh and fish with tmux to start only sometimes

    Below are ready-to-copy snippets for bash, zsh and fish that will offer to attach/create a tmux session on SSH interactive logins — but won’t force it every time. Each snippet:

    • runs only on interactive SSH logins
    • avoids starting tmux if already inside one
    • checks for a TTY (so scp/rsync/git-over-ssh won’t be affected)
    • prompts (with a short timeout) so you can skip easily
    • can be disabled entirely by setting NO_AUTO_TMUX in your environment

    Bash (put in ~/.bash_profile or ~/.profile)

    # Offer to attach/create tmux on interactive SSH login (bash)
    if [[ $- == *i* ]] && [ -n "$SSH_CONNECTION" ] && [ -z "$TMUX" ] && [ -t 1 ] && [ -z "$NO_AUTO_TMUX" ]; then
      session="main-$(hostname -s)"
      printf "Attach to tmux session '%s'? [Y/n] (auto Y in 5s) " "$session"
      # read one char with 5s timeout
      read -t 5 -n 1 answer
      printf "\n"
      answer=${answer:-Y}
      case "$answer" in
        [Yy]) tmux new -As "$session" ;;
        *) ;;  # skip
      esac
    fi
    

    Zsh (put in ~/.zprofile or ~/.zshrc; ~/.zprofile for login shells)

    # Offer to attach/create tmux on interactive SSH login (zsh)
    if [[ -o interactive ]] && [ -n "$SSH_CONNECTION" ] && [ -z "$TMUX" ] && [ -t 1 ] && [ -z "$NO_AUTO_TMUX" ]; then
      session="main-$(hostname -s)"
      printf "Attach to tmux session '%s'? [Y/n] (auto Y in 5s) " "$session"
      # zsh: read one key with timeout
      read -k 1 -t 5 answer
      printf "\n"
      answer=${answer:-Y}
      case "$answer" in
        [Yy]) tmux new -As "$session" ;;
        *) ;;  # skip
      esac
    fi
    

    Fish (put in ~/.config/fish/config.fish)

    # Offer to attach/create tmux on interactive SSH login (fish)
    if status --is-interactive; and set -q SSH_CONNECTION; and not set -q TMUX; and test -t 1; and not set -q NO_AUTO_TMUX
      set session main-(hostname -s)
      printf "Attach to tmux session '%s'? [Y/n] (auto Y in 5s) " $session
      # read one char with timeout (-t 5) and max chars (-n 1)
      read -t 5 -n 1 answer
      printf "\n"
      if test -z "$answer" -o "$answer" = "Y" -o "$answer" = "y"
        tmux new -As $session
      end
    end
    

    Notes and tips

    • Session naming: these snippets use session="main-$(hostname -s)" so each host gets its own session. Change “main” if you prefer something else.
    • tmux command: tmux new -As name will attach to the existing session or create it if missing.
    • To skip on a particular login: press Ctrl-C (before the timeout) or press ‘n’ when prompted.
    • To globally disable the prompt (e.g., from an automated login or a wrapper), set NO_AUTO_TMUX (e.g., NO_AUTO_TMUX=1 ssh host, or export NO_AUTO_TMUX in a wrapper env).
    • If you prefer the login shell to be replaced by tmux (so Ctrl-D exits the session), change tmux new -As "$session" to exec tmux new -As "$session" in the snippet.