Recently my two WHMCS dashboards were stuck due to expired SSL certificates. I check
crontab -e
There was
# 0 0 * * 4 /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/renew.py >/dev/null 2>&1
Unfortunately, it was unable to renew SSL. So I came up with a separate solution that fixed that issue.
So, in the guide, we have installed Let’s Encrypt SSL instead of Zero SSL for multiple domains and subdomains and set up a cron job to check the status of SSL and auto-renewal.
Now let’s get started.
Table of Contents
1. Requirements
Before you begin, make sure you have the following:
| Requirement | Details |
|---|---|
| Operating System | Ubuntu 20.04 / 22.04 / 24.04 |
| Web Server | OpenLiteSpeed (via CyberPanel) |
| Control Panel | CyberPanel |
| Root SSH Access | Required |
| Port 80 & 443 | Must be open/accessible |
| DNS Records | All domains must point to your server IP |
2. Install Certbot
Certbot is the tool that issues and renews Let’s Encrypt SSL certificates. Install it using Snap for the latest version:
sudo apt update
sudo apt install snapd -y
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
# Verify installation
certbot --version
You should see output like certbot 5.x.x
3. Open Required Ports
Make sure your firewall allows traffic on ports 80, 443, and 8090 (CyberPanel):
ufw allow 80
ufw allow 443
ufw allow 8090
ufw reload
4. Check DNS Before Issuing SSL
All domains must point to your server’s IP address. Run these checks first:
# Get your server's public IP
curl ifconfig.me
# Check each domain resolves to the same IP
dig +short yourdomain.com
dig +short www.yourdomain.com
dig +short subdomain.yourdomain.com
dig +short cp.yourdomain.com
Cloudflare Users: If your domain uses Cloudflare, make sure to set the DNS record to “DNS Only” (grey cloud), not “Proxied” (orange cloud). Certbot needs direct access to your server on port 80 to verify domain ownership.
5. Issue SSL Certificates
We use standalone mode, which temporarily uses port 80 to verify your domain. This means OpenLiteSpeed must be stopped briefly during the process.
1 Stop OpenLiteSpeed
systemctl stop lsws
fuser -k 80/tcp
sleep 2
# Confirm port 80 is free (should return nothing)
lsof -i :80
2 Issue Certificate for Main Domain
certbot certonly --standalone \
-d yourdomain.com \
-d www.yourdomain.com \
--email your@email.com \
--agree-tos \
--no-eff-email
3 Issue a certificate for a subdomain (e.g.dash.yourdomain.com)
certbot certonly --standalone \
-d dash.yourdomain.com \
--email your@email.com \
--agree-tos \
--no-eff-email
4 Issue Certificate for CyberPanel Subdomain (e.g. cp.yourdomain.com)
certbot certonly --standalone \
-d cp.yourdomain.com \
--email your@email.com \
--agree-tos \
--no-eff-email
5 Start OpenLiteSpeed Again
systemctl start lsws
6 Verify All Certificates Were Issued
certbot certificates
Tip: If you see “live directory exists” errors, the config files may be corrupted. See the Troubleshooting section below.
6. Apply SSL in CyberPanel for Websites
After issuing certificates, apply them to each website through the CyberPanel dashboard:
- Log into CyberPanel at
https://yourserver:8090 - Go to Websites → List Websites
- Click on your domain → SSL → Manage SSL
- Paste the certificate and private key contents (see commands below)
Get the certificate contents with:
# Certificate (fullchain)
cat /etc/letsencrypt/live/yourdomain.com/fullchain.pem
# Private Key
cat /etc/letsencrypt/live/yourdomain.com/privkey.pem
Repeat this for each domain/subdomain.
7. Apply SSL to CyberPanel Admin Panel Itself
The CyberPanel admin panel (port 8090) uses its own certificate files. First, find the correct file paths:
find /usr/local/CyberCP -name "cert.pem" 2>/dev/null
find /usr/local/CyberCP -name "key.pem" 2>/dev/null
Then copy the certificate:
cp /etc/letsencrypt/live/cp.yourdomain.com/fullchain.pem \
/usr/local/CyberCP/cert.pem
cp /etc/letsencrypt/live/cp.yourdomain.com/privkey.pem \
/usr/local/CyberCP/key.pem
# Restart CyberPanel service
systemctl restart lscpd
https://cp.yourdomain.com:8090 — the SSL warning should be gone.8. Troubleshooting Common Errors
Error: “Could not bind TCP port 80”
OpenLiteSpeed is still running. Force-kill it:
systemctl stop lsws
pkill -f litespeed
fuser -k 80/tcp
lsof -i :80 # Must return nothing
Error: “live directory exists”
Old/broken certificate files exist. Clean and reissue:
# Remove broken config, live, and archive directories
rm /etc/letsencrypt/renewal/yourdomain.com.conf
rm -rf /etc/letsencrypt/live/yourdomain.com
rm -rf /etc/letsencrypt/archive/yourdomain.com
# Then reissue the certificate
certbot certonly --standalone -d yourdomain.com \
--email your@email.com --agree-tos --no-eff-email
Error: “renewal config file is missing a required file reference”
Same fix as above, remove the broken .conf file from /etc/letsencrypt/renewal/ and reissue.
Error: “ZeroSSL requires –eab-kid and –eab-hmac-key”
You’re using the ZeroSSL ACME server, which requires API credentials. The simplest fix is to remove the --server flag and use Let’s Encrypt instead (default), which requires no extra credentials.
Error: “Webroot 404 / ACME challenge failed”
OpenLiteSpeed is blocking the .well-known directory. Use standalone mode instead (stop OLS first as shown above).
| Error | Fix |
|---|---|
| Port 80 in use | fuser -k 80/tcp then stop lsws |
| Live directory exists | Delete renewal config + live + archive dirs |
| ZeroSSL EAB required | Remove --server flag to use Let’s Encrypt |
| Webroot 404 | Switch to --standalone mode |
| DNS not resolving | Update A record, disable Cloudflare proxy |
| CyberPanel cert path wrong | Use find to locate correct cert.pem path |
9. Set Up Auto-Renewal with Pre/Post Hooks
Since standalone mode needs port 80 free, we use pre and post hooks that automatically stop OpenLiteSpeed before renewal and restart it after. This means SSL auto-renews without any manual intervention.
1 Create Hook Directories
mkdir -p /etc/letsencrypt/renewal-hooks/pre
mkdir -p /etc/letsencrypt/renewal-hooks/post
mkdir -p /etc/letsencrypt/renewal-hooks/deploy
2 Create Pre-Hook (Stops OLS Before Renewal)
nano /etc/letsencrypt/renewal-hooks/pre/stop-lsws.sh
Paste the following content:
#!/bin/bash
systemctl stop lsws
sleep 3
fuser -k 80/tcp
sleep 2
3 Create Post-Hook (Starts OLS After Renewal)
nano /etc/letsencrypt/renewal-hooks/post/start-lsws.sh
Paste the following content:
#!/bin/bash
# Start OpenLiteSpeed
systemctl start lsws
# Copy renewed cert to CyberPanel panel
cp /etc/letsencrypt/live/cp.yourdomain.com/fullchain.pem \
/usr/local/CyberCP/cert.pem
cp /etc/letsencrypt/live/cp.yourdomain.com/privkey.pem \
/usr/local/CyberCP/key.pem
# Restart CyberPanel
systemctl restart lscpd
echo "SSL renewed and services restarted at $(date)" >> /var/log/ssl-renew.log
4 Make Both Hooks Executable
chmod +x /etc/letsencrypt/renewal-hooks/pre/stop-lsws.sh
chmod +x /etc/letsencrypt/renewal-hooks/post/start-lsws.sh
5 Verify Renewal Config Uses Standalone
cat /etc/letsencrypt/renewal/yourdomain.com.conf
Check that it contains authenticator = standalone. If not, fix it:
sed -i 's/authenticator = .*/authenticator = standalone/' \
/etc/letsencrypt/renewal/yourdomain.com.conf
10. Add Cron Job for Daily Auto-Renewal
crontab -e
Add this line at the bottom:
# Auto-renew SSL daily at 3 AM
0 3 * * * certbot renew --quiet >> /var/log/ssl-renew.log 2>&1
💡 Note: Certbot only actually renews certificates when they are within 30 days of expiry. Running the cron daily is safe, it simply checks and skips if renewal is not needed yet.
Disable CyberPanel’s Built-in SSL Renewal (Prevent Conflicts)
CyberPanel has its own SSL renewal cron that may conflict. Disable it:
crontab -e
Find and comment out this line by adding # at the start:
# 0 0 * * 4 /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/renew.py >/dev/null 2>&1
11. Verify Everything Works
Test Dry Run (Simulates Renewal Without Actually Renewing)
certbot renew --dry-run
Check SSL on All Domains
for DOMAIN in yourdomain.com subdomain.yourdomain.com cp.yourdomain.com; do
echo "=== $DOMAIN ==="
echo | openssl s_client -connect $DOMAIN:443 2>/dev/null \
| openssl x509 -noout -issuer -dates
echo ""
done
Check Renewal Logs
cat /var/log/ssl-renew.log
12. Final Summary
Here is a complete overview of what we set up:
| Component | Details | Status |
|---|---|---|
| Certbot installed | via Snap (latest version) | ✅ |
| SSL for main domain | yourdomain.com + www | ✅ |
| SSL for subdomain | dash.yourdomain.com | ✅ |
| SSL for CyberPanel panel | cp.yourdomain.com:8090 | ✅ |
| Pre-hook | Auto-stops OLS before renewal | ✅ |
| Post-hook | Auto-starts OLS + copies panel cert | ✅ |
| Cron job | Daily at 3 AM | ✅ |
| CyberPanel conflict | Built-in renew.py disabled | ✅ |
| Downtime on renewal | ~5–10 seconds every 3 months | ✅ |
🎉 Congratulations! Your SSL certificates are now installed and will auto-renew every 90 days without any manual action needed. Your websites, subdomains, and CyberPanel admin panel are all secured with HTTPS.
FAQ
Can I use this guide for multiple servers?
Yes. Repeat the entire process on each VPS/server. Each server needs its own Certbot installation, certificates, hooks, and cron job. Domains must point to the correct server’s IP.
What if Let’s Encrypt rate-limits me?
Let’s Encrypt allows 5 duplicate certificates per week per domain. If you hit the limit, either wait a week or switch to ZeroSSL by registering at app.zerossl.com and using the --server https://acme.zerossl.com/v2/DV90 flag along with your EAB credentials.
Does OLS go down during renewal?
Yes, briefly around 5–10 seconds. This happens only when the certificate is actually renewed (once every ~3 months). The pre/post hooks handle this automatically.
How do I check when my SSL expires?
certbot certificates
What email should I use for Certbot?
Use an email on your own domain (e.g. admin@yourdomain.com) for professionalism. Make sure it’s a working inbox so you receive expiry warnings from Let’s Encrypt.
Ravi Kumar is a Server Performance Analyst with over 4 years of experience benchmarking cloud infrastructure. He has optimized and stress-tested over 150+ websites, digging deep into uptime, disk latency, and CPU performance. Whether you’re looking for a free trial or a high-performance production server, Ravi uses real-world data to help you find the perfect host.
