ACME Server Install Guide

spork-acme-server 0.3.0-alpha.2 -- a standalone ACME certificate authority you can deploy on any Linux server in under five minutes. It implements RFC 8555 and works with certbot, acme.sh, Caddy, Traefik, and any other ACME client.

What is this? SPORK ACME Server is a private CA that speaks the ACME protocol (the same protocol behind Let's Encrypt). It lets you issue and manage TLS certificates for internal servers, dev/test environments, or air-gapped networks -- all from a single static binary with no external dependencies.

Table of Contents

  1. Requirements
  2. Download
  3. Install
  4. CA Setup (Interactive Wizard)
  5. Running the Server
  6. Client Configuration
  7. Quick Start Example
  8. CLI Reference
  9. Systemd Service
  10. Trust Helper Scripts
  11. Admin Web UI
  12. Troubleshooting

1. Requirements

RequirementDetails
Architecturex86_64 (amd64)
Operating SystemLinux (any distribution with kernel 4.x+)
RAM256 MB minimum, 512 MB recommended
Disk100 MB for binary + 1 GB for CA data
NetworkPort 6446 (ACME/HTTPS, configurable)
DependenciesNone -- statically linked against musl libc
PrivilegesRoot or sudo for installation and service management

Should work on any x86_64 Linux with kernel 4.x or later. Tested on Ubuntu 24.04 and Rocky Linux 9.

No OpenSSL required. The binary is pure Rust with no C dependencies. It does not link against glibc, libssl, or any shared library.

2. Download

curl -fSL -o spork-acme-installer \
  https://rayketcham.com/CRLs/sp0rk/static/spork-acme-server-0.3.0-alpha.2-linux-x86_64-installer

Verify Integrity

The installer is Ed25519-signed and contains embedded SHA3-256 checksums. Verify before running:

chmod +x spork-acme-installer
./spork-acme-installer --verify

If verification passes, the installer prints OK and exits with status 0. If it fails, do not proceed -- download a fresh copy.

3. Install

sudo ./spork-acme-installer

The installer does three things:

  1. Verifies the embedded SHA3-256 checksums and Ed25519 signature
  2. Extracts the spork-acme binary to /usr/local/bin/
  3. Launches the interactive setup wizard automatically
Non-destructive: If you already have an existing installation at /opt/spork-acme/, the installer detects it and presents an upgrade/configure/uninstall menu instead.

4. CA Setup (Interactive Wizard)

The setup wizard runs automatically after installation. You can also invoke it manually at any time:

sudo spork-acme

The wizard walks through the following steps:

4.1 Deployment Type

OptionDescriptionWhen to Use
2-Tier CARoot CA + Issuing CARecommended for most deployments
3-Tier CARoot + Policy CA + Issuing CAEnterprise / compliance requirements
Subordinate to Windows CAIssuing CA under existing AD CSHybrid Windows PKI environments
Import PFXMigrate from existing CAReplacing another CA product

4.2 Algorithm Selection

AlgorithmTypeNotes
ECDSA P-256ClassicalFastest, broadest client compatibility. Start here if unsure.
ECDSA P-384ClassicalHigher security margin, still broadly compatible.
RSA 2048 / 4096ClassicalLegacy device support only.
ML-DSA-65Post-QuantumNIST FIPS 204. Requires PQC-aware clients.
ML-DSA-65 + ECDSA P-256HybridComposite signature (RFC 9621). Quantum-safe with classical fallback.
ML-DSA-87 + ECDSA P-384HybridMaximum security for both classical and PQC.
SLH-DSA-SHA2-128sPost-QuantumHash-based signatures, conservative security assumptions.
For testing: Choose ECDSA P-256. It works with every client out of the box. You can always deploy a second CA with PQC later -- no need to decide up front.

4.3 Organization Details

The wizard prompts for your organization name, country, and state. These appear in the CA certificate Subject DN (e.g., CN=My Org Issuing CA, O=My Org, C=US).

4.4 Network Configuration

SettingDefaultDescription
Bind address0.0.0.0IP address to listen on
Port6446HTTPS port for ACME protocol
External URLhttps://hostname:6446URL clients use to reach this server

4.5 Lockbox Passphrase

All CA private keys are encrypted in a lockbox (AES-256-GCM with Argon2id key derivation). The wizard prompts you to create a passphrase:

Set lockbox passphrase (encrypts all CA private keys): ********
Confirm passphrase: ********
Save this passphrase. You need it every time the server starts. For systemd service mode, store it in an environment file (see Systemd Service below). There is no recovery mechanism -- if you lose the passphrase, you lose access to your CA private keys.

4.6 SuperAdmin Credentials

At the end of setup, the wizard generates a SuperAdmin mTLS certificate and displays credentials that are shown only once:

+==============================================================+
|    SAVE THESE CREDENTIALS NOW - SHOWN ONLY ONCE!             |
+==============================================================+
|  Algorithm:       ECDSA P-256                                |
|  PFX Password:    abc123xyz                                  |
|  Recovery Code:   XXXX-XXXX-XXXX-XXXX                        |
+==============================================================+
Record the PFX password and recovery code immediately. The SuperAdmin PFX file is saved to /opt/spork-acme/admin/superadmin.pfx. You need the PFX password to import it into your browser for admin UI access. These credentials cannot be recovered later.

5. Running the Server

After the wizard completes, the server is running and ready. If the wizard installed a systemd service, it starts automatically on boot.

Verify the Server

# Check service status
sudo systemctl status spork-acme

# Test the ACME directory endpoint
curl -k https://localhost:6446/acme/directory

Expected response:

{
  "newNonce": "https://your-server:6446/acme/new-nonce",
  "newAccount": "https://your-server:6446/acme/new-acct",
  "newOrder": "https://your-server:6446/acme/new-order",
  "revokeCert": "https://your-server:6446/acme/revoke-cert"
}

Manual Foreground Mode

For debugging or when not using systemd:

sudo SPORK_LOCKBOX_PASSPHRASE="your-passphrase" \
  spork-acme -s /opt/spork-acme --port 6446 --host 0.0.0.0

Management Menu

After initial setup, running spork-acme without a --state-dir flag detects the existing installation and presents a management menu:

sudo spork-acme
SPORK ACME Server v0.3.0-alpha.2
Existing installation detected at /opt/spork-acme/

  1) Configure    - Web UI, TLS certificates, proxy settings
  2) Backup       - Create/restore encrypted backups
  3) Security     - Audit, hardening, permissions, user management
  4) Upgrade      - Upgrade binary with full backup
  5) Status       - Service health and configuration review
  6) Uninstall    - Clean removal with optional backup

