The Problem with HomeLab SSL
Stop clicking ‘Advanced’ and ‘Proceed anyway.’ If your HomeLab has grown to more than 10 services, managing self-signed certificates or ignoring browser warnings isn’t just annoying—it is a security gap. Every time I deployed a new dashboard or a media server like Jellyfin, I faced that glaring red warning. For a long time, I just dealt with the friction. But as my lab expanded to 25+ containers, the manual overhead became a breaking point.
You might wonder why Let’s Encrypt isn’t the default answer. While Let’s Encrypt is the gold standard for public websites, it requires public DNS records and often an open port for validation. In a local-only environment, exposing your internal infrastructure just for a certificate is an unnecessary risk. Learning to manage your own Public Key Infrastructure (PKI) allows you to mimic production security without any external dependencies.
What is Step-ca?
Step-ca (from Smallstep) is an open-source, private Certificate Authority. It functions like a local version of Let’s Encrypt. Because it supports the ACME protocol, servers like Nginx, Caddy, or Apache can request and renew certificates automatically. It is incredibly lightweight, typically consuming less than 120MB of RAM. You get full automation without needing a public domain or internet access.
Phase 1: Installing the Step CLI and CA
Running this on a dedicated Linux VM or a Proxmox LXC container is the most stable approach. We will start by installing the step CLI, which serves as the primary management tool for your certificates.
1. Install the Step CLI
wget https://github.com/smallstep/cli/releases/download/v0.28.2/step-cli_0.28.2_amd64.deb
sudo dpkg -i step-cli_0.28.2_amd64.deb
2. Install the Step CA Server
wget https://github.com/smallstep/certificates/releases/download/v0.28.1/step-ca_0.28.1_amd64.deb
sudo dpkg -i step-ca_0.28.1_amd64.deb
Phase 2: Initializing Your Private CA
Configuration begins with defining your internal hierarchy. You need to initialize the CA with a name and a local DNS address. I recommend using a dedicated local domain like .internal or .lan to avoid conflicts.
step ca init \
--name "HomeLab-Root-CA" \
--provisioner "[email protected]" \
--dns "ca.lab.internal" \
--address ":443"
The installer will prompt you for a password to protect your Root CA key. Store this safely in a password manager. Once finished, your configuration files will reside in ~/.step. By default, these root certificates are valid for 10 years, while leaf certificates usually expire in 24 hours to encourage frequent, automated rotation.
Activating the ACME Provisioner
Step-ca uses a password-based system by default. To enable automatic renewals like Let’s Encrypt, you must enable the ACME provisioner with this command:
step ca provisioner add acme --type ACME
Phase 3: Running Step-ca as a System Service
Automation requires the CA to start reliably after a reboot. We will use a systemd service to keep the process running in the background.
# Create the service file
sudo nano /etc/systemd/system/step-ca.service
Use the following configuration, ensuring you replace the paths with your actual username:
[Unit]
Description=Step-ca Service
After=network.target
[Service]
User=yourusername
Group=yourusername
ExecStart=/usr/bin/step-ca /home/yourusername/.step/config/ca.json --password-file=/home/yourusername/.step/password.txt
Restart=on-failure
[Install]
WantedBy=multi-user.target
Create the password.txt file containing your CA password and restrict its permissions using chmod 600. While some prefer environment variables, a restricted file is the most practical method for a HomeLab setup.
Phase 4: Trusting the Root Certificate
Your devices only trust certificates from sources they recognize. You must install the Root CA certificate on every laptop, phone, or tablet that accesses your lab. This is a one-time setup that enables the ‘magic’ of green padlocks.
Locate your root certificate at ~/.step/certs/root_ca.crt. On a Linux or macOS machine, you can bootstrap the trust relationship instantly:
step ca bootstrap --ca-url https://ca.lab.internal --fingerprint [YOUR_FINGERPRINT]
Phase 5: Seamless Integration with Caddy
Caddy is a favorite in the DevOps community because it handles internal ACME CAs natively. Your Caddyfile stays clean and readable:
# Point Caddy to your local CA
{
acme_ca https://ca.lab.internal/acme/acme/directory
acme_ca_root /path/to/root_ca.crt
}
proxmox.lab.internal {
reverse_proxy 192.168.1.10:8006
}
media.lab.internal {
reverse_proxy 192.168.1.15:8096
}
Caddy will now automatically reach out to Step-ca, prove its identity via ACME, and fetch a signed cert. No more manual 90-day renewals. It just works.
Final Thoughts
Building a Private CA might seem like overkill for a few services. However, the peace of mind is worth the 20 minutes of setup. You gain enterprise-grade security without the complexity of public DNS challenges or the risks of self-signed certs. If you ever scale to sensitive services like Vaultwarden, having this PKI foundation is a game changer. Monitor your logs to verify everything is healthy:
journalctl -u step-ca -f
Now, go forth and encrypt everything!

