Testing Pre-release images, not for production. Read the status

Composable, mesh-native appliances on Debian.

Every appliance is secure by default and can join a private cloud on day one.

A fork of TurnKey Linux 19 (Debian 13) that turns single-machine appliances into building blocks of a real cloud: small components that run alone or together, replicate across sites, fail over, and are upgraded with apt. IPv6-first, so every node has a routable address of its own, and declarative, so every machine is described by one YAML file that Keel applies and re-applies to the same result.

  • IPv6-first
  • Declarative
  • WireGuard mesh
  • Upgraded with apt
wg0 · mesh · IPv6
  • WireGuard tunnel
  • opened from inside a NAT
  • traffic between nodes
What changes

Six things an appliance should already do

TurnKey appliances are excellent on day zero and built for one machine. Keel keeps the appliances and changes the model around them. Each point says how far it is built.

  • Decided

    Composed, not pre-cooked

    PostgreSQL, MariaDB, Redis, Nginx and the language runtimes are components of their own, each able to run alone or serve another appliance. An appliance is a manifest that names what it is built on. LAMP and LAPP stop being images and become a recipe.

    002300360041, the manifest

  • Partly built

    The mesh is built in

    WireGuard ships in Keel Core, so every appliance can join a mesh, even alone. Tunnels are opened from the inside out, so a node behind NAT needs only outbound UDP. Native IPv6 goes direct, 2001:db8:a::10 to 2001:db8:b::10; IPv4-only nodes enter through a rendezvous point that translates.

    0024How the mesh works

  • Partly built

    Secure by default

    Layers are exported with no host keys, certificates or machine-id, and each machine makes its own at first boot. CrowdSec ships in Core; Keel Web adds a WAF and Anubis. Every listening port is declared loopback, mesh or public, and the firewall is derived from that.

    00290030Security in detail

  • Partly built

    A real cloud, not a single box

    A primary and a hot standby, the standby seeded from the primary and kept read-only. Failover is the operator's call, and automatic failback after full catch-up is a goal. Directories replicate over the mesh, and an etcd registry records every node and its site.

    003100320025

  • Working today

    One file is the truth

    A YAML instance spec declares the machine: its name, IPv6 network, certificates, users, database role and monitoring. keel inspect writes it from a running machine, keel diff shows drift field by field, and keel spec apply converges. Re-applying changes nothing.

    keel0027

  • Decided

    Upgraded with apt, backed up off-site

    Everything Keel installs becomes a .deb in the Keel repository, on a stable or a testing track declared in the YAML. A database primary hands over before it upgrades. Backups go to a Keel Backup appliance in another site, and restores are tested on a schedule.

    003900370038

Composition

Small components, joined by the mesh

An application sits on a runtime, the runtime on Keel Web, and Keel Web on Keel Core. The data services stand beside the stack as appliances of their own, and the WireGuard mesh joins them.

An appliance, top to bottom

  • ApplicationWordPress, Odoo, Nextcloud, Mastodon: a manifest and an inithookplanned
  • RuntimePHP-FPM, Python, Ruby, Node.js or Goplanned
  • Keel WebNginx, the Coraza WAF and Anubisfocus now
  • Keel CoreDebian 13, WireGuard, etcd, CrowdSec, the installerfocus now

Data services, beside it

  • PostgreSQL, MariaDBa primary and a hot standby
  • Redissessions, queues and caches
  • Searchderived data, rebuilt when a node joins
  • Object storageGarage, for media, attachments and backups

In a simple installation the data services are embedded on the same machine. In an advanced one they are appliances of their own, found on the mesh. The composition in detail.

Installation modes

One image, three ways to install it

The image is the same in every mode; the mode chosen at first boot decides what runs. Decided, 0028

  • Simple

    • Everything in one LXC
    • No mesh and no etcd
    • CrowdSec installed and disabled
    • Keel Web is Nginx only
  • Advanced, cloud simple

    • The database replicated from a primary to a standby
    • The application's files in a single LXC
    • The primary hands the standby its key, password and WireGuard endpoint
    • Two nodes cannot elect, so failover is by hand
  • Advanced, cloud advanced

    • Three LXC at least, with an etcd quorum
    • Directory replication over the mesh
    • Three sites, or two plus an external arbiter
    • Later nodes install by discovery, not by typing

