DocsMore

Running the hub on a Mac (launchd)

View source on GitHubdocs/deploy-macos.md

On this page

For a Mac that should run the hub all the time. It builds the single-file hub executable (web UI inside), installs it under one folder, runs it as a launchd service, and backs up its databases every night. Nothing here is specific to one site: a site's names, ports and paths live in an env file you keep outside the repository.

Deploy#

./deploy-dev.sh --dry-run     # print every step, change nothing
./deploy-dev.sh               # build, install, load the services, health-check
./deploy-dev.sh --restart-only
./deploy-dev.sh --rollback    # put the previous binary back

deploy-prod.sh is the same script for a second instance (prod.env). Both call deploy/macos/hub-deploy.sh ENV. Needs bun, curl and sqlite3 (the last two ship with macOS) on the Mac doing the deploy.

The instance env file#

hub-deploy.sh sources ENV.env from $YARRIX_INSTANCE_DIR, else private/instance/ in the checkout (or in the main checkout when you run from a git worktree). YARRIX_INSTANCE_ENV names a file directly. Keep it out of version control.

LABEL=io.example.yarrix           # launchd label; the backup job is "$LABEL.backup"
PREFIX=$HOME/yarrix               # bin/ config/ data/ logs/ backups/ live here
APP_PORT=7791                     # loopback port the hub listens on (not the 7790 default if that is taken)
PUBLIC_URL=https://hub.example.ts.net:7790
TAILNET_HOST=hub.example.ts.net   # optional: enables the https health check and the serve check
TAILNET_PORT=7790
REQUIRED_HOST=hub                 # optional: refuse to deploy anywhere else
BACKUP_KEEP_DAYS=14               # optional
BACKUP_HOUR=3                     # optional, local time; the job runs at HH:15
DC_LIB=/path/to/deploy-common.sh  # optional: a shared deploy library for the lock, restart and health helpers

No secrets go in this file or in the plist. The hub reads them through ${env:NAME} / ${file:path} references in its config (configuration.md).

On first start the hub writes a starter yarrix.yaml into $PREFIX/config and logs the first-run setup address (in $PREFIX/logs/hub.log) that creates the first admin when you enter it on the hub's /setup page.

HTTPS with Tailscale#

The hub listens on loopback only. tailscale serve puts HTTPS (a real certificate) in front of it, reachable only from your tailnet:

tailscale serve --bg --https=7790 http://127.0.0.1:7791     # public port 7790 -> hub on 7791
tailscale serve status

Set PUBLIC_URL to https://<machine>.<tailnet>.ts.net:7790 so setup and sign-in links carry that address. To sign people in with their tailnet identity, add { type: tailscale, mode: headers } to auth.providers (auth.md): the hub trusts the Tailscale-User-Login header only from loopback, which is where tailscale serve connects from. The deploy script checks that the serve door exists and prints this command when it does not; it never runs it for you.

Backups#

deploy/macos/backup-hub-data.sh DATA_DIR BACKUP_DIR [CONFIG_DIR] copies every *.db in the data directory with SQLite's online backup (consistent while the hub runs), integrity-checks each copy, copies the config directory, and prunes folders older than YARRIX_BACKUP_KEEP_DAYS. A deploy installs it with a launchd job ($LABEL.backup) that runs daily. Run it by hand any time:

$PREFIX/bin/backup-hub-data.sh $PREFIX/data $PREFIX/backups $PREFIX/config

To restore: stop the hub (launchctl bootout gui/$(id -u)/$LABEL), copy the .db files from a hub-<timestamp> folder over the same names in $PREFIX/data, delete any *.db-wal and *.db-shm beside them, start the hub (launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/$LABEL.plist). Copy the backups folder to another machine as well: a backup on the same disk does not survive the disk.

This is separate from the hub's own self_monitor.db_backup (a VACUUM INTO copy it makes and checks itself); the nightly job is the one that also covers the config directory and is driven from outside the hub process.