No description
The two recent outages had two distinct root causes, both stemming from `--restart=unless-stopped` on the three containers: 1. rm/run race: container metadata under /var/lib/docker/containers persists across reboot. The daemon restores it asynchronously after docker.service reports active, so the service's ExecStartPre rm -f ran in a window where the containers were invisible to the daemon (no-op), then `docker run --name caddy` later collided with the just-restored container. 2. Bind-mount-before-mount race: docker's auto-restart of the previously running containers happened *before* the mount unit activated /mnt/disks/forgejo-data. The forgejo container ended up with `/data` bind-mounted to the empty underlying mount-point directory on the root fs, shadowed once the disk finally mounted -- presenting the fresh-install setup screen despite the real DB sitting untouched on /dev/sdb. Drop --restart=unless-stopped and let systemd be the single owner of the container lifecycle. Add --rm so containers don't persist on disk after exit. Replace docker's mid-day auto-restart with a 1-minute watchdog timer that restarts forgejo-stack.service if any container goes missing. Add OnFailure=forgejo-stack-recover.service so a boot-time failure self-heals after a 60s delay instead of waiting for me to notice. Note: /etc on COS is overlayfs with upperdir on tmpfs, so the May 9 patch never took effect on reboot -- /etc/systemd/system/* is wiped each boot and cloud-init re-renders from the metadata user-data. The only durable path to changing units on this VM is terraform apply. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| cloud-init | ||
| config | ||
| docs | ||
| scripts | ||
| terraform | ||
| .gitignore | ||
| plan.md | ||
| README.md | ||
forge
Self-hosted Forgejo on GCP. e2-micro VM on Container-Optimized OS, Caddy for HTTPS, IAP for admin SSH, nightly backups to GCS. Targets ~$3–5/month.
See plan.md for the full design rationale.
Quick start
# 1. Set the active project, enable APIs
gcloud config set project YOUR_PROJECT
gcloud services enable compute.googleapis.com secretmanager.googleapis.com \
iap.googleapis.com storage.googleapis.com
# 2. Generate the Forgejo secrets in Secret Manager (one-time)
./scripts/bootstrap-secrets.sh
# 3. Configure Terraform (terraform.tfvars is gitignored)
$EDITOR terraform/terraform.tfvars
# 4. Apply
cd terraform
terraform init
terraform apply
Point your domain's A record at the static_ip output, then visit https://<your-domain> to run the Forgejo installer.
Day two
- docs/runbook.md — admin SSH, container ops, backups, upgrades
- docs/disaster-recovery.md — recovery scenarios
- plan.md — full design, cost breakdown, security checklist
Layout
terraform/ GCP infrastructure (VM, network, IAM, GCS, optional DNS)
cloud-init/ user-data.yaml.tpl — systemd units that boot Forgejo + Caddy + Watchtower
config/ Caddyfile template (reference; the live copy is embedded in cloud-init)
scripts/ bootstrap-secrets.sh, backup.sh, restore.sh, test-restore.sh
docs/ runbook + disaster recovery