Select an option [1-6]:

6. Client Configuration

Before ACME clients can verify your server's TLS certificate and the certificates it issues, they need to trust your CA.

Install the CA Certificate

The CA root certificate is served at https://your-server:6446/ca.crt (PEM) and https://your-server:6446/ca.cer (DER).

Debian / Ubuntu

sudo curl -fsSk https://your-server:6446/ca.crt \
  -o /usr/local/share/ca-certificates/spork-ca.crt
sudo update-ca-certificates

Rocky Linux / RHEL / CentOS / Fedora

sudo curl -fsSk https://your-server:6446/ca.crt \
  -o /etc/pki/ca-trust/source/anchors/spork-ca.pem
sudo update-ca-trust

Alpine Linux

sudo curl -fsSk https://your-server:6446/ca.crt \
  -o /usr/local/share/ca-certificates/spork-ca.crt
sudo update-ca-certificates

Windows (PowerShell, as Administrator)

# Download the trust helper script
Invoke-WebRequest -Uri "https://your-server:6446/ca.cer" -OutFile "$env:TEMP\spork-ca.cer" -SkipCertificateCheck
Import-Certificate -FilePath "$env:TEMP\spork-ca.cer" -CertStoreLocation Cert:\LocalMachine\Root

Using Trust Helper Scripts

SPORK includes platform-specific trust helper scripts for automated CA trust installation:

# Linux (auto-detects distro)
sudo ./install-ca-cert.sh https://your-server:6446

# Windows PowerShell (as Administrator)
.\Install-CACert.ps1 -AcmeUrl https://your-server:6446

# Windows PowerShell (current user only, no admin)
.\Install-CACert.ps1 -AcmeUrl https://your-server:6446 -CurrentUser
The -k / --insecure flag is needed for the initial CA cert download because the server's TLS certificate is signed by the CA you are trying to install. After trusting the CA, all subsequent connections verify normally.

Configure Certbot

Once the CA is trusted system-wide, certbot works without any special flags:

certbot certonly --standalone \
  --server https://your-server:6446/acme/directory \
  --agree-tos \
  --email admin@example.com \
  -d myhost.example.com

If you have not yet installed the CA certificate, add --no-verify-ssl:

certbot certonly --standalone \
  --server https://your-server:6446/acme/directory \
  --no-verify-ssl \
  --agree-tos \
  -d myhost.example.com

Configure acme.sh

acme.sh --issue \
  --server https://your-server:6446/acme/directory \
  -d myhost.example.com \
  --standalone

Automatic Renewal

Certbot's built-in renewal works as expected:

# Enable the certbot timer
sudo systemctl enable --now certbot.timer

