Strait Docs
Guides

Guide for deploying and scaling Strait in production.

Strait is designed as a single Go binary that can be deployed in various environments, from local development with Docker Compose to production on Fly.io or Kubernetes.

Architecture Overview

Production deployment consists of the following services:

ServicePlatformRegion(s)Purpose
Strait (Go backend)Fly.io (strait)iad, lhr, gruAPI server + worker
Sequin (CDC)Fly.io (strait-sequin)iadPostgres WAL → Redis pub/sub
PostgreSQLPlanetScaleus-east-1Primary data store
RedisUpstashPub/sub, SSE streaming, CDC events
Frontendapps/app (Vite/React)

Region Strategy

  • Primary region: iad (Ashburn, Virginia) — co-located with the database for lowest latency.
  • Edge regions: lhr (London), gru (São Paulo) — for faster API responses globally.
  • Sequin: Single instance in iad only. CDC requires a persistent WAL connection and must run next to the database. Multiple instances would cause duplicate event processing.

Secrets Management

All environment variables are managed via Doppler under the strait project with three configs:

ConfigEnvironmentNotes
devDevelopmentLocal defaults, localhost URLs
stgStagingShared infra with production
prdProductionProduction secrets and URLs

Local Development

# Configure Doppler for this directory (one-time)
doppler setup  # select project: strait, config: dev

# Run any command with secrets injected
doppler run -- go run ./cmd/strait
doppler run -- bun run dev

# Or generate a local .env file
doppler secrets download --no-file --format env > .env

Fly.io Integration

Secrets are synced from Doppler to Fly.io via the Doppler-Fly integration. When you update a secret in Doppler, it automatically propagates to Fly machines.

Operation Modes

Strait can run in three different modes, controlled by the --mode flag or the MODE environment variable:

  • api: Runs only the HTTP API server. Stateless and can be scaled horizontally.
  • worker: Runs only the job execution engine. Scales based on queue depth.
  • all (Default): Runs both the API and the worker in a single process. Ideal for small deployments or development.

Infrastructure Requirements

Strait requires the following backing services:

  • PostgreSQL 18: Primary data store and job queue (uses SKIP LOCKED).
  • Redis 8: Used for pub/sub, SSE streaming, and CDC event publishing.
  • Sequin: Required for Change Data Capture (CDC) from the Postgres WAL.

Docker Compose (Development)

For local development, use apps/strait/docker-compose.yml:

docker compose -f apps/strait/docker-compose.yml up -d

Fly.io Deployment

Project Structure

apps/strait/
├── Dockerfile      # Multi-stage Go build
├── fly.toml        # Fly config for the main app
├── Makefile        # Build, test, and deploy commands
└── .dockerignore

deploy/
└── sequin/
    └── fly.toml    # Fly config for Sequin CDC

Deploying the Main App

The Dockerfile uses a multi-stage build: Go compile on Alpine, then copy the binary to a minimal runtime image (~16 MB).

# From apps/strait/ — runs vet, lint, build, test before deploying
make deploy

# Or from repo root
bun run deploy

The deploy workflow runs preflight checks (go vet, golangci-lint, go build, go test) before pushing to Fly. If any check fails, the deploy is aborted.

Deploying Sequin

Sequin uses the official sequin/sequin:latest Docker image — no build step needed.

# From repo root
bun run deploy:sequin

# Or directly
cd deploy/sequin && fly deploy -a strait-sequin

Sequin requires the following secrets set on Fly (managed via fly secrets set):

SecretDescription
PG_HOSTNAMEPostgreSQL host
PG_PORTPostgreSQL port
PG_USERNAMEPostgreSQL user
PG_PASSWORDPostgreSQL password
PG_DATABASEDatabase name
PG_SSLEnable SSL (true)
REDIS_URLRedis connection string
SECRET_KEY_BASESequin internal encryption key
VAULT_KEYSequin vault encryption key

CI/CD

The GitHub Actions workflow (.github/workflows/deploy.yml) automatically deploys to Fly when:

  1. Code is pushed to master
  2. Changes are in apps/strait/**
  3. All required workflows pass (Test, Lint, Security)

Required GitHub secret: FLY_API_TOKEN — generate a deploy token scoped to the app:

fly tokens create deploy -a strait

Production Configuration

Required Environment Variables

VariableDescription
DATABASE_URLPostgres connection string
REDIS_URLRedis connection string
INTERNAL_SECRETSecret for internal API auth (32+ chars, unique per environment)
JWT_SIGNING_KEYKey for signing SDK run tokens (32+ chars, unique per environment)
ENCRYPTION_KEYData encryption at rest (unique per environment)
SECRET_ENCRYPTION_KEYSecret values encryption (unique per environment)

See Environment Variables for the full reference.

Scaling Strategy

For high-traffic production environments, it is recommended to separate the API and worker processes:

  1. Scale API: Increase the number of instances running in api mode to handle more concurrent HTTP requests.
  2. Scale Workers: Increase the number of instances running in worker mode to process more jobs from the queue. Since workers use SKIP LOCKED, they can safely compete for jobs without double-processing.

Graceful Shutdown

Strait implements graceful shutdown for both the API and the worker:

  • API: Stops accepting new requests and waits for active requests to finish (up to the configured timeout).
  • Worker: Stops dequeuing new jobs and waits for currently executing jobs to complete or reach a checkpoint before exiting.

Always use a process manager or orchestrator (like Kubernetes or Fly.io) that respects SIGTERM signals to ensure no jobs are abruptly interrupted.

Quick Reference

# Deploy main app (with preflight checks)
make deploy                        # from apps/strait/
bun run deploy                     # from repo root

# Deploy Sequin
bun run deploy:sequin              # from repo root

# Check status
fly status -a strait
fly status -a strait-sequin

# View logs
fly logs -a strait
fly logs -a strait-sequin

# Manage secrets (via Doppler)
doppler secrets -p strait -c prd   # list production secrets
doppler secrets set KEY=value -p strait -c prd

# Local dev with Doppler
doppler run -- go run ./cmd/strait
doppler run -- bun run dev
Was this page helpful?

On this page