Installation modes and replication in detail.

Security

Three gates before the application

In an advanced installation, a request meets CrowdSec first, then Nginx with the WAF inline, then Anubis, and only then the application. Decided, 0030

  1. InternetA request arrives, over IPv6 first.
  2. CrowdSecBans known bad addresses before anything else runs.
  3. Nginx + WAFTerminates TLS, serves static files and runs Coraza with the OWASP Core Rule Set.
  4. AnubisProof of work, behind Nginx and never at the edge. Search crawlers are exempt.
  5. ApplicationOnly what passed the gates reaches it.
banned by CrowdSec refused by the WAF challenged by Anubis exempt crawler In a simple installation Keel Web is Nginx only and CrowdSec is off.

Keys, the pipeline, exposure classes and what each mode enables.

Declarative

Declared in one file, converged by one command

The instance spec, /etc/keel/instance.yaml, is the machine written down. Keel reads the machine into it, compares the two and converges the machine to it.

  • Inspect. Write a spec from a running machine, every field with its source, secrets never read.
  • Diff. Report drift field by field: same, drift, unknown or not declared.
  • Apply. Converge only what differs. A second run changes nothing.
  • Emit. Every installation will write its complete YAML, defaults included, so one answer file replays many installations. Decided, 0027
root@blog: ~
$ keel inspect --output /etc/keel/instance.yaml
instance.hostname: blog (from /etc/hostname)
instance.fqdn: shop.example.org (from /etc/hosts)
network.interfaces.eth0.ipv6: static 2001:db8:1::10/64 gateway fe80::1
inspect: 20 inferred, 1 not inferred (0 required), 2 secrets to provide; spec complete
# declare the name this machine should have
$ sed -i 's/shop.example.org/blog.example.org/' /etc/keel/instance.yaml
$ keel diff
instance.hostname: same (blog)
instance.fqdn: drift (declared blog.example.org, observed shop.example.org)
network.interfaces.eth0.ipv6.address: same (2001:db8:1::10/64)
diff: 12 same, 1 drift, 0 unknown, 1 not declared, 2 not compared; drift found
$ keel spec apply --system-only
apply --system-only: /etc/inithooks.conf not read or written
instance.fqdn: write /etc/hosts with '127.0.1.1 blog.example.org blog' (mode 0644): done
apply --system-only: 1 change(s), 0 failed
$ keel diff | tail -n 1
diff: 13 same, 0 drift, 0 unknown, 1 not declared, 2 not compared; no drift

Commands and output lines as the keel documentation gives them; the session is abridged.

How it is built

Signed, content-addressed layers

Every appliance is assembled from read-only layers. Each is a deterministic tarball with a sha256, listed in a plain-text layer manifest that is signed by the project key and tied to a git commit.

  • Reproducible. The same commit and the same manifest give the same bytes.
  • Incremental. An update downloads only the layer that changed: a new WordPress release is the app layer, not the base.
  • Verified. keel pull checks every digest, and keel verify checks installed layers against their manifests.

Measured on the build host

Unmodified TurnKey 19.0 recipes built as layers, before the composition redesign. One build host, one measurement each.

  • Core layer326 MB

  • LAMP stack delta80 MB

  • WordPress app delta133 MB

  • WordPress, rebuilt on cached layers69 s

  • WordPress, built from scratch451 s

Standards

