In this post I will outline, how I integrated Mailcow into an existing setup of docker containers and a reverse proxy, including the distribution of required SSL certificates.
Reverse Proxy
On my webserver, I run several services along with a Mailcow installation as my mailserver. All services are run as docker containers. Since only one service may listen on HTTP(S) ports 80 and 443, I use a reverse proxy. It handles all HTTP and HTTPS connections, dispatches them to the respective containers, and manages all SSL certificates issued by Let’s Encrypt. I chose to use Nginx Proxy for that. To expose a service on a subdomain and generate valid SSL certificates, I just need to add the following environment variables to the container specification:
- VIRTUAL_HOST=subdomain.example.com
- VIRTUAL_PORT=8080 #HTTP port of the container
- LETSENCRYPT_HOST=subdomain.example.com
- LETSENCRYPT_EMAIL=letsencryptemail@example.com
Add Mailcow to the Reverse Proxy
All external HTTP(S) connections are done via the reverse proxy. Therefore, I made the web interface of Mailcow only directly accessible internally, by changing mailcow.conf
:
HTTP_PORT=1234
HTTP_BIND=127.0.0.1
HTTPS_PORT=1235
HTTPS_BIND=127.0.0.1
Serving the web interface of Mailcow via the reverse proxy is fairly easy. The configuration for that is done in the docker-compose.override.yml
file in Mailcow’s root folder:
version: '2.1'
services:
nginx-mailcow:
environment:
- VIRTUAL_HOST=mail.example.com
- VIRTUAL_PORT=1234
- LETSENCRYPT_HOST=mail.example.com
- LETSENCRYPT_EMAIL=letsencryptemail@example.com
Postfix and Dovecot
Mailcow uses Postfix as an MTA (for SMTP) and Dovecot as an MDA (for POP3 and IMAP). Both services support SSL encryption for their connections, so the certificates generated by the reverse proxy need to be supplied to them as well.
All certificates generated by nginx-proxy are located in a single folder. This folder is provided to postfix and dovecot by a simple symlink:
ln -s $MAILCOW_ROOT/data/assets/ssl $LE_CERT_FOLDER
Postfix and Dovecot expect the correct certificates to be available with specific file names, which are again symlinked:
cd $LE_CERT_FOLDER
ln -s key.pem mail.example.com/key.pem
ln -s dhparams.pem dhparam.pem
ln -s cert.pem mail.example.com/fullchain.pem
The only remaining problem is that the reverse proxy may renew the certificates and I need to notify Postfix and Dovecot, so that they can load the new certificates. I do that using a small script:
#!/bin/bash
/usr/bin/docker exec $(/usr/bin/docker ps -qaf name=postfix-mailcow) postfix reload
/usr/bin/docker exec $(/usr/bin/docker ps -qaf name=dovecot-mailcow) dovecot reload
This script is called by a cronjob, whenever the certificates did change within the last day.
0 3 * * * find $MAILCOW_ROOT/data/assets/ssl/mail.example.com/key.pem -mmin -1440 -exec <some-folder>/reload_ssl_certs.sh