Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Deployment

Two deploy paths: infrastructure (NixOS config changes) and application code (per-service repos).

Infrastructure

Pushing to main triggers .github/workflows/deploy.yaml which runs deploy-rs over Tailscale to rebuild NixOS on the target machine.

# manual deploy
nix run 'github:serokell/deploy-rs' -- --remote-build --ssh-user kierank .

Application Code

Each service repo has a minimal workflow calling the reusable .github/workflows/deploy-service.yml. On push to main:

  1. Connects to Tailscale (tag:deploy)
  2. SSHes as the service user (e.g., cachet@terebithia) via Tailscale SSH
  3. Snapshots the SQLite DB (if db_path is provided)
  4. git pull + bun install --frozen-lockfile + sudo systemctl restart
  5. Health check (HTTP URL or systemd status fallback)
  6. Auto-rollback on failure (restores DB snapshot + reverts to previous commit)

Per-app workflow — copy and change the with: values:

name: Deploy
on:
  push:
    branches: [main]
  workflow_dispatch:
jobs:
  deploy:
    uses: taciturnaxolotl/dots/.github/workflows/deploy-service.yml@main
    with:
      service: cachet
      health_url: https://cachet.dunkirk.sh/health
      db_path: /var/lib/cachet/data/cachet.db
    secrets:
      TS_OAUTH_CLIENT_ID: ${{ secrets.TS_OAUTH_CLIENT_ID }}
      TS_OAUTH_SECRET: ${{ secrets.TS_OAUTH_SECRET }}

Omit health_url to fall back to systemctl is-active. Omit db_path for stateless services.

mkService

modules/lib/mkService.nix standardizes service modules. A call to mkService { ... } provides:

  • Systemd service with initial git clone (subsequent deploys via GitHub Actions)
  • Caddy reverse proxy with TLS via Cloudflare DNS and optional rate limiting
  • Data declarations (sqlite, postgres, files) that feed into automatic backups
  • Dedicated system user with sudo for restart/stop/start (enables per-user Tailscale ACLs)
  • Port conflict detection, security hardening, agenix secrets

Adding a new service

  1. Create a module in modules/nixos/services/
  2. Enable it in machines/terebithia/default.nix
  3. Add a deploy workflow to the app repo

See modules/nixos/services/cachet.nix for a minimal example.

Machine health checks

Machines with Tailscale enabled automatically expose their hostname for reachability checks in the services manifest via atelier.machine.tailscaleHost. This defaults to networking.hostName when services.tailscale.enable is true.