Rules every change follows

  • 90 percent test coverage minimum, 95 for project code

    Every change ships with tests. The forked repositories keep at least 90 percent coverage; code the project writes keeps 95 percent. The check is required on every pull request.

  • IPv6 in every example

    Commands, defaults and configuration examples use IPv6 addresses, such as [2001:db8:4b1::10]:22. IPv4 is optional and never assumed.

  • English

    Commits, code comments, documentation and pull requests are written in English.

  • History is never rewritten

    Forks keep the upstream history and the upstream remote. No squashing, no force pushes, cherry-picks with attribution.

  • System containers only

    Full init, journal, cron, SSH and filesystem-level backup. Tests run on LXC with native systemd against real databases. No other container model is involved.

Progress, 2 and 3 October 2026

Signed testing images, and the first two mesh nodes

What landed in two days of work. Everything here is on the testing channel and not meant for production.

  • Shared default keys, withdrawn and fixed

    The earlier test images and layers carried keys made at build time, so every machine made from one shared them. They were withdrawn and rebuilt. A scan on every layer and image now checks that none carries SSH host keys, TLS private keys, a Webmin certificate or snakeoil keys, and every machine generates its own at first boot.

    Keys and identityThe withdrawal notice

  • A signed archive with two tracks

    archive.keellinux.org is signed and has two tracks: trixie-testing, where everything lands first, and trixie, the stable track. Promotion to stable is a maintainer's act.

    The archive keyringWhere packages come from

  • Debian and the Keel archive, nothing else

    Images are built from Debian and the Keel archive only, with no TurnKey archive. The TurnKey tools Keel still uses, turnkey-ssl, netinfo, conffile, sysinfo, version, tkl-installer and the dhcpcd glue, are now Keel forks, and turnkey-ssl no longer ships a key. TKLBAM and the TurnKey Hub agent are gone; Keel Backup and Keel Cloud replace them later.

  • Keel Web serves HTTPS from the start

    In simple mode Keel Web serves HTTPS out of the box with the machine's own certificate, a Keel placeholder page and Keel error pages. Let's Encrypt is set up from the console, prefilled from the instance spec. Cloud mode adds the Coraza WAF and the Anubis bot challenge.

  • A first boot that finishes

    First boot asks for the FQDN and offers to keep a root password the hypervisor already set. Unattended, it runs to the end without hanging, and it installs security updates from Debian Security without ever blocking the boot. systemctl --failed is clean in containers.

  • The mesh: two nodes so far

    Phase 5 has started. Two Keel Web nodes in different locations are joined over a WireGuard overlay, declared in each node's instance spec and confirmed through the network safety window. Next is a one-command join, keel mesh invite then keel mesh join (decision 0048, in review), and etcd forms at the third node.

    How the mesh works

Status

In testing, and plain about it

There is no release for end users. The current images are signed testing pre-releases, not meant for production: Keel Core 19.0-8 and Keel Web 19.0-3, built on 3 October 2026. Each is a .tar.zst for LXC, on its GitHub release and on mirror.keellinux.org, and listed in the Proxmox template index. Decision 0043 makes the ISO the second release format.

Packages come from archive.keellinux.org, signed, on a testing track that everything reaches first and a stable track the maintainer promotes to.

The build host reproduces unmodified TurnKey 19.0 from the forked repositories, which is the baseline every change is measured against. keel inspect, keel diff and keel spec apply run on the test images, and a MariaDB pair, with seeding and a read-only replica, is mostly done in keel 0.11.

The composition architecture on this site was decided on 30 September 2026. Where a point says decided or goal, it is on the roadmap and not yet in the code.

The focus now

  1. P5
    The mesh

    Two Keel Web nodes are joined over WireGuard, declared in each node's spec. Next: a one-command join, and etcd at the third node.

  2. P1
    Keel Core and Keel Web

    In testing as signed pre-releases, with the appliance manifest. Keel Web serves HTTPS in simple mode; Coraza and Anubis come with cloud mode.

  3. P4
    WordPress on Keel PHP

    The first application built from a manifest, with no Apache in the image.

Decisions: 0023 to 0040 on the tracker, and the manifest, tracker#39.

Read how it fits together

The architecture, the security model and the cloud are each on a page of their own, with every claim tied to the code or to a decision on the tracker.