Weekly offline snapshots for netcup vServers via the SCP REST API and GitHub Actions.
Every Monday 04:00 UTC, per server in SERVER_IDS, in parallel:
- Skip if today’s
YYYYMMDDsnapshot exists — no downtime. - Resolve disk (first only; warns on multi-disk), read power state.
- Gracefully stop (ACPI
OFF) if running — offline snapshots needSHUTOFF. - Prune our dated snapshots to the newest
SNAPSHOT_KEEP − 1(the new one makesKEEP). - Dry-run: possible → go; out of space → run storage optimization if enabled (deletes all snapshots; data/OS safe), else fail; blocked → fail; inconclusive → try anyway.
- Create snapshot
YYYYMMDD. - Restart — only if it was running at the start.
An EXIT trap (with TERM/INT routed in) always attempts a restart, even on failure/cancel/timeout. Per-server concurrency prevents overlapping runs. State-changing writes retry through netcup’s transient 409/503 locks until they serialize or LOCK_RETRY_DEADLINE.
Secrets (Settings → Secrets and variables → Actions): SCP_USER, SCP_PASS — your SCP username and password. The workflow sends them as an OAuth2 password grant (client_id=scp, grant_type=password) to the SCP Keycloak token endpoint and uses the returned bearer token. No API key. (This is distinct from the CCP auto-login you use in a browser.)
Variable — one required: SERVER_IDS, a JSON array of strings like ["123456", "789012"] (ID is in the SCP URL).
Schedule (optional): edit the cron, default 0 4 * * 1. Also runs manually from the Actions tab.
Create these repo Variables only to override the defaults already in the workflow:
| Variable | Default | Effect |
|---|---|---|
SNAPSHOT_KEEP |
3 |
Dated snapshots to retain (count-based) |
ENABLE_STORAGE_OPTIMIZATION |
true |
Allow the destructive out-of-space fallback |
Everything else is a readonly constant in the workflow: POLL_INTERVAL 5 s, POLL_ATTEMPTS 12 (~60 s), SNAPSHOT_POLL_ATTEMPTS 24 (~120 s), OPT_POLL_ATTEMPTS 240 (~20 min ceiling), RESTART_MAX_ATTEMPTS 3, LOCK_RETRY_INTERVAL 10 s, LOCK_RETRY_DEADLINE 180 s, TOKEN_REFRESH_SECS 240 s. Worst case ≈ the 20 min optimization ceiling → timeout-minutes: 30; raise both together.
- Always restarts, even on failure/cancel/timeout; a failed restart fails the job (red, not silent green).
- No needless downtime — skips if today’s snapshot exists; never powers on a server that was off.
- Cleanup keeps the newest
SNAPSHOT_KEEPdated (YYYYMMDD/YYYY-MM-DD) snapshots bycreationTime; manual snapshots untouched. - Destructive fallback is opt-out — optimization wipes all snapshots, only on out-of-space +
ENABLE_STORAGE_OPTIMIZATION≠false. - Hardened — encrypted secrets, masked tokens, URL-encoded auth, minimal logging,
permissions: {}, no checkout. Only idempotentGETs retry at the transport layer.
MIT