Ops · GET only
What this is
A systemd user timer on an always-on Linux box that GETs PeerWeave so Render Free does not idle-spin-down the origin.
Render Free web services spin down after 15 minutes with no inbound HTTP. Keep-warm is the workaround. Starter does not idle — that is the lasting fix. A Free instance kept awake uses most of the 750 monthly instance hours.
This is not manticore-net. That project is a local packet sensor for networks you own. Keep-warm does not capture packets and does not send credentials.
Narrative: Keeping PeerWeave awake. lucastilford.com itself is a Render static site (empty build, repo root = publish directory) and does not need this job.
What it hits
Identified User-Agent, no body, no credentials. Default GETs:
https://www.peerweave.com/— visitor landing pagehttps://www.peerweave.com/app— web client-
https://peer-weave.onrender.com/— Render origin (bypasses a future CDN HIT that would skip the process that sleeps)
If the body looks like Render’s wake-up interstitial (SERVICE WAKING UP,
APPLICATION LOADING, and kin), the script retries with exponential backoff until PeerWeave /
Next.js HTML arrives. Per-request ceiling is 90 seconds. Success is HTTP 2xx/3xx.
Hub PC (primary)
GitHub’s schedule cron can delay past 15 minutes. A systemd user timer on an
always-on machine is the reliable path. Linger is enabled when passwordless sudo is available
(loginctl enable-linger), so the timer keeps firing after logout.
chmod +x deploy/keep-warm/*.sh ./deploy/keep-warm/install.sh systemctl --user status peerweave-keep-warm.timer journalctl --user -u peerweave-keep-warm.service -n 50 peerweave-keep-warm --once systemctl --user disable --now peerweave-keep-warm.timer
One-shot / loop without systemd:
./deploy/keep-warm/keep-warm.sh --once ./deploy/keep-warm/keep-warm.sh --loop
Install copies the script to ~/.local/bin/peerweave-keep-warm, units to
~/.config/systemd/user/, and an optional env file to
~/.peerweave/keep-warm.env if missing.
Interval
| Knob | Value | Why |
|---|---|---|
| Timer | 10 minutes + up to 45s jitter | Under the 15-minute idle window, not a hammer |
| Script floor | 5 minutes | --loop / PW_KEEPWARM_INTERVAL_SEC cannot go faster |
| Request timeout | 90 seconds | Covers a cold start if one sneaks through |
| OnBootSec | 45 seconds | First ping shortly after boot / login |
| Persistent | true | Catch a missed tick after downtime |
Environment
Optional file: ~/.peerweave/keep-warm.env (systemd EnvironmentFile: no quotes, no
export).
| Variable | Default | Purpose |
|---|---|---|
PW_KEEPWARM_URLS |
the three URLs above | Space-separated GET targets |
PW_KEEPWARM_INTERVAL_SEC |
600 | Loop sleep only. Floor 300. |
PW_KEEPWARM_UA |
PeerWeave-KeepWarm/1.0 (+https://github.com/Lucastil2212/peer-weave) |
Identified User-Agent |
PW_KEEPWARM_MAX_TIME |
90 | Per-request ceiling (seconds) |
PEERWEAVE_SKIP_SYSTEMD |
0 | Install binary only |
PEERWEAVE_KEEPWARM_ENABLE |
1 | 0 = install units but do not start the timer |
GitHub Actions (backup)
.github/workflows/keep-warm.yml runs every 10 minutes on main and on
workflow_dispatch. It sparse-checkouts deploy/keep-warm and runs
keep-warm.sh --once. Treat it as a spare heart, not the only one. It only helps after that
workflow is on the default branch.
If visitors still see the boot page
- Confirm the live Render service is Free, not Starter.
systemctl --user status peerweave-keep-warm.timer— enabled, next elapse under 15 minutes.journalctl --user -u peerweave-keep-warm.service -n 50— look forOKvsWAKE/FAIL.- Manual:
peerweave-keep-warm --once. - Upgrade the Render web service to Starter if the site should stay up without a ping.