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.
Install tmux if it is not already present:
sudo apt-get install tmuxDecide 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 appuserAssume 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.shCreate /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.
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"Create /etc/init.d/tmux-sessions:
sudo nano /etc/init.d/tmux-sessionsPaste 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 0Set 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 defaultsdefaults 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# 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 statusLog 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-sessionsDetach without killing the session with Ctrl-b d.
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- The
XDG_RUNTIME_DIRandDBUS_SESSION_BUS_ADDRESSvariables 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
systemduser services — but for straightforward cases on Debian stable, this SysV approach is simple and reliable.