Infrastructure that is checked before it exists
Why Provisioning keeps every declaration in Nickel and treats YAML as output
Infrastructure that is checked before it exists
Most infrastructure failures are discovered late. A misspelled plan, a service installed before the one it depends on, an environment variable nobody remembered to set: each of them survives review and fails when it is already running. Provisioning is built on one decision that moves those failures earlier — every piece of infrastructure is declared in a typed language, and validated before anything reaches a running system.
One source of truth, and it is typed
Provisioning describes servers, task services and clusters as Nickel schemas. The same declaration works whichever provider carries it out:
{
servers = [
{
name = "web-01",
plan = "medium", # an abstract size, translated per provider
provider = "upcloud", # or "aws", or "local"
}
]
}
TOML and YAML still exist — tools need them — but they are generated output, never the source of truth. A change is made in the Nickel declaration, checked against its schema, and only then rendered into whatever format a tool reads. A malformed server or an impossible combination is refused at the schema, not discovered by a failed deployment.
Order is part of the declaration
Infrastructure components depend on each other: Kubernetes needs containerd and etcd, Cilium needs Kubernetes. Instead of encoding that order in a script, a task service declares its dependencies and Provisioning resolves them:
{
taskservs = ["cilium"] # resolves containerd → etcd → kubernetes → cilium
}
A workspace is composed as a typed dependency graph, with referential integrity checked at schema time. The orchestrator then executes that graph — in parallel where the graph allows, with checkpoints and rollback — so the order of operations is a property of the declaration, not of whoever wrote the last script.
Configuration, never a hardcoded value
The third source of late failures is configuration scattered across environment variables and literals. Provisioning holds one invariant here: nothing is hardcoded; everything is configuration, resolved in a fixed order of precedence:
runtime arguments > environment > user configuration > infrastructure configuration > defaults
Every value has a place in that hierarchy, and every place is declared. Switching a workspace from development to production changes which layer provides a value, never the code that reads it.
What this buys
- Review happens on the declaration. Schemas, Nickel modules and Nushell workflows live in Git, so every change to infrastructure is versionable, reviewable and reversible.
- Providers stay interchangeable. Because the declaration is provider-agnostic and providers implement a common interface, moving a server from one cloud to another is a change of one field, not a rewrite.
- Failures move left. What used to fail on a node now fails at the schema, where it is cheap to fix and impossible to ship.
The declaration and the running system are two wheels of the same mechanism. Provisioning’s job is to keep them turning together.