Files
hackerspace.sh/docs/DEPLOYMENT.md
T
Moheeb Zara 7eb83676e7 feat(notifications): P2e member self-view on /me + docs
getMyNotifications (service-client self-view, mirrors getMyBilling) +
a read-only Notifications section on /me. Docs updated in the same
change: ARCHITECTURE (Phase 2 subsystem + limitation #5), API_REFERENCE
(notifications section, fixed the stale no-auth-middleware claim),
DATABASE_SCHEMA + DB_SCHEMA_MAP (migration 041 + table), DEPLOYMENT
(env vars + dispatcher crontab), HANDOFF pass-45.
2026-05-17 04:58:46 -07:00

4.2 KiB

Deployment

The supported production target is a single DigitalOcean Droplet running self-hosted Supabase plus the Next.js app behind Caddy with automatic HTTPS. The end-to-end procedure lives in DEPLOY_DO_SELFHOSTED.md.

At a glance

Component Hosted on
Database (Postgres) Supabase container on the Droplet, data on a persistent block volume
Auth (GoTrue) Supabase container on the Droplet
REST and Realtime Supabase containers on the Droplet
App (Next.js) systemd service on the Droplet, port 3000
Reverse proxy Caddy 2 on the Droplet, ports 80 and 443
Email Resend (SMTP)
Backups Daily pg_dumpall to /mnt/data/backups, 14-day retention
Continuous deployment GitHub Actions runs /opt/hackerspace-ops/deploy.sh over SSH on every push to main

Required environment variables

All variables are listed in .env.example. At minimum:

  • NEXT_PUBLIC_SUPABASE_URL — public Supabase API URL
  • NEXT_PUBLIC_SUPABASE_ANON_KEY — anon JWT, public
  • SUPABASE_SERVICE_ROLE_KEY — service-role JWT, server-side only
  • NEXT_PUBLIC_APP_URL — public URL of the deployed app, used in OAuth redirects

Optional:

  • NEXT_PUBLIC_OAUTH_GITHUB / NEXT_PUBLIC_OAUTH_GOOGLE — set to "true" only for an OAuth provider you have actually configured in Supabase Auth. The login page hides any provider button that is not enabled, and hides the whole social-sign-in block if neither is set. These are build-time public values, so a change requires a redeploy to take effect.
  • RESEND_API_KEY — Resend HTTP API key for transactional email. Unset means the notification outbox still fills but nothing is sent (dispatcher records each row as failed with "transport not configured").
  • EMAIL_FROM — from address for all platform email, e.g. HeatSync Labs <[email protected]>. The domain must be verified in Resend (SPF + DKIM) for production delivery.
  • CRON_SECRET — shared secret guarding the notification dispatcher. Generate with openssl rand -hex 32. Unset means the dispatcher returns 503 and never sends.

How a deploy runs

  1. A developer pushes to main.
  2. GitHub Actions (.github/workflows/deploy.yml) opens an SSH connection to the Droplet using a deploy key stored in repository secrets.
  3. The action invokes /opt/hackerspace-ops/deploy.sh, which:
    • Fetches latest main.
    • Runs pnpm install --frozen-lockfile.
    • Iterates over scripts/0*.sql. Any file not present in public._migrations_applied is applied and recorded.
    • Restarts PostgREST to refresh its schema cache.
    • Runs pnpm build.
    • Restarts the hackerspace-app systemd unit.
    • Probes the local app for a 200 response.

Migrations are idempotent. A redeploy that introduces no new migrations is a no-op on the database.

Local development

See LOCAL_DEV.md for the fresh-clone-to-running sequence using the Supabase CLI.

First-time provisioning

For initial server provisioning (Docker, the Supabase stack, Caddy, firewall, SSH hardening, deploy-key registration, the systemd unit, backup cron), follow DEPLOY_DO_SELFHOSTED.md end to end.

Notification dispatcher cron

Transactional email is an outbox drained by POST /api/cron/notifications. Add a once-a-minute root crontab entry on the Droplet (after RESEND_API_KEY, EMAIL_FROM, CRON_SECRET are set in the app .env):

* * * * * curl -fsS -m 30 -X POST http://127.0.0.1:3000/api/cron/notifications -H "Authorization: Bearer $CRON_SECRET" >/dev/null 2>&1

It hits the app locally (bypassing Caddy), self-throttles under Resend's rate limit, and is idempotent, so a missed or overlapping minute is harmless. Migration 041_notifications.sql is applied automatically by deploy.sh like every other scripts/0*.sql; no manual database step.

Operational checklist

  • Monitor /var/log/hackerspace-deploy.log for deploy outcomes.
  • Verify backups by restoring pg_*.sql.gz to a staging instance quarterly.
  • Rotate the Supabase service-role key and JWT secret if the .env on the Droplet is ever exposed.
  • Keep the host OS patched. The bootstrap installs unattended-upgrades.