Back in 2024 I wrote about getting a Let’s Encrypt certificate onto a Synology NAS. That method needs ports 80 and 443 open to the NAS. This year I replaced it on three NAS boxes, two Synology and one QNAP, with a wildcard certificate issued through Cloudflare DNS validation. Nothing is exposed to the internet, one certificate covers every host on the domain, and renewal is a cron job. Here is the setup and the four gotchas that cost me an evening.
Why DNS-01 and a wildcard
With DNS validation, acme.sh proves you own the domain by creating a TXT record through the Cloudflare API, so the NAS does not need any inbound ports. A wildcard (*.example.com) means the same certificate can be dropped onto every internal service, and you issue it once rather than per host.
Create a Cloudflare API token with Zone, DNS, Edit on the one zone and nothing else. That token is all acme.sh needs.
Issuing the certificate
Install acme.sh as a user that can run cron on the NAS, export the token, and issue.
export CF_Token="your-token"
acme.sh --issue --dns dns_cf \
-d example.com -d '*.example.com' \
--keylength 2048 \
--server letsencrypt
Note --keylength 2048. That is gotcha number one.
Gotcha 1: DSM 6 rejects ECC certificates
acme.sh defaults to an ECC key. Synology DSM 6 refuses to import it with error 5598 and no further explanation. Use an RSA 2048 key and it imports fine. DSM 7 accepts either.
Deploying to Synology
acme.sh has an official synology_dsm deploy hook that talks to the DSM API, so you do not need to copy files or restart nginx yourself.
export SYNO_Username="certadmin"
export SYNO_Password="..."
export SYNO_Certificate="DefaultCert"
export SYNO_Create=1
acme.sh --deploy -d example.com --deploy-hook synology_dsm
Gotcha 2: the hook only replaces the default certificate if it is already the default
The hook matches on the certificate description. If the certificate it finds by that description is the default one, it replaces it in place and every service keeps using it. If it is not, you end up with a second certificate and the services still point at the old one. So set SYNO_Certificate to the description of the certificate that is currently the default, and let the hook overwrite that.
Gotcha 3: Synology SSH keys and the home folder
If key-based SSH to the NAS stops working after an update, the user’s home directory symlink has usually been broken. Re-enable the user home service in Control Panel and recreate the .ssh folder with the right permissions.
Deploying to QNAP
QTS has no deploy hook, so a short script copies the certificate and key into place and restarts the web service. It needs sudo for the copy.
Gotcha 4: QNAP ignores sudoers.d
The real sudoers file on QTS is /usr/etc/sudoers. Anything you drop into a sudoers.d folder is ignored and the deploy script fails with a password prompt in the log. Edit the real file.
Renewal
acme.sh installs its own cron entry. I run the deploy as a weekly job, test it with --dry-run first, and use --force once to prove the whole chain before trusting it.
acme.sh --renew -d example.com --dry-run
acme.sh --renew -d example.com --force
Three boxes, one certificate, zero open ports. It has renewed itself without a word since.