Requirements
What a self-hosted installation needs from you before the first package is installed — machines, PostgreSQL 18, an S3-compatible store, DNS, and certificates.
Machines
eval | single | ha | |
|---|---|---|---|
| Machines | 1 | 2 | 4 or more |
| Control plane | 4 vCPU, 8 GB RAM, 50 GB disk | same | same |
| Data node | shares the control plane | 200 MB of RAM per resident project plus headroom, local NVMe | same, with one node's worth of spare capacity |
| Operating system | Ubuntu 24.04, Debian 12, RHEL 9 or 10, Rocky 9 or 10 (x86-64) | same | same |
A data node needs glibc 2.38 or newer, which Ubuntu 24.04, Debian 13 and RHEL 10 have and RHEL 9 does not. The control plane runs on RHEL 9; a node on RHEL 9 is refused by the pre-install check before anything is changed.
A node holds at most 64 projects, parked or running. The ceiling is the slot band, not the hardware: every project has a fixed port pair on its node. RUNLOT_MAX_SLOT in the node's configuration raises it, with the same value on the control plane and on every node, set before the first install.
How long a node takes to come back
When a data node is lost, every project on it is restored from the object store onto a replacement. Measured on a 16-core, 62 GB node with every project evicted and then asked for at once:
| Projects | Everyone back and answering |
|---|---|
| 50 | 31 seconds |
| 100 | 56 seconds |
The curve is about half a second per project above a 15-second floor. Those projects held 10 MB each; a project holding 500 MB takes longer to pour, and an object store across a slow link adds its own time.
PostgreSQL 18
The control plane keeps its metadata in a PostgreSQL you run. It needs:
- Version 18. The migrations use
CREATEROLEsemantics that exist from 16 and the product is tested on 18. - A role with
CREATEROLEand ownership of an empty database. The installer creates three more roles inside it and grants itself membership in them. - Reachability from the control plane. The data nodes never connect to it.
CREATE ROLE runlot LOGIN CREATEROLE PASSWORD '…';
CREATE DATABASE runlot_meta OWNER runlot;runlot-admin preflight cp checks the version, the role and the grants before the install changes anything.
If your PostgreSQL does automatic failover, read Operate: the metadata database was restored first. The control plane detects a database that went back in time and stops issuing leases until an operator confirms the new state.
An S3-compatible object store
Backups, uploaded files, Git backups and deploy bundles go to one bucket in a store you run: MinIO, Ceph RGW, or a cloud S3. The control plane and every node need:
- The endpoint, a bucket, and an access key pair that can list, get, put and delete inside that bucket.
- Path-style addressing if your store does not serve a bucket as a virtual host (MinIO by default).
Both machines are checked for reachability and for a working PutObject before the install.
DNS
Four names, and two facts about them that bite.
| Name | Points at | Serves |
|---|---|---|
runlot.acme.example | the control plane (behind your TLS terminator) | the dashboard, the API, the operations console |
git.acme.example | the control plane | Git over HTTPS and SSH |
*.apps.acme.example | the data nodes | every deployed app |
*.wire.acme.example | the data nodes, port 5433 | psql connections |
*.wireis a separate wildcard from*.apps. A wildcard certificate covers one label, so*.wire.acme.examplehas to exist as its own record and its own certificate.- Port 5433 must reach the node unterminated. The wire front routes a connection on the TLS server name in PostgreSQL's startup packet. A proxy that terminates TLS in front of it breaks every connection. HTTP may go through your reverse proxy; 5433 may not.
Certificates
We issue no certificate on your installation. You provide:
- A certificate for the dashboard and Git names, on your TLS terminator in front of the control plane.
- A certificate for
*.apps.acme.exampleand one for*.wire.acme.example, on each data node. node-front re-reads its files onSIGHUP, so a renewal is a file replacement and onesystemctl reload.
A custom domain a user attaches to a project is a CNAME plus a certificate you provide; the dashboard's instructions say so in this mode.
Ports
| Port | On | Open to |
|---|---|---|
| 8080 | control plane | your TLS terminator |
| 8081, 8082 | control plane | the data nodes (private network) |
| 9105 | control plane | the data nodes |
| 80, 443 | data node | the internet, or your reverse proxy |
| 5433 | data node | wherever psql connects from |
| 9100, 9103, 9106 | data node | the control plane and the other nodes (private network) |
| 22 | data node | the control plane, for runlot-admin upgrade |
The control plane and the nodes talk over a private network. A node refuses to bind its backends on a public address.