Skip to content

Homelab / single-box operations

ShoreGuard's smallest supported deployment is one box, one gateway: a DGX Spark (or any Linux machine) running an OpenShell gateway with ShoreGuard alongside it. This page covers installing, backing up, and monitoring that shape. For the first-run walkthrough see Solo Dev.

arm64 / DGX Spark

Every NVIDIA GB10 device — the DGX Spark Founders Edition and all OEM variants (ASUS Ascent GX10, HP ZGX Nano, Lenovo ThinkStation PGX, …) — is aarch64. ShoreGuard supports this natively:

  • The release container images are multi-arch (linux/amd64 + linux/arm64).
  • The unit test suite runs on a native arm64 runner in CI on every push, not just cross-built.
  • ShoreGuard is pure Python; the only binary dependencies (grpcio, SQLAlchemy drivers) publish aarch64 wheels.

pip install shoreguard on DGX OS therefore works out of the box.

Option A — Docker Compose (SQLite, one container)

cd deploy
SHOREGUARD_SECRET_KEY=$(openssl rand -hex 32) \
SHOREGUARD_ADMIN_PASSWORD=pick-a-password \
docker compose -f docker-compose.homelab.yml up -d

This runs ShoreGuard with its default SQLite database (WAL mode) inside a named volume — no PostgreSQL. The port is published on 127.0.0.1 only; see Remote access below before exposing anything.

SQLite is a supported production shape for a single replica. The startup config check flags it as a WARN (informational); it only becomes an ERROR when SHOREGUARD_REPLICAS > 1, because concurrent replicas must not share a SQLite file.

Option B — bare metal with systemd

A hardened unit file ships in deploy/systemd/shoreguard.service:

sudo useradd --system --home-dir /var/lib/shoreguard --shell /usr/sbin/nologin shoreguard
sudo python3 -m venv /opt/shoreguard
sudo /opt/shoreguard/bin/pip install shoreguard
sudo cp deploy/systemd/shoreguard.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now shoreguard

Configuration lives in /etc/shoreguard/env (SHOREGUARD_* variables); state — the SQLite DB and the on-disk secret key — lives in /var/lib/shoreguard (the unit sets XDG_CONFIG_HOME there).

For shoreguard --local (Docker lifecycle management of local gateways) add SupplementaryGroups=docker to the unit.

Backup & restore

All management-plane state is in one directory (the gateways and sandboxes themselves live on the OpenShell gateway, not in ShoreGuard):

Install State directory
Docker Compose (homelab) volume sgdata/home/shoreguard/.config/shoreguard
systemd /var/lib/shoreguard/shoreguard
pip / dev ~/.config/shoreguard

Backup (SQLite) is built in — one archive with a live, consistent DB snapshot, the .secret_key, and the VAPID key:

shoreguard backup create                      # → <config-dir>/backups/
shoreguard backup create -o /mnt/nas --keep 7 # custom dir + rotation

Admins can also click Download backup on the Security Check page (GET /api/system/backup), and SHOREGUARD_BACKUP_ENABLED=true turns on a periodic snapshot task (SHOREGUARD_BACKUP_INTERVAL_HOURS, SHOREGUARD_BACKUP_DIR, SHOREGUARD_BACKUP_KEEP). The archive contains the secret-key material — store it like a credential.

Restore: stop ShoreGuard, then:

shoreguard backup restore /mnt/nas/shoreguard-backup-20260611-070000.tar.gz

Replaced files are kept as *.pre-restore. Migrations run automatically on startup, so restoring an older backup into a newer ShoreGuard works. PostgreSQL deployments should use pg_dump/pg_restore instead.

Health monitoring

  • GET /healthz — liveness (the process is up).
  • GET /readyz — readiness, including the background-task supervisor snapshot (gateway health monitor, cleanup, …).
  • GET /metrics — Prometheus metrics (admin-authenticated by default; SHOREGUARD_AUTH_METRICS_PUBLIC=true to open it to a local scraper).

A minimal external watchdog is one line of cron on another machine:

*/5 * * * * curl -fsS --max-time 5 http://spark:8888/healthz > /dev/null || ntfy pub alerts "ShoreGuard down"

ShoreGuard itself watches the gateways (the health_monitor background task) and fires gateway.unreachable / gateway.recovered webhook events on state transitions — point a webhook at ntfy or Telegram to get gateway outages on your phone.

Remote access

Do not publish the ShoreGuard port to the internet, and avoid binding it to the LAN unauthenticated. The recommended remote-access path is Tailscale:

tailscale serve --bg 8888

ShoreGuard stays bound to loopback; Tailscale terminates TLS and makes the UI reachable from every device in your tailnet (including your phone) under the machine's tailnet name. See Tailscale access for the full recipe, including automatic login via tailnet identity headers (SHOREGUARD_TAILSCALE_IDENTITY) and the topbar QR button for phones.

Passkeys & push on the phone

Once the UI is reachable over HTTPS (Tailscale gives you that for free), two things make the phone experience first-class:

  • Passkeys — on /profile, register a passkey; from then on the login page offers "Sign in with a passkey" and the phone's screen lock signs you in. No password manager gymnastics on mobile. Enabled by default (SHOREGUARD_PASSKEYS_ENABLED); pin the relying-party ID with SHOREGUARD_PASSKEY_RP_ID if you serve under multiple names.
  • Web Push — enable notifications via the QR/phone dialog in the top bar, then create a webpush webhook choosing which events reach the device. No ntfy/Telegram account needed; payloads are end-to-end encrypted. See the webhooks guide.