Systemd Units

All Shiloh Broadcaster services run as user-mode systemd units. No sudo is required for day-to-day operation.

Unit files

Units are installed to ~/.config/systemd/user/ by the install script:

~/.config/systemd/user/shiloh-mixer.service
~/.config/systemd/user/shiloh-web-relay.service
~/.config/systemd/user/mixer_web.service
~/.config/systemd/user/mixer_web.service.d/secret.conf   # SECRET_KEY_BASE drop-in

Enable auto-start (survive login/logout)

By default, user systemd units only run while the user is logged in. To keep services running after you log out (required for a server):

# Allow the user's systemd session to persist without an active login
loginctl enable-linger $USER

# Enable the services to start at boot
systemctl --user enable shiloh-mixer shiloh-web-relay mixer_web

# Verify
loginctl show-user $USER | grep Linger
# → Linger=yes

Starting and stopping

# Start all at once
systemctl --user start shiloh-mixer shiloh-web-relay mixer_web

# Stop individual service
systemctl --user stop shiloh-web-relay

# Restart single service (safest during updates)
systemctl --user restart shiloh-mixer

# Check status
systemctl --user status shiloh-mixer

Dependency ordering

The units have explicit ordering:

PipeWire / WirePlumber (system audio stack)
    └── shiloh-mixer  (After= pipewire.service wireplumber.service)
            └── shiloh-web-relay  (After= shiloh-mixer.service)
            └── mixer_web  (After= shiloh-mixer.service, but PartOf= is intentionally absent
                            so the UI stays up across mixer restarts)

mixer_web is intentionally not a PartOf shiloh-mixer. The Phoenix LiveView UI reconnects automatically when the mixer comes back — keeping the UI alive lets operators see what is happening during a mixer restart.

Unit details

shiloh-mixer.service

[Unit]
Description=shiloh-mixer — Rust JACK mix bus + UDP broadcaster relay
After=pipewire.service wireplumber.service
Wants=pipewire.service wireplumber.service

[Service]
Type=simple
WorkingDirectory=%h/mixer
# pw-jack wraps the mixer so it uses PipeWire's JACK shim instead of native libjack.
# This makes PipeWire clients (Firefox, Desktop Audio) visible as JACK ports.
ExecStart=/usr/bin/pw-jack %h/mixer/shiloh-mixer-linux-x86_64 --config %h/mixer/shiloh-mixer.toml
Restart=on-failure
RestartSec=3
LimitRTPRIO=95
LimitMEMLOCK=infinity
Environment=XDG_RUNTIME_DIR=/run/user/1000
Environment=RUST_LOG=info
Environment=RUST_BACKTRACE=1

[Install]
WantedBy=default.target

Key points:

  • pw-jack wrapper — The mixer runs under PipeWire’s JACK compatibility layer. This makes all PipeWire clients (Firefox, Desktop Audio sink, etc.) visible as JACK ports. Without pw-jack, the mixer uses native libjack and cannot see PipeWire-only clients.
  • LimitRTPRIO=95 and LimitMEMLOCK=infinity allow the JACK RT callback thread to run at realtime priority. Without these, JACK will refuse to enable RT scheduling and xrun rates rise dramatically under load.
  • XDG_RUNTIME_DIR must be set explicitly because linger sessions don’t always inherit it from PAM. The install script hard-codes your UID — verify it matches id -u.
  • Restart=on-failure with RestartSec=3 means the mixer auto-recovers from crashes. Broadcaster and relay clients reconnect automatically.

mixer_web.service

[Unit]
Description=mixer_web — Phoenix LiveView mixer UI (port 8889)
After=network-online.target shiloh-mixer.service
Wants=network-online.target

[Service]
Type=simple
Environment=PHX_SERVER=true
Environment=PHX_HOST=stg-srv001.bq.shilohbv.com
Environment=PORT=8889
ExecStart=/home/shiloh/mixer_web/bin/mixer_web start
ExecStop=/home/shiloh/mixer_web/bin/mixer_web stop
Restart=on-failure
RestartSec=3
SuccessExitStatus=143
TimeoutStopSec=15
  • SuccessExitStatus=143 maps SIGTERM (exit 143) to “clean shutdown” so Restart=on-failure does not re-trigger on graceful stop.
  • TimeoutStopSec=15 gives the Erlang VM 15 seconds to drain active WebSocket connections before systemd kills it.
  • SECRET_KEY_BASE is in the drop-in mixer_web.service.d/secret.conf — not the main unit — so the unit file can be version-controlled without exposing secrets.

Editing a unit

# Open a drop-in (preferred — survives reinstall)
systemctl --user edit shiloh-mixer

# Or edit the file directly
nano ~/.config/systemd/user/shiloh-mixer.service

# Always reload after editing
systemctl --user daemon-reload
systemctl --user restart shiloh-mixer

Socket activation

Socket activation is not currently used. The mixer and web relay bind their own UDP/TCP sockets at startup. If you need to start the mixer on-demand (e.g. for a low-power server that should sleep), socket activation could be added for TCP 8889, but UDP socket activation is awkward — the mixer opens UDP 5005 itself and must be running continuously for relay reconnects to work.

Checking all Shiloh units at once

systemctl --user list-units 'shiloh-*' 'mixer_web*'