Deploying to Fly.io
Fly.io runs Docker containers as Firecracker microVMs in regions close to your users. It’s well-suited for Procella’s single-process Hono + tRPC server with PostgreSQL.
Official docs: fly.io/docs
Prerequisites
Section titled “Prerequisites”brew install flyctl # macOS# or: curl -L https://fly.io/install.sh | sh
flyctl auth loginPostgreSQL
Section titled “PostgreSQL”Fly now recommends Fly Managed Postgres (fully managed, HA, PITR) over the legacy unmanaged cluster. Use Neon as an alternative if you want serverless billing.
Option A — Fly Managed Postgres:
# Provision in the same region as your appfly mpg create --name procella-db --region iad
# Attach to your app (prints the connection string)fly mpg attach procella-db --app procellaOption B — Neon / external Postgres:
fly secrets set PROCELLA_DATABASE_URL="postgres://user:pass@host/db?sslmode=require" --app procellafly.toml
Section titled “fly.toml”A ready-to-use fly.toml is included in the repo root. Customize the app name and primary_region for your setup:
# fly.toml — Procella on Fly.ioapp = "procella"primary_region = "iad" # pick closest to your users
[build] dockerfile = "Dockerfile"
# Non-sensitive runtime config[env] PROCELLA_LISTEN_ADDR = ":9090" PROCELLA_AUTH_MODE = "descope" # or "dev" PROCELLA_BLOB_BACKEND = "s3" # use S3/R2 for multi-region PROCELLA_BLOB_S3_REGION = "us-east-1"
# HTTP service — Fly handles TLS termination[http_service] internal_port = 9090 force_https = true auto_stop_machines = "stop" # scale to zero when idle auto_start_machines = true min_machines_running = 1 # keep at least one warm
[http_service.concurrency] type = "requests" soft_limit = 200 hard_limit = 250
# Health check on the Hono /healthz route [[http_service.checks]] grace_period = "10s" interval = "15s" method = "GET" path = "/healthz" timeout = "5s" type = "http"
# Machine sizing — shared-cpu-1x is sufficient for most workloads[[vm]] memory = "512mb" cpu_kind = "shared" cpus = 1Secrets
Section titled “Secrets”Sensitive values are set via CLI (never committed to fly.toml):
# Required secretsfly secrets set \ PROCELLA_DEV_AUTH_TOKEN="your-token-here" \ PROCELLA_ENCRYPTION_KEY="$(openssl rand -hex 32)" \ --app procella
# Descope modefly secrets set \ PROCELLA_DESCOPE_PROJECT_ID="P..." \ PROCELLA_DESCOPE_MANAGEMENT_KEY="..." \ --app procella
# S3/R2 blob storagefly secrets set \ PROCELLA_BLOB_S3_BUCKET="procella-blobs" \ PROCELLA_BLOB_S3_ENDPOINT="https://..." \ AWS_ACCESS_KEY_ID="..." \ AWS_SECRET_ACCESS_KEY="..." \ --app procellaSecrets are injected as environment variables and never stored in fly.toml.
Deploy
Section titled “Deploy”# Create the app (first time only)fly launch --no-deploy --name procella
# Deployfly deploy
# View logsfly logs --app procella
# Scale to 2 machinesfly scale count 2 --app procellaScaling
Section titled “Scaling”# Horizontal: add machinesfly scale count 3 --app procella
# Vertical: upgrade memoryfly scale memory 1024 --app procella
# Multi-region: add a regionfly scale count 2 --region lhr --app procellaProcella is stateless (all state in PostgreSQL + S3), so horizontal scaling is safe out of the box.
Blob Storage
Section titled “Blob Storage”For multi-region deployments, use S3-compatible storage (Fly Tigris, Cloudflare R2, or AWS S3) instead of local disk:
# Fly Tigris (native to Fly.io)fly storage create --name procella-blobs# This injects AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, BUCKET_NAME, AWS_ENDPOINT_URL_S3
fly secrets set \ PROCELLA_BLOB_BACKEND=s3 \ PROCELLA_BLOB_S3_BUCKET="$(fly storage info procella-blobs --json | jq -r .name)" \ PROCELLA_BLOB_S3_ENDPOINT="$(fly storage info procella-blobs --json | jq -r .endpoint)"