Deptool

Deptool is a declarative configuration deployment tool. It manages configuration files on a cluster of unix hosts reachable over SSH. Deptool is designed for smallish clusters (1–50 hosts) managed by a small group of operators (1–5 people) who run deptool deploy from their local machine. Deptool is:

Declarative. You define the desired state of your cluster, Deptool materializes that state. When you stop defining a file, Deptool removes it. No more lingering files or explicit cleanup tasks.

Daemonless. Deptool requires no installation, enrolment, or daemons on the target host. The only prerequisite is the ability to open an SSH connection from the operator’s machine.

Fast. Deptool only connects to hosts that are affected by a change, which it can determine statically. It deploys against hosts in parallel, and merely pushes files and optionally manages systemd units. Updating a configuration file across the cluster can be done sub-second.

Safe. Deptool computes a deployment plan ahead of time. You can view the full diff before even connecting. Deptool can roll back automatically in case a systemd unit fails to start after a deploy, and it prevents concurrent deploys with host-level locks. Deployment is idempotent, so a deploy that is interrupted half-way can safely be retried.

Opinionated, but simple. Deptool manages files, it does not allow executing arbitrary commands that mutate your system. It is not a generic tool that tries to solve every deployment problem for everybody. Deptool works best when you can configure applications to read config from /var/lib/deptool/…/current, which is where Deptool materializes deployed config files. For applications that demand files in particular locations, Deptool can create symlinks.

Unidirectional. Deptool pushes configuration from the operator machine to target hosts. Aside from tracking the deployed version, Deptool does not transport information from target hosts back to the operator.

A building block, not a full configuration management solution. Deptool does not generate the files it deploys, it has no support for templating or control flow. Cluster configuration should be generated by a separate tool such as Nix, RCL, or custom configure scripts.

Example

This is what a typical Deptool run looks like, in this case when updating DNS records on a cluster running NSD:

$ deptool deploy
s4.ruuda.nl
    update nsd
        ~ zones/ruuda.nl.zone
        restart unit nsd.service

s5.ruuda.nl
    update nsd
        ~ zones/ruuda.nl.zone
        restart unit nsd.service

Auto-rollback if deploy fails.
Apply to 2 hosts in cluster 'prod'? [y/N/d] y

  s4.ruuda.nl: done
  s5.ruuda.nl: done

Changes deployed successfully to 2 hosts in 0.99s.

Workflow

Deptool expects configuration for your entire cluster materialized in a directory, the config tree. By convention this directory is named after the cluster, for example prod. You can maintain this directory tree by hand, but for more complex configurations, you probably want to generate it using an external tool.

To deploy, run deptool deploy prod. This commits the desired cluster state into Deptool’s store. From the store it determines which hosts are affected and computes a deployment plan, completely offline. After you accept this plan, Deptool connects to the affected hosts and applies the new state there.

How it works

Deptool stores configuration in its store: a Git repository with a two-tier directory structure: top-level directories define hosts, second-level directories define apps. For example:

dns01
└── nsd
    ├── example.com.zone
    ├── manifest.json
    ├── nsd.conf
    └── systemd
        └── nsd.service
web01
└── nginx
    ├── manifest.json
    ├── sites-enabled
    │   └── example.com.conf
    └── systemd
        └── nginx.service

Per host, Deptool materializes the app directories in /var/lib/deptool/apps. It then manages systemd units if any are included, and it can create symlinks on the filesystem that point into /var/lib/deptool, as defined in the manifest, manifest.json. See also the directory layout and manifest reference.

On the operator machine, Deptool keeps a remote-tracking ref per target host. This way it knows what is deployed on that host, and whether a new commit affects any of the host’s apps. This also enables it to show a full diff ahead of time. For a single operator deploying from a single store, the remote-tracking refs are always up to date. This means Deptool never initiates any superfluous connections, which keeps it fast.

For multiple operators collaboratively managing a cluster, an operator’s local refs may provide an outdated view of the cluster. Deptool detects this before applying any changes: it locks every host with the intent of applying the plan as accepted. If during locking it turns out the plan was stale, Deptool fetches missing commits so it can re-plan with the latest information. At a team size where this opportunistic deployment workflow starts suffering from contention, Deptool is suitable for running centrally from a single store, integrated into a GitOps pipeline.

Acknowledgements

Deptool takes inspiration from Nix, in particular the idea of generating machine configuration ahead of time, storing it in a store where multiple versions can coexist, and atomically swapping symlinks to activate new versions.

Deptool takes inspiration from OpenTofu, in particular the idea of having a separate plan and apply phase, so that the operator can inspect and accept exactly what will be deployed before applying any destructive changes.

Deptool learns from Ansible’s pitfalls. Deptool requires all configuration to be rendered ahead of time, it does not render templates on the host based on properties discovered on the host, and it does not have any control flow. This enables Deptool to statically compute an exact deployment plan. Deptool tracks what it previously deployed, so it knows what to clean up. Deptool is a static binary that automatically caches itself on the target host; it does not make assumptions about interpreter versions, and it does not send over megabytes of Python libraries on every run. Deptool is parallel by default, with only a single SSH connection per host. And most of all, Deptool avoids yaml.