Technical Whitepaper · v1.0

terraform-stack

Vercel + Supabase + Cloudflare + DigitalOcean as one Terraform repo. Twenty minutes to a full production stack. Reproducible everywhere.

MIT LicenceTerraform 1.9+Four providersEach module < 50 LOCReproducible state
4Providers
~20 minTo full stack
£30–80/mo small SaaS
< 50LOC per module

§ Abstract

A solo engineer shipping a SaaS in 2026 typically uses six providers in the first month: Vercel, Supabase, Cloudflare, GitHub, a registrar, and a transactional email service. Each provider’s console is excellent. Wiring them together by hand is correct, slow, and not reproducible.

terraform-stack is the wiring as code. Four small modules — Vercel, Supabase, Cloudflare, and an optional DigitalOcean — composed in a root main.tf that stands the whole stack up with one apply. The modules are intentionally small: each is under 50 lines, readable in a single sitting, and hides nothing.

This whitepaper documents why these four providers, why the modules are deliberately small, and the conventions that keep the repo useful as a starter rather than as a framework.

1Why these four providers

The combination wins on cost, time-to-ship, and operational simplicity for a solo or small team building a modern SaaS in 2026:

  • Vercel removes operational overhead from the frontend. Auto-scaling, edge functions, preview deployments, native Next.js integration.
  • Supabase removes operational overhead from the database, auth, realtime, and storage. Postgres-native, sane pricing, runnable self-hosted if you ever need to leave.
  • Cloudflare R2 has zero egress fees and KV is cheap and fast. The DNS is free and the developer ergonomics are first-class.
  • DigitalOcean is the cheapest credible cloud for the long-running workloads that do not fit on Vercel. The DOKS Kubernetes service pairs well with the k8s-ops-toolkit chart.

The repo is intentionally not a generic AWS-everywhere stack; there are excellent ones already. It is a specific, opinionated set of modules for the specific stack the solo engineer ships SaaS on.

2Why the modules are small

The largest module (modules/vercel) is under 50 lines of Terraform. A reader can hold the whole module in their head. Compare to a typical "enterprise" Terraform module that wraps a provider in a thousand lines of abstraction; the goal here is the opposite.

No hidden state

The wiring between modules is explicit in main.tf. If the Vercel project depends on the Supabase service role key, the dependency is visible in the env_vars block. There is no "platform" abstracting it away.

Outputs are deliberate

Each module exports the values you actually need to plug into the next module or your CI/CD. Sensitive outputs are marked sensitive. The outputs are stable contracts; we treat changes to them as breaking.

Sensible defaults, easy overrides

Every variable has a sensible default for the common case. Overrides are one Terraform variable away. We do not parameterise everything upfront; we wait for someone to hit the limit and then add the knob.

3State and secrets

State is local by default. You should configure a remote backend for anything you actually deploy. S3 is supported natively, and Cloudflare R2 also speaks the S3 protocol — useful if you want to keep state in the same provider as the rest of your infrastructure.

terraform {
  backend "s3" {
    bucket = "my-tf-state"
    key    = "stacks/sarma-prod/terraform.tfstate"
    region = "eu-west-2"
  }
}

Sensitive outputs (the Supabase service role key, the database password, the anon key) are marked sensitive in the modules. Always encrypt your remote state. Never commit .tfstate files. Never put credentials directly in terraform.tfvars when the repo is public — use environment variables.

4How the modules wire together

module "supabase" {
  source       = "./modules/supabase"
  project_name = var.project_name
  region       = var.supabase_region
  org_id       = var.supabase_org_id
}

module "cloudflare" {
  source = "./modules/cloudflare"
  domain = var.domain
}

module "vercel" {
  source       = "./modules/vercel"
  project_name = var.project_name
  domain       = var.domain
  github_repo  = var.github_repo
  env_vars = {
    NEXT_PUBLIC_SUPABASE_URL      = module.supabase.api_url
    NEXT_PUBLIC_SUPABASE_ANON_KEY = module.supabase.anon_key
    SUPABASE_SERVICE_ROLE_KEY     = module.supabase.service_role_key
    R2_BUCKET                     = module.cloudflare.r2_bucket
    KV_NAMESPACE_ID               = module.cloudflare.kv_namespace
  }
}

The Vercel project is created last so its env vars resolve from the Supabase and Cloudflare outputs. Terraform’s dependency graph handles the ordering automatically. Adding a provider — Resend, Stripe, GitHub Actions, anything — follows the same pattern: a small module, an explicit wiring in main.tf.

5What this saves

Setting up this stack by hand the first time is half a day for an experienced engineer; it is two days for someone learning the providers. With the repo it is twenty minutes.

Reproducibility is the larger value. Production, staging, and a per-PR preview environment can all run from the same Terraform with different variable files. Without IaC, "make me a staging that mirrors prod" is itself a project; with this repo it is a workspace.

Cost discipline shows up as a side effect. The recommended config produces a stack that costs roughly £30–80/month for a small SaaS. There is no hidden capacity, no unused load balancers, no "we forgot we provisioned that" surprises in the bill.

6Where this falls short

  • Multi-region. Each module assumes a single region. Multi-region SaaS is a different design, not a parameter to flip.
  • Compliance frameworks. SOC2, ISO 27001, HIPAA need workflow and documentation that IaC alone does not provide.
  • Backups and DR. Supabase has its own backup story; the Cloudflare modules do not configure cross-region replication. Use the providers’ native tools for this layer.
  • AWS-everywhere stacks. If your primary cloud is AWS, this is not the right starter.

7Companion repos

  • k8s-ops-toolkit — observability and the Helm chart for the optional DigitalOcean Kubernetes path. The DO module here provisions the cluster; the toolkit deploys onto it.
  • The other Sarma open-source repos (voice-agent-starter, agent-orchestrator, ai-eval-runner, etc.) are example consumers of this stack — same shape, same env conventions.

terraform-stack · Built by Sarma Linux · MIT licensed