How we dogfooded reactor.cloud on a single Fly machine

Building our product website entirely on Reactor, deployed to a single machine with embedded Postgres

By Reactor Team

The site you’re reading right now runs entirely on Reactor. A single Fly.io machine serves the marketing site, documentation, blog, user console, and all the APIs. Here’s how we did it.

The architecture

Our deployment is what we call an “O2 topology” - all six capabilities running in a single process:

  • Sites serves this Astro-built static site
  • Auth handles user signup and login
  • Data stores profiles, posts, and analytics
  • Storage holds avatar images and uploads
  • Functions generates dynamic OG images
  • Jobs runs daily rollups and welcome emails

Everything connects to an embedded PostgreSQL 16 instance running on the same machine.

Why embedded Postgres?

Reactor’s bootstrap migration needs to run CREATE EXTENSION citext, which requires superuser privileges. Most managed Postgres services don’t give you that.

Instead of fighting managed services, we run PostgreSQL inside the same container as Reactor. One process tree, one volume snapshot, simple operations.

The data lives on a Fly volume. When we need more scale, we’ll migrate to external Postgres with the right permissions. For now, this is simpler.

The deployment

Our fly.toml is straightforward:

app = "reactor-cloud"
primary_region = "iad"

[http_service]
internal_port = 8000
force_https = true
auto_stop_machines = "off"

[[vm]]
size = "shared-cpu-2x"
memory = "2gb"

[mounts]
source = "reactor_data"
destination = "/data"

We use s6-overlay to supervise both PostgreSQL and reactor-server. The entrypoint script initializes the database on first boot.

Dogfooding every capability

Every page exercises something:

  • The landing page is a static Astro build served by Sites
  • Blog posts use ISR for incremental regeneration
  • The waitlist form submits to a Function that writes to Data
  • User signup goes through Auth with Resend SMTP
  • Profile pictures upload to Storage
  • A Job runs daily to aggregate analytics

If something breaks, we notice immediately because our own site stops working.

Telemetry baseline

We capture metrics to measure improvements:

  • Request latency (p50, p95, p99) per capability
  • Error rates
  • Job queue depth
  • Function cold starts
  • Database pool saturation

Every release, we run the same workload and compare. This gives us concrete numbers when optimizing.

Try it yourself

Everything we use is open source. Check out the deployment configuration in our repository and run your own Reactor instance.

The self-hosting guide covers the details.