Streaming stack is on-demand, not always-on
Measured rather than assumed: the compositor and Sunshine together hold 220 MiB on the 3060 — sway ~112, Sunshine ~108. That card also runs the local LLM, and with an 11.3 GB model resident on a 12 GB card the free headroom goes from 992 MiB to 772. Losing a fifth of what is left matters for KV cache growth, so leaving it running was the wrong default. sway-headless and sunshine no longer start at boot. `stream on` brings them up, `stream status` reports state and VRAM, and sunshine-idle-stop.timer checks every five minutes whether any Moonlight client still holds a connection to 47984/47989/48010 — stopping the stack after 15 idle minutes so a forgotten session does not sit on memory the inference stack wants. Note for future edits: `systemctl --user disable` on a unit dotbot has symlinked into ~/.config/systemd/user deletes the symlink itself, because systemd treats it as a linked unit. Re-run ./install after disabling one.
This commit is contained in:
1 parent
d5cb5f5690
commit
de78118624
5 files changed
+88
-4
No files matched your search
Executable
+63
@@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Start and stop the Moonlight streaming stack on demand.
|
||||||
|
#
|
||||||
|
# It is not left running because it costs ~220 MiB of VRAM on the 3060 (sway
|
||||||
|
# ~112, Sunshine ~108) and that card also holds the local LLM. With an 11.3 GB
|
||||||
|
# model resident on a 12 GB card, 220 MiB is roughly a fifth of the remaining
|
||||||
|
# headroom — enough to matter for KV cache growth.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
UNIT=sway-headless.service
|
||||||
|
IDLE_MINUTES="${STREAM_IDLE_MINUTES:-15}"
|
||||||
|
STAMP="${XDG_RUNTIME_DIR:-/tmp}/stream.lastseen"
|
||||||
|
|
||||||
|
# A Moonlight client holds connections to the control and RTSP ports.
|
||||||
|
clients_connected() {
|
||||||
|
ss -Htn state established '( sport = :47984 or sport = :47989 or sport = :48010 )' 2>/dev/null | grep -q .
|
||||||
|
}
|
||||||
|
|
||||||
|
vram() {
|
||||||
|
local used total
|
||||||
|
read -r used total < <(nvidia-smi --query-gpu=memory.used,memory.total \
|
||||||
|
--format=csv,noheader,nounits 2>/dev/null | tr -d ',')
|
||||||
|
printf '%s MiB used of %s, %s free' "$used" "$total" "$((total - used))"
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-status}" in
|
||||||
|
on|start)
|
||||||
|
systemctl --user start "$UNIT"
|
||||||
|
sleep 6
|
||||||
|
date +%s > "$STAMP"
|
||||||
|
printf 'streaming up — pair or connect at https://%s:47990\n' "$(uname -n)"
|
||||||
|
printf 'GPU: %s\n' "$(vram)"
|
||||||
|
;;
|
||||||
|
off|stop)
|
||||||
|
systemctl --user stop "$UNIT"
|
||||||
|
sleep 3
|
||||||
|
rm -f "$STAMP"
|
||||||
|
printf 'streaming down\nGPU: %s\n' "$(vram)"
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
printf 'compositor: %s\nsunshine: %s\nclients: %s\nGPU: %s\n' \
|
||||||
|
"$(systemctl --user is-active "$UNIT")" \
|
||||||
|
"$(systemctl --user is-active sunshine.service)" \
|
||||||
|
"$(clients_connected && echo connected || echo none)" \
|
||||||
|
"$(vram)"
|
||||||
|
;;
|
||||||
|
--idle-check)
|
||||||
|
# Called from sunshine-idle-stop.timer. Releases the GPU when nobody has
|
||||||
|
# been connected for IDLE_MINUTES, so a forgotten session does not sit on
|
||||||
|
# VRAM the inference stack wants.
|
||||||
|
systemctl --user is-active --quiet "$UNIT" || exit 0
|
||||||
|
if clients_connected; then date +%s > "$STAMP"; exit 0; fi
|
||||||
|
last=$(cat "$STAMP" 2>/dev/null || echo 0)
|
||||||
|
if (( last == 0 )); then date +%s > "$STAMP"; exit 0; fi
|
||||||
|
if (( ($(date +%s) - last) >= IDLE_MINUTES * 60 )); then
|
||||||
|
echo "no Moonlight client for ${IDLE_MINUTES}m; stopping to free the GPU"
|
||||||
|
systemctl --user stop "$UNIT"
|
||||||
|
rm -f "$STAMP"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "usage: stream [on|off|status]" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Release the GPU when no Moonlight client is connected
|
||||||
|
ConditionHost=mainframe
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=%h/.dotfiles/hosts/mainframe/bin/stream --idle-check
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Check every 5 minutes whether the streaming stack can be shut down
|
||||||
|
ConditionHost=mainframe
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnBootSec=5min
|
||||||
|
OnUnitActiveSec=5min
|
||||||
|
AccuracySec=30s
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
@@ -18,5 +18,6 @@ ExecStopPost=/usr/bin/systemctl --user stop sunshine.service
|
|||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
RestartSec=5s
|
RestartSec=5s
|
||||||
|
|
||||||
[Install]
|
# Deliberately NOT WantedBy=default.target. The 3060 also holds the local LLM,
|
||||||
WantedBy=default.target
|
# and this stack costs ~220 MiB of VRAM. Start it with `stream on`; the
|
||||||
|
# idle-stop timer puts it away again.
|
||||||
@@ -12,6 +12,8 @@
|
|||||||
- link:
|
- link:
|
||||||
~/.config/sway/headless.conf: hosts/mainframe/sway/headless.conf
|
~/.config/sway/headless.conf: hosts/mainframe/sway/headless.conf
|
||||||
~/.config/systemd/user/sway-headless.service: hosts/mainframe/systemd/sway-headless.service
|
~/.config/systemd/user/sway-headless.service: hosts/mainframe/systemd/sway-headless.service
|
||||||
|
~/.config/systemd/user/sunshine-idle-stop.service: hosts/mainframe/systemd/sunshine-idle-stop.service
|
||||||
|
~/.config/systemd/user/sunshine-idle-stop.timer: hosts/mainframe/systemd/sunshine-idle-stop.timer
|
||||||
~/.config/systemd/user/sunshine.service: hosts/mainframe/systemd/sunshine.service
|
~/.config/systemd/user/sunshine.service: hosts/mainframe/systemd/sunshine.service
|
||||||
~/.config/inbox-tidy: hosts/mainframe/config/inbox-tidy
|
~/.config/inbox-tidy: hosts/mainframe/config/inbox-tidy
|
||||||
~/.config/llmfit: hosts/mainframe/config/llmfit
|
~/.config/llmfit: hosts/mainframe/config/llmfit
|
||||||
@@ -41,5 +43,5 @@
|
|||||||
description: Reloading user units
|
description: Reloading user units
|
||||||
- command: systemctl --user enable --now inbox-tidy.timer movie-recs.timer rent-utilities.timer ddns.timer
|
- command: systemctl --user enable --now inbox-tidy.timer movie-recs.timer rent-utilities.timer ddns.timer
|
||||||
description: Enabling the singleton timers (mainframe only)
|
description: Enabling the singleton timers (mainframe only)
|
||||||
- command: systemctl --user enable sway-headless.service sunshine.service
|
- command: systemctl --user enable --now sunshine-idle-stop.timer
|
||||||
description: Enabling the headless compositor and Sunshine (not started here — it takes the GPU)
|
description: Enabling the streaming idle-stop watchdog (the stack itself starts on demand via `stream on`)
|
||||||
Reference in new issue
Block a user