This episode covers custom certificates and internal CA: manual certificates in PEM format, private CA with self-signed certs, client certificates for mTLS, Step CA and internal ACME integration, and on-demand TLS for multi-tenant architectures.

Not every certificate comes from a public CA. Inside a corporate network or homelab, you often need an internal CA — your own certificate authority trusted by internal devices. Episode 10 covers the full spectrum of custom certificates: manual, private CA, mTLS, automation with Step CA, and on-demand TLS.
You'll learn to install existing certificates, build trust for an internal CA, enable mutual TLS (mTLS) where clients must also present a certificate, connect Caddy to an internal ACME server, and use on-demand TLS for multi-tenant SaaS.
This is the episode that gives you full control over your PKI infrastructure — a rare and valuable skill in the DevOps world.
Certificates in Caddy use the PEM format — text wrapped in -----BEGIN CERTIFICATE-----. What you need to prepare:
When a CA gives you a chain, combine the leaf and intermediate in one file, leaf on top:
-----BEGIN CERTIFICATE-----
<leaf certificate>
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
<intermediate certificate>
-----END CERTIFICATE-----Then install it in the Caddyfile:
example.com {
tls /etc/ssl/fullchain.pem /etc/ssl/privkey.pem
root * /var/www
file_server
}tls fullchain.pem privkey.pem uses the prepared chain file and key. A wrong file order will make browsers reject the certificate.
For an internal CA, create your own root CA and sign domain certificates with it. The fastest way is tls internal, which makes Caddy the CA:
internal.example.local {
tls internal
root * /var/www
file_server
}To make clients trust the internal CA, import Caddy's root CA into each device's trust store. On Linux systems:
sudo cp /var/lib/caddy/.local/share/caddy/pki/authorities/local/root.crt /usr/local/share/ca-certificates/caddy-local.crt
sudo update-ca-certificatesThe sudo update-ca-certificates command updates the system's list of trusted CAs. After this, internal browsers no longer show warnings.
Large organizations usually have their own PKI (Microsoft AD CS, EJBCA, and others). Caddy can use certificates issued by that PKI through the manual mechanism. The key points: make sure the chain is complete and internal clients trust the organization's root CA.
mTLS requires clients to prove their identity with a certificate:
api.example.com {
tls {
client_auth {
mode require_and_verify
trusted_ca_certs /etc/ssl/client-ca.pem
}
}
reverse_proxy localhost:8080
}mode require_and_verify requires clients to present a certificate signed by the client-ca.pem CA. Requests without a valid certificate are rejected before ever touching the application.
mTLS is a far stronger authentication alternative than API keys because it's based on cryptography.
tls internal produces certificates that are hard to renew across instances. The production solution: run Step CA (Smallstep) as an internal ACME server, then point Caddy at it:
{
acme_ca https://ca.internal.example.com/acme/acme/directory
}
internal.example.local {
root * /var/www
file_server
}Caddy treats Step CA like Let's Encrypt: issuance, automatic renewal, and certificate storage all work the same way, but with an internal CA the organization trusts. This gives automatic renewal for internal certificates without public certificates.
On-demand TLS issues a certificate only when the first request arrives for that domain:
{
on_demand_tls {
ask https://auth.example.com/allow
}
}
*.saas.example.com {
tls {
on_demand
}
reverse_proxy localhost:8080
}on_demand triggers issuance when the first request for a new domain arrives. The ask endpoint validates whether the domain is allowed — preventing abuse by outsiders.
On-demand TLS suits multi-tenant SaaS that serves users' custom domains dynamically. Without ask, anyone could make your Caddy request certificates for their domains — so always enable validation.
Episode 10 gave you full control over PKI: installing manual certificates with PEM chains, establishing an internal CA with tls internal and the trust store, enabling mTLS with client_auth, automating internal certificates with Step CA as an ACME server, and on-demand TLS for multi-tenancy.
Key takeaways:
tls internal makes Caddy an internal CA for development.client_auth with mode require_and_verify enables mTLS.ask endpoint for validation.In the next episode, episode 11, we move into matchers & request filtering — the matcher concept, all standard matchers like path, method, header, host, and remote_ip, inline and named syntax, path matching with prefixes and wildcards, and advanced patterns combining matchers and CEL expressions.