Certbot reports a successful renewal, the files on disk carry new dates, and clients still meet the old identity. SoftEther holds its SSL server certificate in its own server-level configuration, so a new file on disk changes nothing until the leaf and its key are imported. The sequence below covers one manual import, a wrapper that runs after each renewal, and a check that reads what the listener actually presents. Every step leaves evidence you can compare after the next renewal.

A renewed file does not change the server identity

Issuance and deployment are separate events with separate failure modes. The ACME client writes files into a lineage directory and records success; SoftEther keeps using whatever certificate was last loaded into its configuration. A zero exit status therefore describes the files, not the service, and the two can disagree for as long as nobody looks. Treat the import as the deployment step and the renewal as the input to it.

That distinction also explains the confusing half-failure: the old certificate keeps working until its expiry, so nothing appears broken at first. When it does expire, every client fails at once, which looks like an outage rather than a missed deployment step. Checking the presented identity after each renewal converts that surprise into a routine observation.

Choose the challenge that fits the listener

The certificate has to name the host that clients verify, so use the public DNS name rather than an internal label or an address. HTTP-01 needs the challenge answered on port 80 for a moment, which is a temporary listener and never a reason to move SoftEther's own port. DNS-01 proves control through a record and needs no inbound HTTP at all.

Unattended renewal means an automated DNS integration with credentials scoped to the one zone, not a TXT record typed by hand every couple of months. A wildcard changes the challenge type, not the need for that automation. Decide this before the first certificate exists, because switching challenge types later means touching the client configuration and the firewall together.

Keep the artifacts straight while you are here. The leaf and the intermediates arrive as separate files and as a combined chain file, the private key is separate again, and the import needs the chain with its matching key rather than a public certificate on its own.

Import the chain at server level

Back up the server configuration and confirm you can still reach the management interface before touching the certificate. Connect as a server administrator rather than a Virtual Hub administrator, because the certificate belongs to the server object. Then load the chain and key by absolute path, from files the account running the command can read. Note that the paths are absolute because the command resolves them from the working directory of whichever process ran vpncmd:

ServerCertSet /LOADCERT:/etc/letsencrypt/live/vpn.example.com/fullchain.pem /LOADKEY:/etc/letsencrypt/live/vpn.example.com/privkey.pem

Read the command output instead of assuming success, and then read the chain back rather than trusting the file you passed in. The boundary between builds that import a whole PEM bundle and builds that take only the first certificate is not documented in one place, so the check is what settles it for the build in front of you. Count the certificates the listener presents; a leaf with no intermediates is the signature of an importer that consumed only the first block.

Keep the imported key readable only by the account that runs the server, and keep the management password out of shell history. For this first import, type the password at the prompt instead of passing it as a command argument. Those two habits cost nothing and remove the most common way a certificate deployment leaks something it was meant to protect. Do that before the first import, not after it.

Hook the deployment, not the renewal

Once the manual import works, put exactly those steps into a small wrapper and call it from the ACME client after a successful renewal. Certbot sets RENEWED_LINEAGE and RENEWED_DOMAINS for the hook, so the wrapper can act on one certificate on a machine that manages several. Filter on the lineage rather than reacting to every renewal on the host. The wrapper is deployment code that runs with the server's privileges, so review it like any other script in that position.

certbot renew --deploy-hook /usr/local/sbin/softether-deploy.sh

A hook that runs unattended needs its own protected credential store and a failure signal that reaches a human. If issuance succeeds and the import fails, retry the import instead of requesting another certificate, because a second request hides the deployment defect behind a fresh file. The Certbot renewal documentation is clear that a run which updates nothing is a normal outcome, so the hook is what tells the two apart.

Compare the presented chain with the file

Read the listener, not the disk. Connect to the client-facing port with the server name the clients use, then compare the fingerprint and the delivered chain with the lineage the hook installed. Two commands cover it, and the first one also shows whether intermediates arrive at all:

openssl s_client -connect vpn.example.com:443 -servername vpn.example.com -showcerts </dev/null 2>/dev/null | grep -c 'BEGIN CERTIFICATE'
openssl s_client -connect vpn.example.com:443 -servername vpn.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -dates -fingerprint -sha256

Then make a fresh client connection, because sessions established before the import were authenticated against the previous identity and prove nothing about the new one. If clients pin the old leaf, they need a coordinated update even when the public chain is valid, and that task belongs in the same runbook as the renewal. A reverse proxy in front of the service can present a good certificate while the VPN listener still holds the old one, which is why the check has to name the port clients actually reach. Record the fingerprint beside the renewal date, so the next comparison is a check rather than an act of memory.

Ebitabo ebifaananyi