Back to Articles

acmetool: The Make-Like ACME Client That Treats Certificates as Declarative State

[ View on GitHub ]

acmetool: The Make-Like ACME Client That Treats Certificates as Declarative State

Hook

Most ACME clients treat certificate acquisition like a script you run. acmetool treats it like a build system—you declare what certificates you want, and it ensures they exist, just like make ensures your binaries are up to date.

Context

When Let's Encrypt launched in 2015, it democratized SSL/TLS certificates but introduced a new operational challenge: certificate lifecycle management. The official client (now Certbot) took an opinionated approach—it would automatically detect your webserver, modify configurations, and handle everything for you. This "helpful" automation was a double-edged sword. For system administrators managing complex production environments with hand-tuned configurations, having a client rewrite nginx or Apache configs was terrifying.

acmetool emerged as a philosophical counterpoint to this approach. Built by Hugo Landau in Go, it embraces the Unix philosophy: do one thing well, maintain transparency, and never surprise the operator. Instead of trying to be smart about webserver integration, acmetool focuses purely on certificate acquisition and lifecycle management, storing everything in a documented filesystem structure that you can inspect, version control, and reason about. It's designed for operators who want their automation predictable, not magical.

Technical Insight

Declare intent

Check expiry

Certificate needed

Challenge

Proof

Issue certificate

Trigger on change

Reload signal

Serve from

/var/lib/acme/desired

Certificate Declarations

Reconcile Engine

(State Comparison)

/var/lib/acme/live

Active Certificates

ACME Server

(Let's Encrypt)

HTTP-01 Validator

(Webroot/Proxy/Standalone)

Hook System

(Post-Acquisition Scripts)

Web Server

(Nginx/Apache)

System architecture — auto-generated

The genius of acmetool lies in its state-based architecture. At its core is a simple directory structure at /var/lib/acme (configurable) that acts as the source of truth. When you want a certificate, you don't run a command with flags—you declare your intent by creating a file in the desired directory:

# Declare that you want a certificate for these hostnames
echo 'example.com www.example.com' > /var/lib/acme/desired/example.com

# Run the reconciliation
sudo acmetool reconcile

The reconcile command is the heart of acmetool's design. Like make, it examines the current state (what certificates exist, which are expired or expiring soon) and compares it to the desired state (what certificate files exist in desired). It then performs only the minimum necessary work to bring reality in line with your declarations. Run it ten times, get the same result—pure idempotence.

This filesystem-based state model has profound implications. Your certificate policy becomes visible, version-controllable configuration. Want to know what certificates a server should have? ls /var/lib/acme/desired. Want to add that to Git? Go ahead—it's just text files. Want to deploy certificates with configuration management? Point Ansible or Salt at the desired directory and let acmetool handle the rest.

The validation method flexibility showcases careful architectural thinking. acmetool supports HTTP-01 challenges through multiple strategies without requiring you to modify existing webserver configs. The most elegant is the proxy mode:

# Configure acmetool to use webroot validation
sudo acmetool quickstart
# Select "PROXY" mode during interactive setup

# In your nginx config, add this to port 80 server blocks:
location /.well-known/acme-challenge/ {
    proxy_pass http://127.0.0.1:402;
}

Now acmetool runs a challenge responder on port 402, and you've added exactly one nginx directive. No file permission wrestling, no webroot path confusion, no files appearing in your DocumentRoot. The ACME server makes a request to your domain on port 80, nginx proxies just the challenge path to acmetool, validation succeeds. Your nginx config remains under your control.

For post-acquisition actions, acmetool implements a hook system that's refreshingly straightforward—it's just executable files in /usr/lib/acme/hooks:

#!/bin/bash
# /usr/lib/acme/hooks/reload-nginx

EVENT_NAME="$1"

if [ "$EVENT_NAME" = "live-updated" ]; then
    # A certificate was updated and is now live
    systemctl reload nginx
fi

The tool invokes these hooks with environment variables describing what changed. No plugin APIs to learn, no DSLs—just shell scripts that receive events. This simplicity means you can integrate with any service manager, notification system, or deployment pipeline with a few lines of bash.

The certificate storage structure itself demonstrates thoughtful design. Certificates live in /var/lib/acme/live/HOSTNAME/, with separate directories for private keys (privkey), full chains (fullchain), and individual components. Crucially, these are symlinks to immutable content-addressed files in /var/lib/acme/certs/. When a certificate renews, acmetool writes the new certificate to a new file and atomically updates the symlink. Your webserver continues serving the old certificate until you reload it—zero-downtime by design, no race conditions where someone catches you mid-write.

Gotcha

The project's maintenance status is the elephant in the room. The last significant commit was years ago, and while ACME v2 support exists, newer protocol extensions or edge cases may not be handled. The Let's Encrypt ecosystem moves forward—new validation methods, certificate transparency requirements, account management features—and acmetool might not keep pace. For production systems where you need guaranteed long-term support and rapid response to ACME server changes, this creates risk.

The minimalist philosophy also means you're doing more manual integration work. While acmetool won't break your configs, it also won't set them up for you. You need to understand your webserver's challenge validation options, configure the appropriate proxy or webroot settings, and set up reload hooks yourself. Certbot will attempt to do all this automatically (for better or worse). If you're managing hundreds of diverse servers or need onboarding to be foolproof for junior operators, acmetool's DIY approach becomes operationally expensive. The transparency and control are features for experienced sysadmins but can feel like missing features for teams wanting turnkey solutions.

Verdict

Use if: you're managing servers where configuration stability matters more than convenience, you want certificate state you can version control and reason about, you value idempotent operations and transparent state storage, or you're integrating ACME into existing automation and need predictable behavior without side effects. It's ideal for experienced system administrators who know their stack and want a tool that respects that expertise. Skip if: you need cutting-edge ACME features or guaranteed active development, you're managing a large fleet where the setup overhead multiplies painfully, you want automatic webserver configuration without manual integration work, or you're uncomfortable deploying tools that aren't actively maintained. For those cases, stick with Certbot despite its complexity, or consider newer options like Caddy for greenfield deployments where built-in automation shines.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/automation/hlandau-acmetool.svg)](https://starlog.is/api/badge-click/automation/hlandau-acmetool)