Skip to content

Instantly share code, notes, and snippets.

@daemonhorn
Created April 25, 2026 15:04
Show Gist options
  • Select an option

  • Save daemonhorn/3aaecac9d3ab98077f1d80dd7af000a6 to your computer and use it in GitHub Desktop.

Select an option

Save daemonhorn/3aaecac9d3ab98077f1d80dd7af000a6 to your computer and use it in GitHub Desktop.
Debian tmux initscript

Auto-Run Shell Scripts in Tmux Sessions on Debian

This guide walks through creating a System V init script on Debian stable that launches a set of shell scripts in individual named tmux sessions, running as a non-privileged local user.

Configuration — including which user to run as — lives in /etc/default/tmux-sessions, the standard Debian location for externalizing init script settings. The init script itself contains no hardcoded usernames or paths.


Prerequisites

Install tmux if it is not already present:

sudo apt-get install tmux

Decide which user will own the sessions. This guide uses appuser as the example value; it is set in /etc/default/tmux-sessions, not in the init script. The user must already exist:

id appuser

The Scripts to Launch

Assume the following scripts exist and are executable:

/home/appuser/bin/monitor.sh
/home/appuser/bin/worker.sh
/home/appuser/bin/logger.sh

Each script is a long-running process (loop, server, tail, etc.). Make them executable:

chmod 755 /home/appuser/bin/monitor.sh \
           /home/appuser/bin/worker.sh \
           /home/appuser/bin/logger.sh

The Default Configuration File

Create /etc/default/tmux-sessions to hold all site-specific values. The init script sources this file at startup so nothing user-specific ever needs to live in /etc/init.d/.

sudo nano /etc/default/tmux-sessions
# /etc/default/tmux-sessions
# Edit this file to configure the tmux-sessions init script.

# User to run tmux sessions as (must already exist)
DAEMON_USER="appuser"

# Session name -> script mappings (name:path pairs, space-separated)
# Each entry launches one named tmux session running that script.
SESSIONS="monitor:/home/appuser/bin/monitor.sh worker:/home/appuser/bin/worker.sh logger:/home/appuser/bin/logger.sh"

SESSIONS is a space-separated list of name:path pairs. Adding or removing a session requires only editing this file — the init script needs no changes.

Adding a New Script

Append a new name:path pair to SESSIONS:

SESSIONS="monitor:/home/appuser/bin/monitor.sh \
          worker:/home/appuser/bin/worker.sh \
          logger:/home/appuser/bin/logger.sh \
          newjob:/home/appuser/bin/newjob.sh"

The Init Script

Create /etc/init.d/tmux-sessions:

sudo nano /etc/init.d/tmux-sessions

Paste the following:

#!/bin/sh
### BEGIN INIT INFO
# Provides:          tmux-sessions
# Required-Start:    $local_fs $network $remote_fs
# Required-Stop:     $local_fs $network $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Launch shell scripts in tmux sessions as a local user
# Description:       Starts named tmux sessions for each managed script.
#                    Configuration is read from /etc/default/tmux-sessions.
### END INIT INFO

set -e

TMUX="/usr/bin/tmux"
SU="/bin/su"
DEFAULTS="/etc/default/tmux-sessions"

# Load externalized configuration
if [ ! -f "$DEFAULTS" ]; then
    echo "Missing configuration file: $DEFAULTS" >&2
    exit 1
fi
. "$DEFAULTS"

# Validate required settings
if [ -z "$DAEMON_USER" ]; then
    echo "DAEMON_USER is not set in $DEFAULTS" >&2
    exit 1
fi
if [ -z "$SESSIONS" ]; then
    echo "SESSIONS is not set in $DEFAULTS" >&2
    exit 1
fi

run_as_user() {
    $SU - "$DAEMON_USER" -s /bin/sh -c "$1"
}

session_exists() {
    run_as_user "$TMUX has-session -t '$1' 2>/dev/null"
}

start_session() {
    session="$1"
    script="$2"
    if session_exists "$session"; then
        echo "  Session '$session' already running, skipping."
    else
        run_as_user "$TMUX new-session -d -s '$session' '$script'"
        echo "  Started session '$session' -> $script"
    fi
}

stop_session() {
    session="$1"
    if session_exists "$session"; then
        run_as_user "$TMUX kill-session -t '$session'"
        echo "  Stopped session '$session'."
    else
        echo "  Session '$session' not running."
    fi
}

case "$1" in
    start)
        echo "Starting tmux sessions as $DAEMON_USER..."
        for pair in $SESSIONS; do
            name="${pair%%:*}"
            path="${pair#*:}"
            start_session "$name" "$path"
        done
        echo "Done."
        ;;

    stop)
        echo "Stopping tmux sessions..."
        for pair in $SESSIONS; do
            name="${pair%%:*}"
            stop_session "$name"
        done
        echo "Done."
        ;;

    restart)
        "$0" stop
        sleep 1
        "$0" start
        ;;

    status)
        echo "tmux sessions for $DAEMON_USER:"
        run_as_user "$TMUX list-sessions 2>/dev/null || echo '  No sessions running.'"
        ;;

    *)
        echo "Usage: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac

exit 0

Set Permissions and Register at Boot

Set ownership of the defaults file and make the init script executable, then register it with update-rc.d:

sudo chmod 644 /etc/default/tmux-sessions
sudo chmod 755 /etc/init.d/tmux-sessions
sudo update-rc.d tmux-sessions defaults

defaults places symlinks in the standard runlevels (2–5 for start, 0/1/6 for stop) matching the Default-Start and Default-Stop values in the LSB header.

To verify the symlinks were created:

ls /etc/rc*.d/*tmux-sessions

Manual Control

# Start all sessions now
sudo service tmux-sessions start

# Stop all sessions
sudo service tmux-sessions stop

# Restart (stop then start)
sudo service tmux-sessions restart

# Check which sessions are alive
sudo service tmux-sessions status

Attaching to a Running Session

Log in or su to the configured DAEMON_USER, then attach by the session name defined in SESSIONS:

su - appuser
tmux attach -t monitor   # use whatever name you set in /etc/default/tmux-sessions

Detach without killing the session with Ctrl-b d.


Removing the Service

To stop the service and remove it from boot:

sudo service tmux-sessions stop
sudo update-rc.d tmux-sessions remove
sudo rm /etc/init.d/tmux-sessions
sudo rm /etc/default/tmux-sessions

Notes

  • The XDG_RUNTIME_DIR and DBUS_SESSION_BUS_ADDRESS variables are not set in this non-login context. If your scripts depend on them, set them explicitly inside the scripts themselves.
  • tmux requires a writable socket directory. By default it uses /tmp/tmux-<uid>/, which is created automatically on first use.
  • Avoid backgrounding the scripts inside the tmux sessions (&). Let tmux hold them in the foreground so that session lifetime tracks process lifetime.
  • For more complex dependency ordering or parallelism, consider migrating to systemd user services — but for straightforward cases on Debian stable, this SysV approach is simple and reliable.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment