Write your config in a real programming language.
An inventory is a program. You run it, and it builds one big JSON-compatible data structure — your whole config, assembled dynamically. From there hexol renders it to whatever you need: Kubernetes manifests, Terraform JSON, Ansible playbooks, SQL, plain YAML.
Hexol is both:
- a library — the Scheme you write inventories with, plus target libraries (Kubernetes, Terraform, …) layered on top, and
- a CLI — to act on an inventory:
renderit,applyit,inspectwhat it produced.
An inventory is a program that builds your config. Here's a Kubernetes one
(examples/kubernetes.scm):
(use-modules (hexol k8s))
(hx-ops
(with-namespace "loulou"
;; A Deployment (+ its Service). Its ConfigMap and Secret are pulled in by
;; name — one becomes `envFrom`, the other a mounted volume. Positional
;; name, then `(key value)` entries; values are evaluated Scheme.
(app "loulou"
(image "secure.io/loulou:2.1") (port 9000) (replicas 2)
(env-from (cm "loulou-config"))
(volumes (mount (sec "loulou-secret") "/etc/loulou/secret"))
(resources "200m-*/256Mi"))
;; The config the Deployment above consumes — ordinary Scheme data.
(configmap "loulou-config"
(data (LOG_LEVEL "debug") (CACHE_SIZE "256")))
(secret "loulou-secret"
(data (DB_PASSWORD "bG91bG91LXNlY3JldA=="))))
;; Cross-cutting pass: hash every Deployment's referenced ConfigMaps and
;; Secrets, and stamp the result onto the pod template as an annotation —
;; so a config change forces a rollout. No templating, just code.
(checksum-config))A few things this buys you over plain manifests:
- The Secret is attached to the Deployment without hand-writing the
volumesandvolumeMountssections —(mount …)wires both ends. "200m-*/256Mi"expands to the fullresourcesblock (CPU request200m, memory request + limit256Mi).checksum-configruns after every resource is built, finds each Deployment's ConfigMaps and Secrets, hashes them, and stamps aconfig/checksumannotation onto the pod template — so a config change forces a rollout, for every workload, for free. In Helm you'd hand-write asha256sumper Deployment.
Hexol runs on Guile 3.x — install that,
clone the repo, and run ./bin/hexol (it auto-compiles on first use):
git clone https://github.com/Polyedre/hexol && cd hexol
./bin/hexol render -i examples/kubernetes.scmThe CLI itself shells out to nothing. Individual features do, and only when you
use them: helm/yq to expand charts, sops for inline secrets, and
tofu/kubectl/talosctl for apply. Install whichever you need.
./bin/hexol render -o json -i examples/kubernetes.scm
# act on it (appliers an inventory registers via `applies-with`)
./bin/hexol apply --list -i examples/kubernetes.scm # show the applier pipeline
./bin/hexol apply --dry-run -i examples/kubernetes.scm # delegate to each tool's dry-run
# introspect: rendering is not opaque
./bin/hexol tree -i examples/kubernetes.scm # op tree (with hashes)
./bin/hexol show OP_HASH -i examples/kubernetes.scm # one op: source + delta
./bin/hexol explain regions.alpha5.network.cni -i examples/inventory.scm # what touched a pathWhile the kernel is target-agnostic, the library provide a few syntaxic sugar helpers:
- Kubernetes — Deployments, Services, ConfigMaps, Secrets, and
cross-cutting passes like
checksum-config. Render to a manifest stream with-o yaml. - Terraform — providers, resources, and data sources as Scheme, rendered to
terraform init-ready JSON with-o terraform. Seeexamples/terraform.scm. - Ansible — plays and tasks accumulated into a playbook, rendered with
-o ansible. Seeexamples/ansible.scm. - Secrets (SOPS) — secrets live inline in the inventory, encrypted at rest
with sops: no separate
*.sops.yamlfiles to keep in sync.(secret-ref 'key)is a cheap marker, so onlyrendershells out to sops; manage the store with thehexol secretsubcommands. Seeexamples/homelab.scmanddocs/authoring.md.
Inventories can also register their own renderers via renders-with — the
database-schema.scm and
ledger.scm examples add -o sql and -o ledger that
way.
How is this different from Pulumi, CDK, jsonnet, or Dhall?
Those build your config in memory and hand it off. Hexol keeps the build
itself around: resolving an inventory is a chain of small labeled steps over a
growing data structure, and every value remembers the step that set it — its
source, its file and line, the exact change it made. So you can point at any
value in the output and ask "what set this, and to what?" — tree, show, and
explain answer it. No other config tool does that.
Is this a serious Kubernetes/Terraform tool, or a Scheme demo?
The Kubernetes and Terraform libraries are the real ones —
examples/homelab.scm deploys a 3-node Talos cluster to
OVH for real, end to end. The SQL and Ledger renderers are tiny example-file
extensions, there to show the kernel doesn't care what it's building. Take them
as proof the engine is general, not as products.
Is it production-ready?
It's young: one author, no outside production users. It has a test suite
(make test, run on every push and PR via GitHub Actions) and it does run a
live homelab. Kick the tires before you bet a cluster on it.
docs/model.md— the engine model and its rationale.docs/authoring.md— writing inventories: surface forms, helpers, file splitting, ordering, and the repository layout.docs/extending.md— building target libraries, the kernel/library/example boundary, worked Terraform and Helm conversions, and introspection.docs/cmdb.md— the event-sourced CMDB built on the same kernel (fact log + versioned libraries + HTTP server).
Copyright (C) 2026 Polyedre. GNU General Public License v3.0 or later; see
LICENSE for the full text.