# Disaster Recovery: Git + Docs stack Goal: restore `git.frostlabs.me` and the docs sites after losing the VPS. ## What state actually matters | Thing | Where | Rebuildable? | |---|---|---| | Gitea data (repos, DB, config) | `/opt/gitea/gitea/` (SQLite + repos) | **No — back this up** | | Runner registration | `/opt/gitea/runner/.runner` | No (but re-registerable in minutes) | | Built sites | `/srv/www/` | Yes — rebuilt on next push | | Tunnel / nginx / compose config | `/opt/gitea/*.yml`, `.env`, `nginx.conf` | Keep a copy off-box | The docs repos themselves are safe as long as one clone survives (VPS, homelab, laptop). This runbook is about restoring the **server**. ## Backup (do this on a schedule) ```bash # Off-box snapshot of everything stateful on the VPS rsync -aP /opt/gitea/ backup-target:/backups/gitea/ ``` !!! tip A `gitea dump` is the "proper" way, but for a single-user SQLite instance an rsync of `/opt/gitea` while the stack is stopped is simplest and complete. ## Restore onto a fresh VPS 1. Provision the box, install Docker + compose. 2. Recreate `/opt/gitea/` and restore the backup into it. 3. Recreate `.env` with `TUNNEL_TOKEN` and (optionally) `RUNNER_TOKEN`. 4. `mkdir -p /srv/www/docs-public /srv/www/docs-private` 5. `docker compose up -d` 6. In Cloudflare Zero Trust, point the tunnel at the new box (token is the same tunnel, so hostnames just reattach). Confirm `git.frostlabs.me` loads. 7. If the runner didn't reconnect, re-register it (Site Admin → Actions → Runners). 8. Push any repo (or hit **Run workflow**) to rebuild both sites into `/srv/www`. ## Verify - [ ] `git.frostlabs.me` reachable, repos present - [ ] `runbook.frostlabs.me` rebuilt and gated by CF Access - [ ] `docs.frostlabs.me` rebuilt and public - [ ] a test push triggers a green Actions run