Deployment Guide
Deployment Guide
Section titled “Deployment Guide”Status: Authoritative deployment reference.
Cross-references: ARCHITECTURE.md (infrastructure stack), SECURITY.md (secret injection).
Prerequisites
Section titled “Prerequisites”- Cloudflare account with Workers enabled
- Supabase account with a project
- Domain registered (e.g.
gondolier.dev) wranglerCLI installed (npm install -g wrangler)- Node.js 20+
- Go 1.24+ (optional, for local builds)
Cloudflare setup
Section titled “Cloudflare setup”1. Create the Worker
Section titled “1. Create the Worker”wrangler init gondolier --type javascript2. Configure wrangler.toml
Section titled “2. Configure wrangler.toml”name = "gondolier"compatibility_date = "2026-08-03"workers_dev = true
main = "index.mjs"
[durable_objects]bindings = [{ name = "LEASE_DO", class_name = "LeaseDO" }]
[[migrations]]tag = "v1"new_sqlite_classes = ["LeaseDO"]
[[kv_namespaces]]binding = "RATE_LIMIT_KV"id = "<kv-namespace-id>"
[triggers]crons = ["*/5 * * * *"]3. Create KV namespace
Section titled “3. Create KV namespace”wrangler kv:namespace create RATE_LIMIT_KV# Copy the namespace ID into wrangler.toml4. Create Durable Object
Section titled “4. Create Durable Object”The LeaseDO class is defined in lease-do.mjs and exported from index.mjs.
No additional migration steps needed — the [[migrations]] section in
wrangler.toml handles it.
5. Inject secrets
Section titled “5. Inject secrets”wrangler secret put GONDOLIER_MASTER_KEYwrangler secret put SUPABASE_URLwrangler secret put SUPABASE_SERVICE_ROLE_KEYwrangler secret put ADMIN_KEYSecrets: See SECURITY.md for the full list of secrets and their purposes. Never commit secrets to the repo.
Supabase setup
Section titled “Supabase setup”1. Create project
Section titled “1. Create project”Create a new Supabase project at gondolier.supabase.co.
2. Enable Row Level Security (RLS)
Section titled “2. Enable Row Level Security (RLS)”RLS is enabled by default on new Supabase projects. Verify:
-- For each table, ensure RLS is enabled:ALTER TABLE tenants ENABLE ROW LEVEL SECURITY;ALTER TABLE forge_connections ENABLE ROW LEVEL SECURITY;ALTER TABLE managed_repos ENABLE ROW LEVEL SECURITY;ALTER TABLE audit_log ENABLE ROW LEVEL SECURITY;3. Run migrations
Section titled “3. Run migrations”# Install Supabase CLInpm install -g supabase
# Loginsupabase login
# Link to projectsupabase link --project-ref <project-ref>
# Apply migrationssupabase db pushOr manually:
psql -h <supabase-host> -U postgres -d postgres < migrations/001_base_tables.sqlpsql -h <supabase-host> -U postgres -d postgres < migrations/002_user_accounts.sqlpsql -h <supabase-host> -U postgres -d postgres < migrations/003_billing.sql4. Generate types (optional)
Section titled “4. Generate types (optional)”supabase gen types typescript --project-id <ref> > src/db/types.tsCloudflare Pages setup
Section titled “Cloudflare Pages setup”Sell site
Section titled “Sell site”Deploy the sell site (site/) to Cloudflare Pages:
# Option 1: Via CLIwrangler pages deploy site/ --project-name=gondolier-site
# Option 2: Via GitHub# Connect repo → Cloudflare Pages → build command: none (static HTML)# Publish directory: site/Dashboard
Section titled “Dashboard”Deploy the dashboard (templates/dashboard.html rendered by the Worker):
# Option 1: Via CLIwrangler pages deploy templates/ --project-name=gondolier-dashboard
# Option 2: Via GitHub# Connect repo → Cloudflare Pages → build command: none# Publish directory: templates/DNS configuration
Section titled “DNS configuration”# Sell site (Cloudflare Pages)gondolier.dev. CNAME <pages-site-id>.pages.dev
# Worker APIapi.gondolier.dev. CNAME <worker>.gondolier.<user>.workers.dev# (or point gondolier.dev directly if using Workers Dev)Note: DNS proxy (orange cloud) must be OFF for Workers routes that handle TLS. Cloudflare Pages uses its own TLS certificates.
CI/CD pipeline
Section titled “CI/CD pipeline”Forgejo Actions (.forgejo/workflows/ci.yaml)
Section titled “Forgejo Actions (.forgejo/workflows/ci.yaml)”The CI workflow runs on PR and main:
name: CIon: push: branches: [main] pull_request: branches: [main]
jobs: vet: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: '1.24' - run: go vet ./...
test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: '1.24' - run: go test ./...
build: runs-on: ubuntu-latest needs: [vet, test] steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: '1.24' - run: go build ./cmd/gondolierMerge queue gate
Section titled “Merge queue gate”The mq/** push trigger runs the shunt merge queue gate. This is the required
status that shunt checks before allowing a queued PR to merge.
Deployment workflow
Section titled “Deployment workflow”Deploy to preview
Section titled “Deploy to preview”wrangler deploy --env previewDeploy to production
Section titled “Deploy to production”wrangler deployDry run (CI)
Section titled “Dry run (CI)”wrangler deploy --dry-run --outdir=dist/Rollback procedures
Section titled “Rollback procedures”Worker rollback
Section titled “Worker rollback”# List versionswrangler versions list
# Rollback to specific versionwrangler versions rollback <version>Database rollback
Section titled “Database rollback”Migrations are forward-only. To rollback:
-- Manual rollback (run on Supabase)DROP TABLE IF EXISTS audit_log CASCADE;DROP TABLE IF EXISTS managed_repos CASCADE;DROP TABLE IF EXISTS forge_connections CASCADE;DROP TABLE IF EXISTS tenants CASCADE;
-- Re-run previous migrationpsql ... < migrations/000_previous.sqlNever drop tables in production without a backup. Always run migrations on a fresh database first to verify the rollback path.
DNS rollback
Section titled “DNS rollback”If DNS changes break routing:
# Revert CNAME records to previous values# Cloudflare DNS → Records → Edit → Restore previous valueMonitoring
Section titled “Monitoring”Cloudflare Analytics
Section titled “Cloudflare Analytics”Access via the Cloudflare dashboard at dash.cloudflare.com → Workers →
gondolier → Analytics.
Workers Observability
Section titled “Workers Observability”The wrangler.toml enables observability:
[observability]enabled = truehead_sampling_rate = 1Logs are available at:
wrangler tailHealth checks
Section titled “Health checks”GET https://gondolier.dev/healthz → 200 OKGET https://gondolier.dev/readyz → 200 OK