Berth

How it works

 GitHub ──webhook──▶ Cloudflare ──tunnel──▶ cloudflared ─┬─▶ berthd 127.0.0.1:7777   (hooks.<domain>)
 visitor ───────────▶   edge                             └─▶ Caddy  127.0.0.1:7780   (*.<domain>)
 LAN visitor ─────────────────────────────────────────────▶ Caddy  :443, :80 → 443   (*.<mac>.local)
                                                                 │
 berth CLI ──unix socket──▶ berthd ──container build/run──▶ app VMs on 127.0.0.1:8001–8999
                                   └──Caddy admin API             │ berth network
                                                                  ▼
                                               pg-<name>, redis-<name> VMs on the network gateway
  • berthd is the brain. It takes webhooks, builds with container build, runs each release in its own VM, health-checks it, and switches traffic by updating a DNS SRV record that Caddy re-reads every second (so a deploy never reloads Caddy). Then it watches everything. It's the only thing that writes to ~/.berth.
  • Caddy routes each hostname to the right app, does HTTPS on the LAN, compresses responses, writes access logs and enforces basic auth.
  • cloudflared keeps an outbound connection to Cloudflare, so the Mac needs no open ports. HTTPS for public URLs ends at Cloudflare's edge.
  • Every app and database is its own lightweight VM with its own Linux kernel, so a compromised app can't get at the others or at macOS.

All the state is plain files you can read:

~/.berth/
  config.json              domain, ports, defaults
  apps/<app>/app.json      repo, branch, mounts, links, domains
  apps/<app>/env           live config (mode 0600)
  apps/<app>/releases/     one JSON file per release
  apps/<app>/logs/         app.log, build-<n>.log, release-<n>.log, cron.log, access.<created>.log
  apps/<app>/cron.json     each scheduled job's last run
  services/<kind>/<name>/  database settings
  backups/                 database dumps and SQLite backups
  data/apps/<app>/         storage:mount folders
  secrets/                 tokens and tunnel credentials (mode 0700)
  logs/                    berthd, Caddy and cloudflared logs
  offsite.json             the last off-Mac backup (its settings are in secrets/)
  events.log               the event log

The full design, including what changed along the way, is in design.md.

Edit this page on GitHub (it's a section of the README)