the open-source, self-hosted Fly.io alternative
Hostname routing, per-tenant isolation and scale-to-zero, without the platform bill. ORCHD is a single Go daemon that provisions per-tenant workloads, gives each one a hostname, caps and isolates it, puts it to sleep when nobody is using it, and wakes it on the next request in about a second. No cluster, no control-plane sprawl, no YAML.
measured on a Hetzner Cloud VM under gVisor, tinbase workload incl. initdb
the problem
Preview environments, per-customer backends, per-project dev servers, agent sandboxes — they all have the same shape: many small workloads, each needing its own URL and its own data, almost all of them idle almost all of the time.
systemd or pm2 will keep N processes alive, but nothing hands out hostnames, mints credentials, isolates a tenant from its neighbour, or reclaims the memory of the 900 tenants nobody is using.
A cluster to run 40 MB dev servers means an API server, etcd, a CNI, an ingress controller and a knative-shaped add-on before the first request is served — and someone to keep all of it alive.
Per-app pricing on a platform you do not operate stops being an option the moment your unit is a free-tier tenant, or the code you run is untrusted, or the margin is the product.
the shape
Everything ORCHD does is expressed with a project, a workload and a route. The control plane provisions and mints; the gateway resolves hostnames, wakes what is asleep, and proxies. Both live in the same binary.
*.example.com (wildcard DNS -> the box)
|
+--------------+ gateway: Host -> route table -> workload,
| gateway | wake if suspended, reverse-proxy
| :8081 | (HTTP, WebSockets, streaming)
+--------------+
|
+-------------------+-------------------+
+----------+ +----------+ +----------+ one instance per
| workload | | workload | | workload | workload, its own
| + volume | | + volume | | + volume | volume, suspended
+----------+ +----------+ +----------+ when idle
^
+--------------+ control plane: create projects and
| API :8080 | workloads, mint keys, attach domains,
+--------------+ back up, place in a regionA tenant, an environment, a customer. Owns workloads and cascades on delete.
One routable, independently scheduled, scale-to-zero instance with a volume, resource caps and env.
A hostname pointing at a workload. Default subdomain, custom domains, or a local port.
in practice
The control plane is an ordinary JSON API behind a bearer key. Everything the admin panel does, you can do with curl — or with a template, which describes a whole multi-workload project in one file that lives in the repo.
curl -X POST https://api.example.com/v1/projects \
-H "Authorization: Bearer $ORCHD_KEY" \
-d '{"name":"acme","workloads":[{"preset":"vite"},{"preset":"api"}]}'
# -> workloads, each with routes, endpoints and keys.
# First request through the gateway wakes them.{
"name": "acme",
"workloads": [
{ "name": "db", "kind": "tinbase" },
{ "name": "api", "kind": "node", "dir": "api",
"install": ["npm","install"],
"run": ["node","index.js"], "port_env": "PORT" },
{ "name": "web", "kind": "static", "dir": "web" }
]
}The same file runs without a control plane at all: npx orchd up boots every workload on your machine, each on its own port. See the CLI.
what you get
An idle reaper suspends what has not been asked for; the gateway wakes it on the next request. keep_warm pins the ones that must stay hot.
The Docker driver runs tenants under gVisor, a userspace kernel — VM-grade syscall isolation on a plain cloud VM, plus memory, CPU and pid caps per container.
Default subdomains, bring-your-own domains, path subroutes, and on-demand certificates gated so a cert is only ever issued for a host that resolves to a real workload.
One orchd.json describes a multi-workload project. Provisioning is async, so a heavy first install never blocks the call.
Freeze a template into an immutable v1, v2… — a tarball plus a container image per workspace — push it to a registry, import it on another instance.
Byte-exact volume snapshots to a local dir or S3/R2 over hand-rolled SigV4, excluding everything derived. The control-plane index is backed up too.
A bootstrap key from a file on the box, minted keys stored hashed, readonly vs admin roles, and a per-key token bucket so one client cannot starve another.
Runtime driver, state store, backup target, event sink and metrics sink are all interfaces. Most switch from the settings page; the rest are one env var.
The same control loop runs with no Docker at all — workloads as host processes on stable ports, or on a real wildcard *.test domain with trusted TLS.
who uses it
It is a standalone orchestrator with no opinion about what it runs. Two products are being built on it — and they are users of ORCHD, not part of it.
One dedicated Supabase-compatible backend per project, coupled the way Supabase Cloud couples them, but a single small process per tenant instead of a container stack.
A web runner, a mobile runner, an API server and a dev database per generated project — mostly idle, running untrusted user code, which is exactly why the isolation matters.
The docs open with the reasoning — the problem, the goals, and the things ORCHD deliberately refuses to do — before any endpoint.
from the same team
ORCHD is built by the makers of these open-source tools and products.
AI that generates production-ready React Native apps and UIs from a prompt.
A Supabase-compatible backend without Docker — one process, real Postgres, runs in the browser.
Linux APIs in the browser — real dev tooling with no VM and no container.
A Metro plugin and a thin dev server for Expo — many dev environments per machine.