How to Automate SSL Certificate Issuance and Renewal for stunnel Using ACME
-
Stunnel is a lightweight TLS wrapper. It sits in front of a service that has no TLS support of its own — a mail daemon, a database, an internal API — and terminates the encrypted connection on its behalf. Because stunnel is not a web server, it has no built-in ACME client and cannot talk to a certificate authority directly.
Certificate management can still be automated: a dedicated ACME client handles issuance and renewal, writes the files where stunnel expects them, and reloads stunnel afterwards. This guide uses acme.sh, which needs nothing beyond curl and OpenSSL. The same logic applies to Certbot.
-
-
*
Before you start
- Root access to the server running stunnel.
- stunnel 5.x or newer, already installed and working with a self-signed or existing certificate.
- A domain name that resolves to this server.
- An ACME SSL subscription with EAB credentials (EAB_KID and EAB_HMAC_KEY) and your ACME server URL, for example https://acme.sectigo.com/v2/DV
- Outbound access to the ACME server on port 443.
- Either inbound TCP port 80 reachable from the internet for HTTP validation, or access to your DNS zone for DNS validation.
Important: Your ACME account must be bound to your GoGetSSL subscription before any certificate can be issued. This is done with the External Account Binding (EAB) credentials shown in your panel — pointing a client at the server URL alone is not enough. -
1
Step 1. Install the ACME client
Install acme.sh as root so that the renewal cron job runs with permission to write into /etc/stunnel:
curl https://get.acme.sh | shLoad the environment and verify the installation. If your shell is not bash, open a new terminal session instead of sourcing the file:
source ~/.bashrc acme.sh --versionThe installer copies acme.sh into /root/.acme.sh and registers a daily cron entry that checks whether any certificate is due for renewal.
-
2
Step 2. Register your ACME account
Register the account once, supplying the EAB credentials from your GoGetSSL panel:
acme.sh --register-account \ --server SERVER \ --eab-kid EAB_KID \ --eab-hmac-key EAB_HMAC_KEY \ --accountemail you@example.comReplace SERVER with the ACME server URL provided by your Certificate Authority (for example https://acme.sectigo.com/v2/DV), and EAB_KID and EAB_HMAC_KEY with the External Account Binding credentials from your account panel. If an account already exists for those credentials, acme.sh reuses it.
-
3
Step 3. Issue the certificate
If TCP port 80 is free on this server and reachable from the internet — common on a dedicated stunnel host — use standalone HTTP validation. A port that is free locally is not necessarily reachable, so check the OS firewall and any cloud security group first.
acme.sh --issue -d mail.example.com \ --standalone \ --server SERVERIf port 80 is blocked, already in use, or you need a wildcard certificate, use DNS validation instead: acme.sh --issue -d example.com -d "*.example.com" \
--dns dns_cf \ --server SERVERTip: Standalone mode requires the socat utility on most distributions. Install it with apt install socat or dnf install socat before issuing your first certificate. -
4
Step 4. Deploy the certificate to stunnel
Stunnel reads its certificate files at start-up and on every configuration reload, so a renewal only takes effect once stunnel is signalled. Do not point stunnel at the files inside acme.sh’s own directory. Use install-cert, which copies them to a stable location and records the reload command to run after every renewal:
mkdir -p /etc/stunnel/certs acme.sh --install-cert -d mail.example.com \ --key-file /etc/stunnel/certs/mail.example.com.key \ --fullchain-file /etc/stunnel/certs/mail.example.com.pem \ --reloadcmd "systemctl reload stunnel4"
On Red Hat and derivative distributions the service is usually named stunnel rather than stunnel4. Confirm with systemctl list-units | grep stunnel.
Set ownership and permissions. The certificate chain is public, the private key is not:
chown root:root /etc/stunnel/certs/mail.example.com.* chmod 644 /etc/stunnel/certs/mail.example.com.pem chmod 600 /etc/stunnel/certs/mail.example.com.keystunnel reads both files before dropping privileges to an unprivileged user, so a root-owned key with mode 600 works correctly.
-
5
Step 5. Point stunnel at the new files
Edit /etc/stunnel/stunnel.conf so that the service section references the deployed files. The cert directive points to the full chain, key to the private key — it can be omitted if the key is stored inside the same file. Then reload:
[imaps] accept = 993 connect = 127.0.0.1:143 cert = /etc/stunnel/certs/mail.example.com.pem key = /etc/stunnel/certs/mail.example.com.key systemctl reload stunnel4 A reload sends SIGHUP: stunnel re-reads its configuration and certificate files without dropping established connections. A full restart is not required. -
6
Step 6. Verify the result
Confirm that the certificate presented on the wire is the one you just issued:
openssl s_client -connect mail.example.com:993 \ -servername mail.example.com < /dev/null | openssl x509 -noout -dates -issuer -subjectThe output should show the correct subject, the issuing CA, and a validity window starting today. If the previous certificate still appears, stunnel was not reloaded — check the service name in the reload command.
-
*
Renewal
No further action is needed. The cron job installed in Step 1 runs daily, renews the certificate when it comes due, copies the new files to /etc/stunnel/certs, and runs the reload command from Step 4. Test the whole chain before relying on it:
acme.sh --renew -d mail.example.com --forceThen repeat the check from Step 6. If the new dates appear, the automation works end to end.
Important: If you ever move the certificate files or rename the stunnel service, re-run the install-cert command from Step 4. The reload command is stored per certificate at install time and is not updated automatically.Tip: Running several stunnel services from one configuration file? A single certificate covering all the hostnames involved keeps the setup simpler than issuing one certificate per section, since every renewal then triggers only one reload. -
*
Quick fixes
- Unauthorized / not delegated — confirm you used the correct ACME server URL and the EAB credentials belonging to this subscription.
- Standalone validation fails — check that port 80 is open in the OS firewall and in any cloud security group, and that nothing else is bound to it (sudo lsof -i :80).
- socat not found — install it before using standalone mode, or switch to DNS validation.
- stunnel still serves the old certificate — the reload command did not run. Verify the service name and re-run install-cert with the correct one.
- Permission denied on start-up — check ownership of the files under /etc/stunnel/certs.
-