Skip to content

swag: ondemand add per-container stop threshold label - #1169

Open
UrekD wants to merge 1 commit into
linuxserver:swag-ondemandfrom
UrekD:swag-ondemand-stop-threshold
Open

UrekD wants to merge 1 commit into
linuxserver:swag-ondemandfrom
UrekD:swag-ondemand-stop-threshold

Conversation

@UrekD

@UrekD UrekD commented Oct 8, 2026 •

Copy link
Copy Markdown

linuxserver.io


  • I have read the contributing guideline and understand that I have made the correct modifications

Description:

Adds an optional container label, swag_ondemand_stop_threshold=<seconds>, that overrides SWAG_ONDEMAND_STOP_THRESHOLD for that container. Containers without the label keep the global value, so existing setups behave as before. The only visible change is the Started monitoring log line, which now ends with , stop threshold: 600s.

  • The label is parsed with int(), like the env var. A value that doesn't parse, or isn't positive, falls back to the global threshold. The ValueError is caught on purpose: process_containers runs after the cycle's accessed URLs have been consumed, so an uncaught exception there would drop those accesses and skip start/stop for every host, and it would repeat every cycle while the label exists.
  • If the value changes (container recreated with a different label), Updated stop threshold for ... to: 300s is logged once, the same way URL changes are. Nothing is logged per cycle.
  • Stopped ... after Ns of inactivity reports the container's own threshold.
  • Websocket containers are unchanged: inactivity only starts counting once the websocket closes, then the container's threshold applies.
  • README: documents the label, mentions it under SWAG_ONDEMAND_STOP_THRESHOLD, and notes that containers sharing a URL are stopped independently.

Benefits of this PR and context:

Cold-start cost differs a lot between apps (a single-container app wakes in a couple of seconds, a Paperless-ngx stack takes much longer, a music server needs a long window so a listening session doesn't hit a cold start), but one global value has to cover all of them. A label fits the existing label-driven config (swag_ondemand_urls, swag_ondemand_websocket).

How Has This Been Tested?

SWAG 5.8.0-ls488 (amd64), mod side-loaded (DOCKER_MODS_SIDELOAD=true) from this branch's docker build layer, SWAG_ONDEMAND_STOP_THRESHOLD=60, the README log_format, and include /config/nginx/ondemand.conf; in each proxy conf. Five on-demand containers, same run against the current branch head (acdf19e) and this PR:

container label acdf19e this PR
od-short 20 66.6 s 26.3 s
od-long 120 66.0 s 127.6 s
od-default none 66.1 s 66.1 s
od-invalid 5m 66.1 s 66.1 s
od-ws (swag_ondemand_websocket=1) 20 66.6 s 26.5 s

Times are from the last request until the container stopped, polled from the host. For od-ws the clock starts when a websocket held open for 45 s closed, and it was not stopped while the socket was open. The extra ~6 s is pickup latency (up to 1 s log reader sleep plus up to 5 s query cycle) before the mod registers the access. Each app served the loading page while stopped, woke, then served real content.

Also checked:

  • Recreating od-short with the label changed to 45 logged Updated stop threshold for od-short ... to: 45s once.
  • A 5000-digit label (int() raises above 4300 digits) falls back to the global value. Other containers kept starting and stopping normally, and ondemand.log shows no errors.
  • Unit-level checks against ContainerThread with fake containers and a fake clock (threshold boundaries, invalid values, label changed or removed on recreate, shared URLs, websocket timing, no repeated log lines). Not included in this PR.

Source / References:

AI disclosure:

Used Claude and Codex to help with this. Changes built and validated on a local machine.

Add an optional `swag_ondemand_stop_threshold=<seconds>` container label
that overrides SWAG_ONDEMAND_STOP_THRESHOLD for that container. Containers
without the label keep using the global value, so existing setups are
unchanged.

The label is parsed with int() like the env var. Values that don't parse
or aren't positive fall back to the global threshold instead of raising,
since an exception in process_containers would skip starting and stopping
for every host in that cycle. The effective value is logged in the
"Started monitoring" line, and again only if it changes (e.g. the
container is recreated with a different label), so a bad label doesn't
spam the log every query cycle.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

2 participants