# Or add a cron entry
0 0 * * * certbot renew --quiet

7. Quick Start Example

End-to-end: download the installer, set up a CA, issue your first certificate.

Step 1: Install

curl -fSL -o spork-acme-installer \
  https://rayketcham.com/CRLs/sp0rk/static/spork-acme-server-0.3.0-alpha.2-linux-x86_64-installer
chmod +x spork-acme-installer
sudo ./spork-acme-installer

Step 2: Complete the Setup Wizard

The wizard starts automatically. Accept the defaults or customize:

Save the SuperAdmin PFX password and recovery code displayed at the end. They are shown only once and cannot be recovered.

Step 3: Verify the Server

curl -k https://localhost:6446/acme/directory

Step 4: Trust the CA

# Debian/Ubuntu
sudo curl -fsSk https://localhost:6446/ca.crt \
  -o /usr/local/share/ca-certificates/spork-ca.crt
sudo update-ca-certificates

# Rocky/RHEL
sudo curl -fsSk https://localhost:6446/ca.crt \
  -o /etc/pki/ca-trust/source/anchors/spork-ca.pem
sudo update-ca-trust

Step 5: Issue Your First Certificate

# Install certbot if needed
sudo apt install certbot    # Debian/Ubuntu
sudo dnf install certbot    # Rocky/RHEL

# Issue a certificate
certbot certonly --standalone \
  --server https://localhost:6446/acme/directory \
  --agree-tos \
  --email admin@example.com \
  -d myhost.example.com

Your certificate is now at:

Configure your web server (nginx, Apache, etc.) to use these files, and enable the certbot.timer for automatic renewal.

8. CLI Reference

Subcommands and Modes

InvocationBehavior
spork-acmeAuto-detect: setup wizard if no installation, management menu if installed
spork-acme -s /opt/spork-acmeStart server with explicit state directory
spork-acme --statusShow installation status
spork-acme --uninstallRemove an existing installation
spork-acme --update-crlRegenerate the CRL (run via timer)
spork-acme --renew-tlsRenew the server's own TLS certificate if expiring
spork-acme --install-serviceInstall the systemd service unit

Server Flags

