soli-one
Turn a rack of Rocky Linux machines into one computer. You stop choosing which server runs what — you say what should run, and the cluster places it.
Cluster membership over SWIM gossip, authenticated node-to-node transport,
node fact collection, and the aggregate views:
one nodes, one top, one free, one df.
Three agents on three machines already report as one host.
Plus the content-addressed artifact store — ingest, materialize, verify and garbage-collect immutable deployment artifacts, with the round-trip property tested as an equation. A redeploy touching one file stores 4% of the tree.
And workload supervision: soli-oned runs workloads as systemd
transient units, so killing the agent with -9 leaves them
serving and a replacement agent adopts them with the same PID. That claim
has a test against the real binary.
Artifacts move between nodes over an authenticated socket, transferring only the chunks the receiver lacks — and consensus is in: a three-node Raft group elects a leader, replicates, survives losing a member and comes back from disk, with linearizable reads, transactions and leases on top.
On that: the scheduler, the alias table and cluster-wide port leases. A declared workload is placed, a dead node's work is relocated after a grace period, a pinned one is reported rather than moved, and a domain resolves to the full URLs of whatever is actually serving it.
And it runs on Rocky. The RPM builds, installs and passes an end-to-end
acceptance run on Rocky Linux 9.3 — including the one that matters:
kill -9 the agent, and the workload keeps serving with the
same pid.
Plus WAN federation policy: what may cross between regions, checked on the receiving side.
Verified on Rocky twice over, on every push: a container with systemd as
PID 1, on Rocky 10 and Rocky 9, with
ONE_ROCKY_BENCH=1 so a test that would skip fails the job
instead. Over 900 tests, plus the end-to-end acceptance run through the
installed RPM.
And the parts an operator needs before a cluster holds anything that
matters: one backup and one restore, sealed to a
key kept off the cluster; one secret, delivered to a workload
as a systemd credential; one metrics for node_exporter and
one logs --all across the fleet; one node upgrade,
which drains before it touches a node and stops at the first one that does
not come back; and a signed dnf channel to upgrade from.
Four crates are libraries with their own tests and no binary that
links them: VIP failover (soli-one-vip), the elected
ACME orderer (soli-one-acme), the OVHcloud client
(soli-one-ovh) and the /soli view
(soli-one-fuse, mounted read-only and read back through real
syscalls on the bench). Neither soli-oned nor
one depends on them, so none of this happens on a running
cluster today. The pages that describe them say so.
The trust model is split, and the change is not
compatible with clusters bootstrapped before it. The single shared
secret, which let every worker sign Raft traffic and open every operator
secret, is now a keyring with separate gossip, fetch and control keys, plus
a per-worker node key; federation has a key of its own, outside the keyring.
Join tokens (soli2) carry the role they admit —
one token --role control|worker — and a worker is given
nothing that votes or unseals. The agent's role is read from its keyring,
control nodes are enrolled into a roster in Raft rather than believed from
gossip, and proxy API keys moved to a proxy-keys file.
Security has the key table and the reasons.
An existing cluster cannot acquire the new keys in place: the old secret and
old tokens are refused with a message saying so. It has to be
re-bootstrapped — one bootstrap on a fresh founder,
one token --role … and one join for every
other node, workloads and secrets redeclared. Take a one backup
first.
Artifact replication is driven now. This note used to name it as a decision nobody invoked, so a rack going down cost copies in silence. Every twenty seconds the node holding the scheduler lease asks each peer which artifacts it holds, places copies across racks, and writes the answer; every node then fetches what it should hold and releases what it should not.
Verified with three agents in three racks: an artifact ingested on one node was on all three shortly after, with nothing telling the other two to copy it, and killing a rack produced “wanted 3 copies, placed 2” rather than silence.
Idle workloads go to sleep, and a request wakes them. This is what makes unlimited preview environments affordable: a Soli deployment is a long-lived process that holds its memory around the clock, and previews are mostly not being looked at.
Suspension is desired state — the reconcile loop is told the
workload should be asleep, rather than being fought by a second thing that
stops it — and the mode is stop, not freeze, because freezing holds the
cgroup and every byte in it. It is off unless a node is given
--idle-suspend-after-min: taking running workloads down is not a
behaviour to acquire by being upgraded.
The signal is a per-app last-request time the proxy now records. Three things it deliberately does not do: it never sleeps a workload the proxy said nothing about (an unreachable proxy would otherwise put a whole fleet to bed), never one that started within the grace period (redeploying something idle would otherwise send it straight back to sleep under the person who deployed it), and never one whose timestamp is in the future (clock skew keeps it awake).
Verified against a real agent and a proxy under test control: the workload came up, went to sleep with its unit stopped, stayed asleep across passes without flapping, and came back when the timestamp went fresh.
The WAN gossip pool carries region summaries, so cross-region routing has data to decide with. It is a second socket and a much slower timer than LAN gossip — per-node facts between regions is a transit bill for information nobody reads at that resolution — and what may cross is an allowlist, because the failure mode of a denylist is a new message type crossing by default until the invoice arrives.
Only control nodes join, and only those given the federation's WAN key
(--wan-key-file or the wan-key credential), which
no worker holds. Frames are believed only from configured
--wan-peer region=addr peers, and each peer speaks for one
region. The role in the frame is checked too, and is worth least — it
is the sender's own claim, so it catches a misconfigured node rather than an
attacker.
A summary carries a degraded reason, and it is set from whether the
region has a Raft leader rather than from whether it answers. “Healthy”
and “serving but cannot schedule” are identical in capacity numbers,
and the difference is the whole story. one regions shows it, and
one regions --route <name> asks where a request would go.
Verified with two regions on one workstation: each learned about the other, one reported itself degraded because it had no leader, and routing answered local, no-leader and unknown as three distinct things rather than one error. A worker was refused the pool outright.
Datacenter gateways, so a region fetching an artifact pulls it once over
the WAN rather than once per node; certificates in replicated state, which
is a security posture to choose rather than to grow into; and the per-app
migration off soli-proxy, where every mechanism exists and no
app has been moved yet.
Also covered by no automated run: SELinux enforcing and
firewalld. SELinux is a kernel feature, the CI runner has none, and
--privileged disables confinement anyway — so
packaging/rocky/vm.sh test under KVM is the only place they are
real, and it is a per-release step a person takes. See each
page for exactly where the line sits — nothing on this site documents a
command that does not exist.
Why this exists
The Soli stack runs on one machine at a time. soli-proxy supervises
around thirty apps in production, and its whole notion of state —
run/app_state.json, run/ports.lock, the sites/
directory — lives on local disk. That is a single point of failure and a
ceiling on growth at the same time.
It is also a product problem, not only an infrastructure one. A Soli deployment is a long-lived process, so an idle preview environment holds its RAM around the clock. Without multi-node placement and suspend-on-idle, you either cap preview environments or you lose the margin on them.
What "one machine" means here
Three layers of illusion, in increasing order of difficulty:
| Layer | What you get |
|---|---|
| Unified control plane | top, free, df, ps report the sum of the fleet. You run things without naming a host. |
| Single network identity | One floating address per datacenter. A node dies, the address does not move. |
| Single file namespace | Content-addressed immutable artifacts replicated across nodes, visible at the same path everywhere. |
What it deliberately does not claim
"Any node can take over any workload" is true for stateless work and false for
a database. solidb is pinned to its node and gets its availability from
its own replication, not from this cluster manager.
So the CLI says so. one ps carries a mobility column, one free
prints the largest single node next to the cluster total, and
one node drain refuses to empty a node holding pinned work without
naming what it would interrupt. Summing free memory across two hundred nodes is
a real number and a useless one: you cannot put a 400 GiB workload into it.
Where it sits in the stack
one/ this — cluster fabric
proxy/ L7 data plane, fed by the cluster
db/ SoliDB, replicates itself, pinned
kv/ SoliKV, one instance per tenant
es/ event log — audit trail and usage metering
lang/ the runtime the workloads are written in
soli-proxy keeps routing, TLS, ACME and Lua. What it gives up is
supervision: the cluster decides what runs where, and pushes the proxy a routing
table instead of the proxy discovering it from local disk.