step-ca (Smallstep) is a private, ACME-compatible certificate authority — useful for issuing internal TLS certs to services like Warpgate, Coroot, internal APIs, etc.
Assumptions: Ubuntu 24.04 LTS RamNode VPS, a dedicated internal hostname (e.g. ca.internal.example.com), root or sudo access.
1. Prerequisites
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl ufwCreate a dedicated non-root user to run the CA:
sudo useradd -r -m -d /etc/step-ca -s /usr/sbin/nologin step2. Install step CLI and step-ca
curl -fsSL https://dl.smallstep.com/gh-release/cli/gh-release-header/latest/step-cli_amd64.deb -o step-cli.deb
curl -fsSL https://dl.smallstep.com/gh-release/certificates/gh-release-header/latest/step-ca_amd64.deb -o step-ca.deb
sudo dpkg -i step-cli.deb
sudo dpkg -i step-ca.debVerify:
step version
step-ca version(If the download URLs above have changed, grab the latest .deb links from https://github.com/smallstep/cli/releases and https://github.com/smallstep/certificates/releases.)
3. Initialize the CA
Run as the step user so file ownership/permissions are correct:
sudo -u step step ca init \
--name "Example Internal CA" \
--dns "ca.internal.example.com" \
--address ":9000" \
--provisioner "admin@example.com" \
--password-file /dev/stdin <<< "changeme-strong-password"This creates /etc/step-ca (or ~/.step for the step user, typically /etc/step-ca/.step) containing:
config/ca.json— main CA configconfig/defaults.jsoncerts/root_ca.crt,certs/intermediate_ca.crtsecrets/root_ca_key,secrets/intermediate_ca_key(encrypted with the password you set)
Store the CA password somewhere safe (a password manager or secrets vault) — you'll need it any time the CA process restarts, unless you configure a password file (see below).
To avoid typing the password on every restart, save it to a root-only file:
sudo bash -c 'echo "changeme-strong-password" > /etc/step-ca/password.txt'
sudo chown step:step /etc/step-ca/password.txt
sudo chmod 600 /etc/step-ca/password.txt4. Add an ACME provisioner (optional but recommended)
This lets other services (Caddy, nginx with acme.sh, Warpgate, etc.) request certs automatically:
sudo -u step step ca provisioner add acme --type ACMERestart the CA after adding provisioners (see systemd setup below).
5. Run step-ca as a systemd service
sudo tee /etc/systemd/system/step-ca.service > /dev/null <<'EOF'
[Unit]
Description=step-ca certificate authority
After=network.target
[Service]
Type=simple
User=step
Group=step
ExecStart=/usr/bin/step-ca /etc/step-ca/config/ca.json --password-file /etc/step-ca/password.txt
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now step-ca
sudo systemctl status step-caAdjust the
ExecStartpath ifstep ca initplaced config under/home/step/.stepinstead of/etc/step-ca— check withsudo -u step step path.
6. Firewall
sudo ufw allow OpenSSH
sudo ufw allow 9000/tcp # step-ca HTTPS API/ACME port
sudo ufw enableIf the CA should only be reachable from your internal network/VPN (recommended), restrict with ufw allow from <trusted-subnet> to any port 9000 instead of a blanket allow.
7. Distribute the root certificate to clients
On any machine that needs to trust certs issued by this CA:
step ca bootstrap --ca-url https://ca.internal.example.com:9000 --fingerprint <root-ca-fingerprint>Get the fingerprint from the CA host:
sudo -u step step certificate fingerprint /etc/step-ca/certs/root_ca.crtTo trust it system-wide on a Debian/Ubuntu client:
sudo cp $(step path)/certs/root_ca.crt /usr/local/share/ca-certificates/internal-root-ca.crt
sudo update-ca-certificates8. Issue a certificate for another service (example: Warpgate or Coroot's reverse proxy)
step ca certificate "warpgate.internal.example.com" warpgate.crt warpgate.key \
--ca-url https://ca.internal.example.com:9000 \
--root root_ca.crtFor automatic renewal, step-cli has a step ca renew --daemon mode, or use the ACME provisioner with a client like acme.sh/Caddy pointed at https://ca.internal.example.com:9000/acme/acme/directory.
9. Backups
Back up (securely, encrypted) at minimum:
secrets/root_ca_keyandsecrets/intermediate_ca_keyconfig/ca.json- The CA password
Losing the root key means every issued cert becomes unrenewable and every client's trust bootstrap must be redone.
10. Troubleshooting
| Symptom | Likely cause |
|---|---|
step-ca won't start | Wrong --password-file path, or ca.json path mismatch — check journalctl -u step-ca |
| Clients can't validate certs | Root CA not installed in client trust store |
| ACME clients fail | Confirm provisioner was added and CA restarted; check port 9000 reachable |
| Permission denied on secrets | Ensure step user owns /etc/step-ca recursively (chown -R step:step) |
