Infrastructure that is checked before it exists

Why Provisioning keeps every declaration in Nickel and treats YAML as output

Provisioning 5 min read
Configuration sprawl, dependency order and imperative scripts fail at runtime. Provisioning moves all three to the schema: typed declarations, a resolved dependency graph and a configuration hierarchy with one source of truth.

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.

We use cookies to help this site function, understand service usage, and support marketing efforts. Cookie Policy for more info.