SSL certificates
MZPanel issues and renews TLS certificates for the sites on a server, and shows
each domain’s certificate state next to its DNS health. It lives in the DNS &
TLS section of a server (/servers/:id/dns-tls) — the old /servers/:id/ssl
deep-link still redirects here. The agent runs certbot/openssl on the box;
the private key never leaves the server. Standard Let’s Encrypt issue and renew
are available on all plans.
Issue a certificate, step by step
Section titled “Issue a certificate, step by step”- Open a server and go to DNS & TLS. Each domain shows a combined row: its DNS health (does it resolve here? is it proxied?), its SSL/TLS state (valid / expiring / expired / none), the Certificate issuer + expiry, and toggles for HTTPS (force redirect) and Proxy.
- Click a domain to open its Manage drawer. The drawer suggests the right strategy from the DNS signal — direct-to-box domains get Let’s Encrypt, proxied domains get a Cloudflare Origin cert recommendation.
- Click Issue certificate. For a domain that points straight at the server, MZPanel obtains a Let’s Encrypt certificate over the HTTP-01 challenge and reloads Nginx.
- The row flips to a valid certificate with days-remaining, and auto-renew turns on automatically for Let’s Encrypt certs.
What else you can do
Section titled “What else you can do”- Renew now — re-run issuance for a domain immediately. For a Let’s Encrypt cert it’s a real renew; for a self-signed / origin cert the button reads Re-issue (auto) and re-detects the right method.
- Auto-renew — toggle per domain. It applies only to Let’s Encrypt certs (self-signed / Cloudflare-origin certs don’t renew via certbot, so the toggle is hidden for them).
- Force HTTPS — the HTTPS toggle redirects HTTP → HTTPS for the domain. It’s disabled until a certificate exists.
- Delete — remove a domain’s certificate (reversible — you can re-issue any time).
- Wildcard (
*.example.com) — one cert that covers every subdomain via the DNS-01 challenge (HTTP-01 cannot issue wildcards). Wildcards require the domain’s zone connected through a Cloudflare token, and are issued and listed from the fleet-level DNS providers connection, not this per-domain drawer. - Advanced — custom or paid certificate (the CustomCertPanel in the Manage
drawer, three options):
- Cloudflare Origin Certificate — free, up to 15-year cert signed by Cloudflare for the edge↔origin hop. Best for proxied domains; needs a connected Cloudflare token.
- Upload a certificate — paste a cert + key you already hold (migrated / corporate). The key is passed through and never stored by MZPanel.
- Generate a CSR — the box makes a private key (which stays on the box) and a CSR; you buy a cert from any CA, then paste the signed cert back to install it.
- Bulk actions — select multiple domains and Renew SSL, or set Force HTTPS / Cloudflare proxy across all selected domains at once, with a live progress count.
How it works
Section titled “How it works”The DNS & TLS row list is read from MZPanel’s per-server inventory cache (the
ssl inventory kind), so it renders instantly even when the box is offline. Every
mutation dispatches an ssl.* job to the agent, which runs it natively:
| Action | Job | Agent runs |
|---|---|---|
| Issue | ssl.issue | ssl issue <domain> --json (smart HTTP-01 issuer) |
| Renew | ssl.renew | ssl renew [<domain>] --json |
| Delete | ssl.delete | ssl delete <domain> --json |
| Auto-renew | ssl.auto-renew | ssl auto-renew <domain> on|off --json |
| Force HTTPS | ssl.https-redirect | ssl https-redirect <domain> on|off --json |
The smart issuer digs the apex: if it points here, it issues Let’s Encrypt; if
it’s Cloudflare-proxied, it installs a self-signed origin cert; otherwise it skips.
Certificates and their metadata live on the box (/etc/letsencrypt/,
/etc/mz/ssl/<domain>/), and Let’s Encrypt certs auto-renew on the server’s own
timer before expiry.
The wildcard, CSR, upload and Cloudflare Origin flows are
control-plane orchestrated (routes under /v1/dns/*), because they need a secret
that must stay off the box — the Cloudflare token. The key-split keeps each
secret where it belongs:
- The box generates the private key + CSR (
ssl gen-csr <domain> [--wildcard]). The private key never leaves the server. - The control plane runs the ACME order, writing the
_acme-challengeTXT record through your Cloudflare connection (which holds the token), validates, and finalizes with the box’s CSR. - Only the signed certificate chain is sent back and installed
(
ssl install-cert <domain> --b64 <chain>), then Nginx reloads and the temporary challenge record is cleaned up.
Wildcard certs are tracked and auto-renewed by the control plane like any other certificate.
CLI & automation
Section titled “CLI & automation”Every SSL action maps to an mz command on the box — the same engine the dashboard
drives. SSH in and run them directly, or let an on-box AI (ClaudeCode) run them:
mz ssl list --json # every cert on the boxmz ssl issue example.com --json # Let's Encrypt via HTTP-01mz ssl renew example.com --json # renew one (omit domain = renew all)mz ssl auto-renew example.com on --json # toggle auto-renewmz ssl https-redirect example.com on --json # force HTTP → HTTPSThe wildcard / paid-cert path also has on-box helpers — mz ssl gen-csr <domain> [--wildcard] to make the CSR and mz ssl install-cert <domain> --b64 <chain> to
install a signed chain — though the dashboard normally drives these through the
control plane so the Cloudflare DNS-01 step is automated. See
The mz CLI and the command catalog.
Gotchas & troubleshooting
Section titled “Gotchas & troubleshooting”- DNS first, always. “Certificate won’t issue” is almost always the domain not resolving to this server. Check the DNS column in the row before retrying.
- Wildcard and Origin certs need a connected Cloudflare token. Without a connected zone, the wildcard / Origin actions are gated with a “connect Cloudflare” prompt rather than failing silently. Cloudflare integration is a paid-tier feature.
- Auto-renew is Let’s Encrypt-only. For self-signed or Cloudflare-origin certs the toggle is hidden — use Re-issue (auto) to refresh them instead.
- Force HTTPS needs a cert first. The HTTPS toggle stays disabled until the domain has a valid certificate; issue one, then flip it.
- Docker app domains fall back. The native engine ports issue/renew/delete for standard WordPress-site vhosts; a domain that resolves to a Docker app (proxy vhost) is handled by a different code path — issuance still works but behaves per the app’s proxy block.
- Let’s Encrypt rate limits. LE limits issuance per registered domain. If you’re testing repeatedly you’ll hit them; automatic renewals are scheduled to stay well clear.
- Actions need the agent online. The row list renders from cache when the server is offline, but issuing, renewing and installing certs require a live agent.
Related
Section titled “Related”- Sites — the domains whose certificates you manage here.
- Server overview — at-a-glance HTTPS status per server.
- The mz CLI — driving the box from the command line.
- API reference — automating cert flows from the control plane.