A small Bash deploy hook for installing Certbot-managed TLS certificates into the standard SER connector certificate locations.
- The SER connector receives certificate and key files in predictable locations.
- The private key is installed with restrictive permissions.
- The
ser-connectorservice is restarted after deployment. - Existing certificate and key files are backed up before replacement.
- The deployed certificate and private key are validated as a matching pair.
This code is provided as-is, without warranty, support commitment, or guarantee of fitness for a particular purpose.
Users are responsible for reviewing, testing, and validating the script in their own environment before using it in production. Certificate deployment errors can cause TLS failures, service interruption, or mail-flow impact.
Always test in a lab or non-production environment first, and ensure that your operational rollback process is understood and validated before production use.
By default, deployed files are written to:
/opt/ser/config/tls/certs/<deploy-name>.crt
/opt/ser/config/tls/private/<deploy-name>.key
For example, if the deploy name is relay.company.com, the deployed files are:
/opt/ser/config/tls/certs/relay.company.com.crt
/opt/ser/config/tls/private/relay.company.com.key
A typical SER connector YAML configuration would reference those files:
User:
# Modify and uncomment the following lines
ConnectorUser:
ClientID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
ClientSecret: "<secret>"
RelayUser:
Username: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
Password: "<secret>"
#NDR:
# StoreBounces:
# # Select one of the two following lines
# Enable: true
# Enable: false
# Path: "Your path where to store the bounces if different from the default /var/spool/cmgw/deadletter"
# ForwardEmail:
# # Select one of the two following lines
# Enable: true
# Enable: false
# FromAddress: "Your from address for NDR"
# ToAddress: "Your to address for NDR"
# # Select one of the three following lines
# TlsMode: "forced TLS and verify"
# TlsMode: "forced TLS"
# TlsMode: "opportunistic"
Listener:
- Port: 25
Certificate: '/opt/ser/config/tls/certs/relay.company.com.crt'
Key: '/opt/ser/config/tls/private/relay.company.com.key'
StartTLS: 'optional'
Type: SMTP
- Port: 587
Certificate: '/opt/ser/config/tls/certs/relay.company.com.crt'
Key: '/opt/ser/config/tls/private/relay.company.com.key'
Type: SMTPS
- Port: 465
Certificate: '/opt/ser/config/tls/certs/relay.company.com.crt'
Key: '/opt/ser/config/tls/private/relay.company.com.key'
Type: SMTPS
When Certbot runs a deploy hook, it provides environment variables to the hook script.
The most important one is:
RENEWED_LINEAGE
This points to the specific Certbot live lineage directory for the renewed certificate.
Example:
/etc/letsencrypt/live/relay.company.com
The script then reads:
/etc/letsencrypt/live/relay.company.com/fullchain.pem
/etc/letsencrypt/live/relay.company.com/privkey.pem
Certbot may also provide:
RENEWED_DOMAINS
Example:
relay.company.com smtp.company.com
The script uses the first renewed domain as the default deploy name unless --deploy-name is provided.
--deploy-name controls only the destination filename base used by the SER connector.
It does not change:
- the certificate subject
- the certificate SANs
- the Certbot certificate name
- the Certbot lineage path
- the source files under
/etc/letsencrypt/live
Example:
--deploy-name outbound-relaycreates or updates:
/opt/ser/config/tls/certs/outbound-relay.crt
/opt/ser/config/tls/private/outbound-relay.key
This is useful when the SER connector YAML uses a stable logical name that differs from the certificate's first domain.
Install the script as a root-owned administrative command:
sudo install -o root -g root -m 0755 ser-certbot-deploy.sh /usr/local/sbin/ser-certbot-deploy.shUse the absolute path in Certbot commands. Do not rely on PATH for automated renewal hooks.
Single-name certificate:
sudo certbot certonly \
--standalone \
--agree-tos \
--email user@company.com \
--server <YOUR_ACME_OR_SCM_DIRECTORY_URL> \
--eab-kid <YOUR_EAB_KEY_ID> \
--eab-hmac-key <YOUR_EAB_HMAC_KEY> \
--domain relay.company.com \
--deploy-hook "/usr/local/sbin/ser-certbot-deploy.sh"This will normally deploy:
/opt/ser/config/tls/certs/relay.company.com.crt
/opt/ser/config/tls/private/relay.company.com.key
A single certificate can contain multiple DNS names.
Example:
sudo certbot certonly \
--standalone \
--agree-tos \
--email user@company.com \
--server <YOUR_ACME_OR_SCM_DIRECTORY_URL> \
--eab-kid <YOUR_EAB_KEY_ID> \
--eab-hmac-key <YOUR_EAB_HMAC_KEY> \
--domain relay.company.com \
--domain smtp.company.com \
--deploy-hook "/usr/local/sbin/ser-certbot-deploy.sh"Even though the certificate is valid for multiple names, the script deploys one certificate/key pair for the SER connector.
By default, the first domain becomes the deploy name:
relay.company.com
Result:
/opt/ser/config/tls/certs/relay.company.com.crt
/opt/ser/config/tls/private/relay.company.com.key
Use --deploy-name when the SER connector YAML should use a logical or stable name instead of the first certificate domain.
sudo certbot certonly \
--standalone \
--agree-tos \
--email user@company.com \
--server <YOUR_ACME_OR_SCM_DIRECTORY_URL> \
--eab-kid <YOUR_EAB_KEY_ID> \
--eab-hmac-key <YOUR_EAB_HMAC_KEY> \
--domain relay.company.com \
--domain smtp.company.com \
--deploy-hook "/usr/local/sbin/ser-certbot-deploy.sh --deploy-name outbound-relay"Result:
/opt/ser/config/tls/certs/outbound-relay.crt
/opt/ser/config/tls/private/outbound-relay.key
Manual testing is useful before wiring the script into Certbot.
Dry run using a domain:
sudo /usr/local/sbin/ser-certbot-deploy.sh \
--domains relay.company.com \
--dry-run \
--verboseDry run using a Certbot certificate name:
sudo /usr/local/sbin/ser-certbot-deploy.sh \
--cert-name relay.company.com \
--dry-run \
--verboseDry run using a Certbot certificate name with a custom SER deploy filename:
sudo /usr/local/sbin/ser-certbot-deploy.sh \
--cert-name relay.company.com \
--deploy-name outbound-relay \
--dry-run \
--verbose| Option | Purpose | When to use |
|---|---|---|
-l, --lineage PATH |
Advanced manual override for the Certbot lineage path | Usually not needed; Certbot provides RENEWED_LINEAGE during deploy hooks |
-n, --cert-name NAME |
Manual mode shortcut for /etc/letsencrypt/live/<cert-name> |
Useful for manual testing |
-N, --deploy-name NAME |
Filename base for deployed SER cert/key files | Use when SER should use a name different from the certificate's first domain |
-d, --domains DOMAINS |
Domain names used for manual mode and deploy-name fallback | Usually provided by Certbot as RENEWED_DOMAINS during deploy hooks |
-s, --service NAME |
Override the SER service name to restart | Use only if your install uses a custom systemd service name |
-o, --owner USER |
Override owner for deployed files and created directories | Use only if your SER install uses a custom service account |
-g, --group GROUP |
Override group for deployed files and created directories | Use only if your SER install uses a custom group |
-f, --log-file PATH |
Optional log file path | If used, configure logrotate |
-t, --dry-run |
Show what would happen but do not modify files or restart services | Recommended for testing |
-r, --no-reload |
Deploy files but do not restart the service | Useful for controlled maintenance windows |
-v, --verbose |
Print more details | Useful for troubleshooting |
-h, --help |
Show help output | Use anytime |
| Setting | Default |
|---|---|
| Certificate directory | /opt/ser/config/tls/certs |
| Private key directory | /opt/ser/config/tls/private |
| Service name | ser-connector |
| Owner | ser-connector |
| Group | root |
| Certificate directory mode | 0755 |
| Private key directory mode | 0750 |
| Certificate file mode | 0644 |
| Private key file mode | 0600 |
The script logs to stdout/stderr and also attempts to write to syslog using the tag:
ser-certbot-deploy
View logs with:
journalctl -t ser-certbot-deployView Certbot service logs with:
journalctl -u certbot.serviceOptional file logging can be enabled with:
--log-file /var/log/ser-certbot-deploy.logIf file logging is used long term, configure log rotation.
The script performs several safety checks before and during deployment. The SER connector is restarted because the service does not support reload:
- verifies source certificate and private key files exist
- verifies required commands are available
- verifies owner and group exist
- creates destination directories with expected ownership and permissions
- backs up existing deployed files before replacement
- installs certificate and key with explicit ownership and modes
- validates that the certificate and private key match
- restarts the SER service after deployment
- attempts rollback if service restart fails after deployment
If the deploy hook is saved in Certbot's renewal configuration, it will be reused during future renewals.
Check the renewal configuration with:
sudo cat /etc/letsencrypt/renewal/relay.company.com.confTo test renewal behavior without replacing certificates, use:
sudo certbot renew --dry-runFor production, prefer the standard deploy-hook form:
--deploy-hook "/usr/local/sbin/ser-certbot-deploy.sh"Only pass --deploy-name when the SER destination filename should be different from the certificate's first domain.
Avoid placing the script in a user home directory for production use. Certbot renewals are normally run by system automation, and the hook should not depend on a user home path.