FlagDefaultDescription
-s, --state-dir(required)Path to the CA state directory (keys, certs, database)
-p, --port8080HTTPS port to listen on
--host0.0.0.0IP address to bind to
--external-url(auto)Public URL for ACME responses (e.g., https://acme.example.com:6446)
--tls-cert(auto)Path to TLS certificate file (PEM)
--tls-key(auto)Path to TLS private key file (PEM)
--allow-wildcardsdisabledAllow wildcard certificates (requires DNS-01 validation)
--dns-serversystem defaultCustom DNS server for challenge validation (e.g., 192.168.1.1:53)
--terms-of-service(none)URL to terms of service document
--website(none)URL to CA website (included in ACME directory)
--caa-identity(none)CAA identity for CAA record checking (repeatable)
--cdp-url(none)CRL Distribution Point URL embedded in issued certificates
--aia-base-url(none)AIA base URL for OCSP and CA Issuer URLs in issued certificates
--http-challenge-dir(none)Directory for HTTP-01 webroot challenge files
--auto-validatedisabledAuto-validate challenges (testing only, INSECURE)
--log-levelinfoLog level: trace, debug, info, warn, error
-y, --yesdisabledAuto-accept all confirmation prompts (scripted/automated setup)

Environment Variables

VariableDescription
SPORK_LOCKBOX_PASSPHRASELockbox passphrase for non-interactive startup (systemd, scripts)
SPORK_KEYSTORE_PASSPHRASEDeprecated alias for SPORK_LOCKBOX_PASSPHRASE

9. Systemd Service

The setup wizard can install the systemd service automatically. To install it manually:

sudo spork-acme --install-service

Or create the unit file by hand:

Service Unit

[Unit]
Description=SPORK ACME Server - Private Certificate Authority
Documentation=https://github.com/rayketcham/spork-ca
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/spork-acme -s /opt/spork-acme --port 6446 --host 0.0.0.0 --external-url https://your-server:6446
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=5
User=root
WorkingDirectory=/opt/spork-acme

# Lockbox passphrase (use EnvironmentFile for production)
EnvironmentFile=/etc/spork-acme/env

# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/spork-acme
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Passphrase Environment File

# Create the passphrase file
sudo mkdir -p /etc/spork-acme
sudo tee /etc/spork-acme/env > /dev/null <<'EOF'
SPORK_LOCKBOX_PASSPHRASE=your-passphrase-here
EOF
sudo chmod 600 /etc/spork-acme/env
sudo chown root:root /etc/spork-acme/env
Protect the passphrase file. It must be owned by root with mode 0600. Never commit it to version control or include it in backups sent off-host.

CRL Timer

[Unit]
Description=SPORK ACME CRL Update

[Service]
Type=oneshot
ExecStart=/usr/local/bin/spork-acme --update-crl -s /opt/spork-acme
EnvironmentFile=/etc/spork-acme/env
[Unit]
Description=SPORK ACME CRL Update Timer

[Timer]
OnCalendar=*-*-* 00/6:00:00
Persistent=true

[Install]
WantedBy=timers.target

Enable and Start

sudo systemctl daemon-reload
sudo systemctl enable --now spork-acme.service
sudo systemctl enable --now spork-acme-crl.timer

10. Trust Helper Scripts

SPORK ships platform-specific scripts that automate installing the CA root certificate into the operating system trust store.

Linux: install-ca-cert.sh

Auto-detects distro (Debian, RHEL, Alpine) and installs the CA certificate:

sudo ./install-ca-cert.sh https://your-server:6446

The script:

  1. Downloads the CA certificate from https://your-server:6446/ca.crt
  2. Validates the PEM format
  3. Displays the certificate subject, issuer, and fingerprint
  4. Copies it to the correct trust store path for your distro
  5. Runs update-ca-certificates or update-ca-trust

Windows: Install-CACert.ps1

Installs the CA certificate into the Windows certificate store:

# LocalMachine\Root (requires Administrator)
.\Install-CACert.ps1 -AcmeUrl https://your-server:6446

# CurrentUser\Root (no admin required)
.\Install-CACert.ps1 -AcmeUrl https://your-server:6446 -CurrentUser

The script downloads the DER certificate from /ca.cer, removes any existing certificates with the same Subject to prevent duplicates, and installs the new certificate.

11. Admin Web UI

The admin portal is served at https://your-server:6446/admin and is protected by mutual TLS (mTLS). Only clients presenting a valid admin certificate can access it.

Access Setup

  1. Copy /opt/spork-acme/admin/superadmin.pfx to your workstation
  2. Import the PFX into your browser (Firefox: Settings > Certificates > Import; Chrome: Settings > Security > Manage Certificates > Import)
  3. Enter the PFX password from the setup wizard
  4. Navigate to https://your-server:6446/admin

Admin Roles

RoleCapabilities
SuperAdminFull control: issue admin certs, factory reset, all operations
AdminRevoke certificates, manage CRLs, issue certificates
OperatorIssue certificates, view audit logs
ViewerRead-only access to dashboard and certificate inventory

12. Troubleshooting

Server will not start

# Check logs
sudo journalctl -u spork-acme -n 50 --no-pager

# Common cause: missing or wrong passphrase
# Verify the env file exists and is correct
sudo cat /etc/spork-acme/env

Port already in use

sudo ss -tlnp | grep 6446

Kill the conflicting process or change the port with --port.

Certbot SSL verification error

Add --no-verify-ssl or install the CA certificate to the system trust store first (see Client Configuration). Alternatively, point certbot at the CA cert directly:

export REQUESTS_CA_BUNDLE=/opt/spork-acme/ca/ca.crt
certbot certonly --standalone \
  --server https://localhost:6446/acme/directory \
  --agree-tos -d test.example.com

SELinux denials (Rocky / RHEL)

# Check for denials
sudo ausearch -m avc -ts recent

# Fix binary context after copy
sudo restorecon -v /usr/local/bin/spork-acme

# If further denials, generate a policy module
sudo ausearch -m avc -ts recent | audit2allow -M spork-acme-local
sudo semodule -i spork-acme-local.pp

Firewall blocking connections

# Rocky / RHEL / CentOS
sudo firewall-cmd --add-port=6446/tcp --permanent
sudo firewall-cmd --reload

# Ubuntu / Debian (if UFW is active)
sudo ufw allow 6446/tcp

No TTY error in systemd

The server requires the lockbox passphrase via environment variable when running non-interactively. Ensure SPORK_LOCKBOX_PASSPHRASE is set in the EnvironmentFile:

sudo grep SPORK_LOCKBOX_PASSPHRASE /etc/spork-acme/env

CRL timer not running

sudo systemctl status spork-acme-crl.timer
sudo systemctl enable --now spork-acme-crl.timer

Directory Structure

After installation, the state directory at /opt/spork-acme/ contains:

PathPurpose
ca/CA certificates (root, issuing, chain)
tls/Server TLS certificate and key
admin/Admin PFX certificates
keystore.dbEncrypted lockbox containing all CA private keys
config.jsonServer configuration
acme.dbACME accounts, orders, and certificate records
backups/Automatic backup snapshots