DEV Community

Cover image for CyberPanel's SSL Auto-Renewal Can Silently Fail — Here's the Fix
Pascal CESCATO
Pascal CESCATO Subscriber

Posted on

CyberPanel's SSL Auto-Renewal Can Silently Fail — Here's the Fix

Compare UI success with OpenSSL dates

Last week I was doing a routine check on a CyberPanel server and noticed something that didn't add up. The panel's SSL status showed a clean "SSL Issued Successfully". Nothing to see, apparently. Except the certificate expiration date hadn't moved.

The browser was still serving a certificate that was about to expire.

That's the kind of mismatch that's easy to miss if you trust the panel's UI at face value — the message says success, the log (if you even check it) looks uneventful, and you move on. I didn't move on.

Checking what's actually served

The panel tells you what it thinks happened. What matters is what the server actually hands out to a browser. That's a one-liner away:

openssl s_client -connect  \
domain.com:443 -servername domain.com </dev/null 2>/dev/null \
    | openssl x509 -noout -dates

# outputs
notBefore=Jul 31 22:59:26 2026 GMT
notAfter=Oct 29 22:59:26 2026 GMT
Enter fullscreen mode Exit fullscreen mode

Wait — that date range looked fine on its own. The real tell was comparing it against what the panel had just claimed to renew: the dates hadn't changed since before the "successful" renewal. Whatever acme.sh had just done, it hadn't produced a new certificate that OpenLiteSpeed was actually serving.

Checking what acme.sh actually did

CyberPanel doesn't implement ACME itself — it delegates to acme.sh under the hood. So the next step was checking what acme.sh had registered:

acme.sh --list
Enter fullscreen mode Exit fullscreen mode

That's where the mismatch became clearer. The certificate authority listed for this domain wasn't Let's Encrypt production — it was the staging CA. Staging certificates are useful for testing, but they're not production certificates trusted by browsers. In this case, the important part wasn't just that staging was configured — it was that the renewal workflow no longer matched the certificate actually served by OpenLiteSpeed. CyberPanel reported success because, from its point of view, acme.sh had returned successfully. It just wasn't returning the certificate the site needed.

I can't say for certain how this CyberPanel installation's acme.sh configuration ended up pointed at the staging CA — only that it had. Worth keeping in mind if you're chasing a similar mismatch: the fix doesn't require knowing the origin, just confirming the current state.

The fix

Once the mismatch was clear, the fix was mechanical:

  1. Force acme.sh back onto the Let's Encrypt production CA.
  2. Drop the existing (staging) registration for the domain.
  3. Reissue the certificate from scratch, this time against production.
  4. Install it where CyberPanel and OpenLiteSpeed expect it: /etc/letsencrypt/live/<domain>/.
  5. Reload OpenLiteSpeed so it picks up the new files.

None of this is exotic — it's exactly what acme.sh is designed to do. The problem was never the tooling, it was that CyberPanel's reported status wasn't a reliable signal of what state the certificate was actually in.

Turning it into something reusable

Fixing one server by hand is fine once. But when the same class of drift keeps showing up across CyberPanel releases, it stops being a one-off and starts being something worth scripting around. I wrote two tools:

  • renew-ssl.sh — routine renewal, bypassing CyberPanel's scheduler entirely. It supports an auto mode (renews anything expiring within a configurable threshold, default 10 days), a single-domain mode, and a --check mode that simulates the run and just reports what would happen — useful for confirming a diagnosis like this one before touching anything.
  • fix-ssl.sh — the heavier tool, for when a certificate is actually broken or stuck in staging. Given that the whole premise of this post is "don't trust the reported status, verify it," the script holds itself to the same standard on both ends:
    • Before touching anything, it writes a test file into the HTTP-01 challenge path and fetches it over plain HTTP, the same way Let's Encrypt will. If that fails, it stops before issuing anything — no point calling acme.sh against a webroot that can't serve the challenge.
    • It backs up the existing certificate and acme.sh registration to a timestamped archive before removing anything, and refuses to proceed if the backup itself fails.
    • It forces the production CA, clears the old registration, reissues via webroot, and installs the new certificate.
    • After reloading OpenLiteSpeed, it doesn't just assume the reload worked — it fetches the certificate actually served over HTTPS and compares its SHA256 fingerprint against the one just installed. A mismatch is reported as an error, with the exact restore command from the backup printed alongside it.

