Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.kindling.systems/llms.txt

Use this file to discover all available pages before exploring further.

Production Setup

The common public shape for Kindling is a Linux host running the control plane with public edge-facing hostnames.

Core Pieces

  • a Linux host with the required Kindling toolchain
  • PostgreSQL configured for Kindling state and logical replication
  • public DNS records for API, dashboard, and any workload hostnames
  • firewall and host permissions aligned with the chosen runtime
Start with one of these supported shapes:
  • All-in-one single server: one Linux host runs API, edge, worker, WAL listener, reconcilers, and PostgreSQL.
  • Single control plane + remote workers: one Linux host runs API, edge, PostgreSQL, WAL listener, and reconcilers; additional Linux hosts run worker-only kindling serve processes against the same database cluster.
For topology boundaries and unsupported layouts, see Topology and Hostnames and the repo HA guide (docs/high-availability.md).

Important First-Boot Inputs

  • public_base_url
  • optional dashboard_public_host
  • optional service and preview base domains
  • optional advertise_host for browser-openable runtime URLs
These can be seeded through kindling serve flags on first boot.

What Production Means In Practice

Production self-hosting is not only “the server starts.” It also means:
  • TLS can be issued for the public hosts you expose
  • the dashboard build points at the correct API origin
  • the host can actually expose runtime URLs or edge-routed traffic
  • the runtime and networking permissions on the machine match the chosen deploy path

Multi-Server Bootstrap

This is the recommended sequence for a control-plane-plus-workers cluster. Canonical topology boundaries: repo docs/high-availability.md and docs/private-networking.md.

1. Prepare the database tier

  • Create or identify the PostgreSQL cluster that will hold Kindling state.
  • Put PgBouncer or another private SQL entry point in front of PostgreSQL on your private network.
  • Make sure every Kindling host can reach that same private host:port.
  • Write the same DSN to /etc/kindling/postgres.dsn on every host.
  • If /etc/kindling/postgres.dsn points at PgBouncer, also write the same direct-primary DSN to /etc/kindling/postgres.replication.dsn on every host for the WAL listener.
Example DSN shape:
postgres://kindling:secret@10.50.0.5:6432/kindling?sslmode=require
Do not point multi-server production nodes at localhost, 127.0.0.1, or a public PostgreSQL listener. Example direct replication DSN shape when pooling:
postgres://kindling:secret@10.50.0.6:5432/kindling?sslmode=require

2. Prepare the control-plane host

  • Install host dependencies (make install-deps or contrib/install-host-deps.sh --all as appropriate).
  • Build or deploy the kindling binary.
  • Run sudo bash contrib/setup-kindling-prod.sh.
  • Edit /etc/kindling/kindling.env with:
    • KINDLING_HOME
    • KINDLING_ADVERTISE_HOST
    • KINDLING_PUBLIC_URL
    • optional KINDLING_DASHBOARD_HOST
    • KINDLING_ACME_EMAIL
  • Keep /etc/kindling/postgres.dsn present before first start.
  • If you use PgBouncer, keep /etc/kindling/postgres.replication.dsn present before first start too.

3. Start the first node

Bring up the control-plane services:
sudo systemctl daemon-reload
sudo systemctl enable --now kindling@edge kindling@api kindling@worker
On first boot, this node will:
  • run migrations
  • create or load ~/.kindling/server-id
  • register itself in the servers table
  • seed initial cluster/server settings from the serve flags and env-backed wrapper

4. Validate the first node

Verify:
  • the API is reachable at public_base_url
  • the dashboard can load against the intended API origin
  • GET /api/meta returns successfully
  • the first server appears in GET /api/servers
  • server-id exists on disk and remains stable across restarts

5. Join a worker node

