Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions apps/loopover-ui/content/docs/self-hosting-operations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -444,13 +444,18 @@ less than 7 days is left alone, so a recent deploy is never at risk):

<CodeBlock
lang="bash"
code={`sudo cp systemd/loopover-docker-prune.service.example /etc/systemd/system/loopover-docker-prune.service
sudo cp systemd/loopover-docker-prune.timer.example /etc/systemd/system/loopover-docker-prune.timer
sudo $EDITOR /etc/systemd/system/loopover-docker-prune.service # set WorkingDirectory / ExecStart to your path
code={`sudo cp systemd/loopover-docker-prune@.service.example /etc/systemd/system/loopover-docker-prune@.service
sudo cp systemd/loopover-docker-prune@.timer.example /etc/systemd/system/loopover-docker-prune@.timer
sudo systemctl daemon-reload
sudo systemctl enable --now loopover-docker-prune.timer`}
INSTANCE=$(systemd-escape -p /opt/loopover) # substitute your loopover install path
sudo systemctl enable --now "loopover-docker-prune@\${INSTANCE}.timer"`}
/>

This is a templated (instantiated) unit — the instance name is your loopover install path,
systemd-escaped, so the same pair of installed unit files can prune Docker resources for more
than one loopover install on a host, or be copied verbatim across a fleet, without editing a
per-install path by hand.

Run it manually at any time with `docker system df` before and after to see what
it reclaimed: `sh scripts/selfhost-docker-prune.sh`.

Expand Down
2 changes: 1 addition & 1 deletion packages/loopover-miner/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ sudo systemctl daemon-reload
sudo systemctl enable --now loopover-miner.service
```

Because `loop` is a **long-running daemon that schedules its own cycles**, it is a persistent `Type=simple` service (with `Restart=on-failure`) — **not** a oneshot unit driven by a `.timer`, unlike the periodic `loopover-docker-prune.*.example` hygiene job in [`systemd/`](../../systemd/). Keep `GITHUB_TOKEN` (and any coding-agent credentials) in a root-owned `0600` `EnvironmentFile`, never in the unit file. Follow the loop with `journalctl -u loopover-miner -f`; `systemctl stop` sends SIGTERM, which the loop handles cleanly at its next kill-switch check.
Because `loop` is a **long-running daemon that schedules its own cycles**, it is a persistent `Type=simple` service (with `Restart=on-failure`) — **not** a oneshot unit driven by a `.timer`, unlike the periodic `loopover-docker-prune@.*.example` hygiene job in [`systemd/`](../../systemd/). Keep `GITHUB_TOKEN` (and any coding-agent credentials) in a root-owned `0600` `EnvironmentFile`, never in the unit file. Follow the loop with `journalctl -u loopover-miner -f`; `systemctl stop` sends SIGTERM, which the loop handles cleanly at its next kill-switch check.

Want the dashboard too? [`systemd/loopover-miner-ui.service.example`](../../systemd/loopover-miner-ui.service.example) is a companion unit that serves `apps/loopover-miner-ui` persistently over the same local state — see that app's [README](../../apps/loopover-miner-ui/README.md#running-as-a-persistent-service).

Expand Down
27 changes: 0 additions & 27 deletions systemd/loopover-docker-prune.service.example

This file was deleted.

13 changes: 0 additions & 13 deletions systemd/loopover-docker-prune.timer.example

This file was deleted.

37 changes: 37 additions & 0 deletions systemd/loopover-docker-prune@.service.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Runs scripts/selfhost-docker-prune.sh on a schedule (paired with loopover-docker-prune@.timer.example) to
# reclaim unused Docker images and build cache before disk fills up on a long-running self-host deployment.
#
# TEMPLATED UNIT (#4894): the `@` in the filename makes this a systemd instantiated unit, so ONE pair of
# installed unit files can prune Docker resources for any number of loopover installs on the same host (or
# be copied verbatim to every host in a fleet) without hand-editing a WorkingDirectory/ExecStart path per
# instance. The instance name (the string after `@` when you enable it) IS the install path, systemd-escaped
# -- `%I` below expands back to the real, unescaped path.
#
# Install (repeat the last two lines once per loopover install path on this host):
# sudo cp systemd/loopover-docker-prune@.service.example /etc/systemd/system/loopover-docker-prune@.service
# sudo cp systemd/loopover-docker-prune@.timer.example /etc/systemd/system/loopover-docker-prune@.timer
# sudo systemctl daemon-reload
# INSTANCE=$(systemd-escape -p /opt/loopover) # -> "opt-loopover"; repeat per install path if more than one
# sudo systemctl enable --now "loopover-docker-prune@${INSTANCE}.timer"
#
# A single-instance host works exactly the same way -- there is no non-templated form anymore; just pick one
# instance name (e.g. `systemd-escape -p /opt/loopover`) and enable that.
#
# This is a HOST-level unit, not a Docker Compose service: reclaiming images/build-cache needs real Docker
# daemon access, which no container in docker-compose.yml is granted (see that file's docker-proxy and
# runner service comments for why raw /var/run/docker.sock exposure into a container is avoided).

[Unit]
Description=LoopOver self-host Docker resource hygiene (image + build-cache prune) for %I
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
# %I is the unescaped instance name -- the loopover install path you passed to `systemd-escape -p`.
WorkingDirectory=%I
ExecStart=/bin/sh %I/scripts/selfhost-docker-prune.sh
# Optional: override the default 7-day (168h) safety window before an unused image/build-cache entry is
# eligible for removal. Per-instance overrides need a matching per-instance drop-in
# (systemctl edit "loopover-docker-prune@<instance>.service") rather than editing this shared template.
# Environment=LOOPOVER_DOCKER_PRUNE_RETAIN_HOURS=168
14 changes: 14 additions & 0 deletions systemd/loopover-docker-prune@.timer.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Pairs with loopover-docker-prune@.service.example. See that file for install instructions -- this is
# also a templated (instantiated) unit, enabled per instance: `loopover-docker-prune@<instance>.timer`.

[Unit]
Description=Run loopover-docker-prune@%i.service daily

[Timer]
OnCalendar=daily
Persistent=true
# Spread the run over a window instead of firing at exactly midnight on every host.
RandomizedDelaySec=30m

[Install]
WantedBy=timers.target
Loading