Both are plain Bash, no dependencies beyond acme.sh and openssl (fix-ssl.sh also uses curl for the webroot check, though it degrades gracefully — with a warning — if curl isn't available). Both are meant to be run as root since they touch /etc/letsencrypt/, /root/.acme.sh/, and the OpenLiteSpeed service.

Source, with usage examples and a debugging section covering both commands above:

GitHub logo pcescato / cyberpanel-ssl-renewal-scripts

Renew and repair SSL certificates on CyberPanel 2.x directly via acme.sh (bypasses the broken built-in renewal scheduler)

CyberPanel SSL Renewal Scripts

Bash scripts to renew and repair SSL certificates on CyberPanel 2.x directly via acme.sh, bypassing CyberPanel's unreliable built-in renewal scheduler.

Why this exists

  • CyberPanel can report a successful SSL renewal while the certificate actually served remains close to expiration.
  • In some cases acme.sh is left configured with the Let's Encrypt staging CA, so "renewals" produce certificates that browsers reject.
  • These scripts renew — and repair — certificates directly via acme.sh, without going through the panel.

Requirements

  • CyberPanel 2.x
  • OpenLiteSpeed
  • acme.sh installed at /root/.acme.sh/ (CyberPanel default)
  • curl — used by fix-ssl.sh to validate the webroot challenge path (optional: without it, that check is skipped with a warning)
  • Root access — the scripts write to /etc/letsencrypt/live/, read /root/.acme.sh/, and restart OpenLiteSpeed

Scripts

renew-ssl.sh — Renew certificates

Renews certificates that were previously issued and installed with acme.sh.

It supports three modes:

  • Auto mode (recommended):




Why this is worth checking on your own servers

If you're running CyberPanel and haven't looked recently, it's worth running the openssl s_client check against your actual domains — not because CyberPanel is broken in general, but because "the panel says success" and "the certificate is actually valid and current" are two different claims, and only one of them is verifiable from outside the panel.

Have you seen similar ACME renewal drift on CyberPanel, OpenLiteSpeed, or other hosting panels? I'd be interested in comparing root causes — certificate automation failures are often harder to diagnose than certificate issuance itself.

Top comments (5)

Collapse
 
ismail-benali profile image
H3l!0s_T3k

Great write up. One important lesson here is that infrastructure automation should always be verified from the outside not only trusted from the control panel.
A successful ACME renewal message does not always mean the certificate currently served by the web server has changed.
Using tools like openssl s_client, certificate fingerprints, and external monitoring can help detect these silent failures before users see SSL errors.
This is a good reminder that observability is as important as automation in DevOps.

Collapse
 
pascal_cescato_692b7a8a20 profile image
Pascal CESCATO

Thanks! I completely agree. Automation is only half of the story; without independent verification, it's easy to end up trusting an assumed state instead of the actual one. That's why I added post-renewal verification to the scripts, so they don't just generate a certificate—they also help confirm that OpenLiteSpeed is really serving it.

Collapse
 
rizzdev profile image
Andrew R

when CyberPanel says SSL issued successfully that's when you stop trusting the UI and check openssl dates

Collapse
 
pascal_cescato_692b7a8a20 profile image
Pascal CESCATO

That's exactly what this incident reminded me. It's not specific to CyberPanel either—whenever something security-related looks "successful", I prefer verifying the result from the outside rather than relying solely on the management interface.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.