For each additional worker host:
  • install dependencies and deploy the same kindling binary
  • provision the same /etc/kindling/postgres.dsn
  • if pooling, provision the same /etc/kindling/postgres.replication.dsn
  • run sudo bash contrib/setup-kindling-prod.sh
  • edit /etc/kindling/kindling.env
    • set KINDLING_ADVERTISE_HOST to that host’s reachable address
    • keep KINDLING_PUBLIC_URL pointed at the shared control-plane API origin
    • Without WireGuard mesh: set KINDLING_INTERNAL_IP to this host’s stable private IPv4 (required when other servers already exist in the cluster)
    • With WireGuard mesh (Linux): set KINDLING_WG_MESH=1, KINDLING_WG_ENDPOINT to this host’s UDP ip:port, and on joiners set the coordination-server variables (see below)
  • start the worker component:
sudo systemctl enable --now kindling@worker
The worker must not join with:
  • loopback internal_ip (or, without mesh, omitting KINDLING_INTERNAL_IP when the cluster already has other servers)
  • a loopback / localhost PostgreSQL DSN
Kindling rejects those unsafe multi-server join cases at startup.

WireGuard mesh (Linux, optional)

When KINDLING_WG_MESH=1 on Linux edge/worker processes, Kindling:
  • allocates a deterministic 10.64.x.x overlay address per server-id
  • stores WireGuard metadata on servers (wireguard_ip, wireguard_public_key, wireguard_endpoint)
  • configures wg0, syncs peers from PostgreSQL on an interval, and sets servers.internal_ip to the overlay address for that process
Environment (see docs/private-networking.md for the full contract):
VariableRole
KINDLING_WG_MESH=1Enable automation
KINDLING_WG_ENDPOINTThis node’s UDP endpoint (host:port) as seen by peers
KINDLING_WG_LISTEN_PORTOptional; default 51820
KINDLING_WG_PRIVATE_KEYOptional override (base64); else file ~/.kindling/wg-private-key, else Kindling generates a per-node local key
KINDLING_COORDINATION_SERVER_WG_IPJoiners only — coordination peer overlay IP
KINDLING_COORDINATION_SERVER_ENDPOINTJoiners only — coordination peer UDP endpoint
KINDLING_COORDINATION_SERVER_PUBKEYJoiners only — coordination peer WireGuard public key
Edge → workloads: the edge proxy still connects to vm_ip:port from Postgres. With mesh enabled, peers advertise each server’s VM ip_range as WireGuard AllowedIPs, so the control-plane host can route to remote VM addresses over wg0 when those IPs sit in the peer’s allocated range. If you disable mesh, you must provide your own routing or port publishing between edge and workers.

6. Validate the joined worker

After joining, verify:
  • the new server appears in GET /api/servers
  • the worker reports healthy component metadata
  • deployments can place workloads onto the joined worker
  • the control plane can still reach workloads running there
  • if using Cloud Hypervisor across multiple workers, KINDLING_CH_SHARED_ROOTFS_DIR is consistent with your shared-storage design

Operator Checklist

Before calling the cluster healthy:
  • every host uses the same /etc/kindling/postgres.dsn
  • the DSN points at a shared private database entry point
  • if pooling, every host uses the same /etc/kindling/postgres.replication.dsn and it points at a replication-capable PostgreSQL host
  • every host has a stable server-id at ~/.kindling/server-id (no KINDLING_SERVER_ID env — obsolete)
  • without mesh: every joining worker has KINDLING_INTERNAL_IP set to a non-loopback private address
  • with mesh: every participating host has KINDLING_WG_MESH=1, KINDLING_WG_ENDPOINT, and joiners have coordination-server env vars until the mesh converges
  • no multi-server worker advertises 127.0.0.1, 0.0.0.0, or localhost as its internal address
  • the control plane can proxy to workloads on joined workers
  • DNS, TLS, and dashboard API origin all match the public hostnames you configured

Documentation map

Strategic product architecture (including supported topology narrative) may live in your team’s Kindling vault as Spec.md. Operator runbooks in this repository are what you follow for day-one setup and validation: