Use Step CA as an ACME Server (ACME v2) and Issue/Renew TLS Certificates with Certbot + NGINX

This guide enables ACME v2 on an existing Smallstep Step CA deployment, then shows how a host like test.maksonlee.com can trust your private CA, request/renew TLS certificates with Certbot, and let Certbot’s NGINX plugin automatically enable HTTPS on the default NGINX site.


This Post Is Based On


Lab Context

  • Step CA host: stepca.maksonlee.com
  • Client host: test.maksonlee.com
  • OS: Ubuntu 24.04
  • Step CA server path: /etc/step-ca
  • Step CA config: /etc/step-ca/config/ca.json
  • ACME provisioner name: acme-internal
  • ACME directory URL:
https://stepca.maksonlee.com/acme/acme-internal/directory

What You’ll Build

  • Enable ACME v2 on Step CA
  • Install step-cli on the client and trust the Root CA
  • Install NGINX (default site)
  • Use Certbot + NGINX plugin to:
    • register an ACME account,
    • request a TLS certificate from Step CA,
    • and automatically configure NGINX for HTTPS + HTTP→HTTPS redirect
  • Confirm Certbot’s auto-renewal timer and test renewal
  • Verify the certificate chain and SAN using OpenSSL

Prerequisites

  • Step CA is already running and reachable at:
https://stepca.maksonlee.com
  • DNS: test.maksonlee.com resolves to the client host.
  • This post uses HTTP-01 challenges. test.maksonlee.com must accept inbound TCP/80 during issuance/renewal.
    If you can’t expose port 80, use DNS-01 instead.

  1. Enable ACME on Step CA

Run on stepca.maksonlee.com:

sudo -i
export STEPPATH=/etc/step-ca

step ca provisioner add acme-internal --type ACME \
  --ca-config /etc/step-ca/config/ca.json \
  --ca-url https://stepca.maksonlee.com \
  --root /etc/step-ca/certs/root_ca.crt \
  --password-file /etc/step-ca/password.txt

sudo systemctl kill -s HUP step-ca

ACME Directory URL (clients will use this):

https://stepca.maksonlee.com/acme/acme-internal/directory

  1. Install step-cli on the Client

Run on test.maksonlee.com.

Install dependencies:

sudo apt update && sudo apt install -y --no-install-recommends curl gpg ca-certificates

Add Smallstep’s apt signing key:

sudo install -d -m 0755 /etc/apt/keyrings
sudo curl -fsSL https://packages.smallstep.com/keys/apt/repo-signing-key.gpg \
  -o /etc/apt/keyrings/smallstep.asc
sudo chmod 0644 /etc/apt/keyrings/smallstep.asc

Add the Smallstep apt repository:

sudo tee /etc/apt/sources.list.d/smallstep.sources >/dev/null <<'EOF'
Types: deb
URIs: https://packages.smallstep.com/stable/debian
Suites: debs
Components: main
Signed-By: /etc/apt/keyrings/smallstep.asc
EOF

Install step-cli:

sudo apt update && sudo apt install -y step-cli

  1. Bootstrap Trust on the Client and Install the Root CA

Get the Root CA fingerprint:

curl -fsSk https://stepca.maksonlee.com/roots.pem | step certificate fingerprint

Bootstrap the client context (replace <FINGERPRINT> with the value above):

step ca bootstrap \
  --ca-url https://stepca.maksonlee.com \
  --fingerprint <FINGERPRINT>

Install the Root CA into the OS trust store:

sudo step certificate install ~/.step/certs/root_ca.crt

Verify the ACME directory endpoint from the client:

curl -fsS https://stepca.maksonlee.com/acme/acme-internal/directory | head

Expected output: JSON containing endpoints like newNonce, newAccount, newOrder, etc.


  1. Install NGINX
sudo apt update
sudo apt install -y nginx
sudo systemctl enable --now nginx

Confirm HTTP works (default page):

curl -fsS http://127.0.0.1/ | head

  1. Install Certbot + NGINX Plugin
sudo apt update
sudo apt install -y certbot python3-certbot-nginx

  1. Request a Certificate from Step CA and Let Certbot Configure NGINX

This command:

  • registers an ACME account (first run),
  • requests the certificate from Step CA,
  • enables HTTPS in NGINX,
  • and configures HTTP → HTTPS redirect.

Replace the email address:

sudo certbot --nginx -n \
  -d test.maksonlee.com \
  --server https://stepca.maksonlee.com/acme/acme-internal/directory \
  --agree-tos --email you@maksonlee.com \
  --redirect

Certificates are stored at:

/etc/letsencrypt/live/test.maksonlee.com/fullchain.pem
/etc/letsencrypt/live/test.maksonlee.com/privkey.pem

Verify HTTPS:

curl -fsS https://test.maksonlee.com/ | head

Expected: HTML for the default “Welcome to nginx!” page.


  1. Verify Renewal and the Issued Certificate

Confirm Certbot auto-renewal is scheduled (systemd timer)

systemctl status certbot.timer --no-pager
systemctl list-timers | grep certbot || true

Expected: certbot.timer is active (waiting) and has a future trigger time.

Simulate renewal against your Step CA ACME directory

sudo certbot renew --dry-run \
  --server https://stepca.maksonlee.com/acme/acme-internal/directory

Expected: “all simulated renewals succeeded”.

Verify the certificate chain (client-side)

sudo openssl verify \
  -CApath /etc/ssl/certs \
  -untrusted /etc/letsencrypt/live/test.maksonlee.com/chain.pem \
  /etc/letsencrypt/live/test.maksonlee.com/cert.pem

Expected: cert.pem: OK

Show issuer, validity period, and SAN

sudo openssl x509 -in /etc/letsencrypt/live/test.maksonlee.com/cert.pem \
  -noout -issuer -dates -ext subjectAltName

Expected:

  • Issuer = your Step CA Intermediate
  • SAN includes DNS:test.maksonlee.com
  • notAfter matches your short-lived policy

Confirm NGINX is presenting the expected certificate

echo | openssl s_client -connect test.maksonlee.com:443 -servername test.maksonlee.com 2>/dev/null | \
  openssl x509 -noout -issuer -dates -ext subjectAltName

Expected: same issuer / dates / SAN as the cert.pem output above.


Summary

You now have Step CA acting as an ACME v2 server, a client host that trusts your private PKI, Certbot issuing and renewing certs, and NGINX automatically configured for HTTPS using your Step CA-issued certificate:

https://stepca.maksonlee.com/acme/acme-internal/directory

Did this guide save you time?

Support this site

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top