The core issue is that simply copying files and restarting Stalwart is not how certificate management works in current Stalwart. Stalwart does not scan a directory for cert files on its own. Instead, Certificate objects must be registered in Stalwart’s database (via the WebUI or CLI), pointing to the file paths on disk. After files are rotated, a hot reload must be triggered via the CLI – a full service restart is not needed and does not re-read file-referenced certs either.
Here’s the correct approach:
1. Register your certificate(s) in Stalwart (one-time setup)
In the WebUI under Settings > TLS > Certificates, create a Certificate object for each domain with @type: File references:
{
"certificate": {
"@type": "File",
"filePath": "/etc/stalwart/certs/domain1.de/fullchain.pem"
},
"privateKey": {
"@type": "File",
"filePath": "/etc/stalwart/certs/domain1.de/privkey.pem"
}
}
Do this for each domain. Stalwart will then read the actual PEM content from those paths at load/reload time.
2. Replace your systemctl restart with a CLI hot-reload
After copying the renewed files, instead of restarting the service, call:
stalwart-cli --url https://mail.yourdomain.com --api-key <TOKEN> create action/ReloadTlsCertificates
This tells the running server to re-read the certificate files from disk without any downtime or service interruption.
Updated Certbot deploy hook script
#!/bin/bash
MY_DOMAINS=("domain1.de" "mail.domain1.de" "domain2.de" "mail.domain2.de")
for RENEWED_DOMAIN in ${RENEWED_DOMAINS}; do
for MY_DOMAIN in "${MY_DOMAINS[@]}"; do
if [ "${RENEWED_DOMAIN}" = "${MY_DOMAIN}" ]; then
CERTIFICATE_DIR="/etc/stalwart/certs/${RENEWED_DOMAIN}"
cp "${RENEWED_LINEAGE}/fullchain.pem" "${CERTIFICATE_DIR}/fullchain.pem"
cp "${RENEWED_LINEAGE}/privkey.pem" "${CERTIFICATE_DIR}/privkey.pem"
chown stalwart:stalwart "${CERTIFICATE_DIR}/fullchain.pem" "${CERTIFICATE_DIR}/privkey.pem"
fi
done
done
# Trigger Stalwart to re-read all cert files (no restart needed)
stalwart-cli --url https://mail.yourdomain.com --api-key <TOKEN> create action/ReloadTlsCertificates
Note on your original script: there’s also a syntax bug – the array initializer uses commas between elements ("domain1.de", "mail.domain1",...), which is not valid in bash. Array elements should be space-separated only.
Docs references:
This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.