Bloggings

Back

Mailcow with Nginx reverse proxy (updated)

2025-04-19

Using other web services with Mailcow

This is roughly what I did to have an Nginx web server on the same machine as dockerized Mailcow

Note: mail.xxxxx.com is your mail server (MX), yourdomain.com is whatever domain you wish to use.

  1. Change the default http and https ports (for example 8480 and 8443)
    and set SKIP_LETS_ENCRYPT=y in /opt/mailcow-dockerized/mailcow.conf
  2. Restart Mailcow by executing
    cd /opt/mailcow-dockerized;docker compose restart
  3. Make sure the DNS entry mail.xxxxx.com points to the server
  4. mkdir -p /var/www/html/letsencrypt/.well-known/acme-challenge
  5. Create /etc/nginx/letsencrypt_path. This can be re-used in your other domains. # url for letsencrypt location ^~ /.well-known/acme-challenge/ { allow all; default_type "text/plain"; # Path can be used for cert-validation on all domains root /var/www/html/letsencrypt; break; }
  6. Stop the nginx server systemctl stop nginx
  7. Run certbot certonly -d mail.xxxxx.com (select 2 standalone)
  8. Create /etc/nginx/sites-available/mail.xxxxx.com
  9. server { listen 443 ssl; listen [::]:443 ssl; http2 on; server_name mail.xxxxx.com; charset UTF-8; access_log /var/log/nginx/access.mail.xxxxx.com; error_log /var/log/nginx/error.mail.xxxxx.com; include snippets/error_pages.conf; ssl_certificate /etc/letsencrypt/live/mail.xxxxx.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/mail.xxxxx.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams-2048.pem; include /etc/nginx/letsencrypt_path; location / { proxy_pass http://127.0.0.1:8480; proxy_buffering off; include /etc/nginx/proxy_params; } } server { listen 80; listen [::]:80; server_name mail.xxxxx.com; return 301 https://mail.xxxxx.com; }
  10. ln -s /etc/nginx/sites-available/mail.xxxxx.com
    /etc/nginx/sites-enabled/mail.xxxxx.com
  11. Test with nginx -t, if all is well run sudo systemctl start nginx
  12. Pointing your browser to https://mail.xxxxx.com should show you the Mailcow login page.

Problem

The problem is that Mailcow can no longer use port 80 to update it's ssl certificates that are used by postfix and dovecot. Instead certbot puts them in /etc/letsencrypt/live. To fix this the following script runs from crontab daily.

#!/usr/bin/env bash # we are running behind an nginx proxy, where certbot is run by systemd, # so the mail.xxxxx.com certificates are updated, but not copied to the mailcow folder # in case the certificate isn't renewed automatically, run this: # sudo certbot -n certonly --webroot -w /var/www/html/letsencrypt -d mail.xxxxx.com t1="/etc/letsencrypt/live/mail.xxxxx.com/fullchain.pem" t2="/opt/mailcow-dockerized/data/assets/ssl/mail.xxxxx.com/cert.pem" # test if certificate has been updated differ=$(cmp -b $t1 $t2 | grep -c "differ") [[ "$differ" = "0" ]] && exit 0 # these files are required in /data/assets/ssh/mail.xxxxx.com cp /etc/letsencrypt/live/mail.xxxxx.com/fullchain.pem /opt/mailcow-dockerized/data/assets/ssl/mail.xxxxx.com/cert.pem cp /etc/letsencrypt/live/mail.xxxxx.com/privkey.pem /opt/mailcow-dockerized/data/assets/ssl/mail.xxxxx.com/key.pem # these files are required in /data/assets/ssl/ (turns out sending failed otherwise) cp /etc/letsencrypt/live/mail.xxxxx.com/fullchain.pem /opt/mailcow-dockerized/data/assets/ssl/cert.pem cp /etc/letsencrypt/live/mail.xxxxx.com/privkey.pem /opt/mailcow-dockerized/data/assets/ssl/key.pem # update postfix & docker docker exec $(/usr/bin/docker ps -qaf name=postfix-mailcow) postfix reload docker exec $(/usr/bin/docker ps -qaf name=dovecot-mailcow) dovecot reload

Inspiration: Felix Moesbauer

Don't forget to check and correct if necessary your DANE and MTA-STS records if you use them

DANE

On your mail server:

apt install hash-slinger -y

Create a TLSA record:

tlsa --create --selector 1 -p 25 --certificate /etc/letsencrypt/live/mail.xxxxx.com/fullchain.pem mail.xxxxx.com

The last line or the result will be something like:

_25._tcp.mail.xxxxx.com. IN TLSA 3 1 1 443ac7c5c70fbfbc...

Enter this TLSA line in your mail server's DNS record.

Do the same for the following ports:

_110._tcp.mail.xxxxx.com _143._tcp.mail.xxxxx.com _465._tcp.mail.xxxxx.com _587._tcp.mail.xxxxx.com _993._tcp.mail.xxxxx.com _995._tcp.mail.xxxxx.com

You can check your DANE records at huque.com

dane success

MTA-STS

This is a fallback to DANE, you can run both.

Note: https access to mta-sts.yourdomain.com is obligatory

  1. Create /var/www/html/mta-sts/.well-known/mta-sts.txt with the following content:
    version: STSv1 mode: enforce max_age: 172800 mx: mail.xxxxx.com
  2. Create the DNS record _mta-sts.mail.xxxxx.com as TXT with "v=STSv1; id=INSERT AN ID, EX. 202511101644"
  3. Create the DNS record _mta-sts.yourdomain.com as CNAME pointing to _mta-sts.mail.xxxxx.com (your mail server)
  4. Create the DNS record mta-sts.yourdomain.com pointing to the ip of the web server
  5. You may need to wait for the DNS records to propagate
  6. Stop Nginx with systemctl stop nginx
  7. Create a letsencrypt certificate with certbot certonly -d mta-sts.yourdomain.com (select 2 standalone)
  8. Create mta-sts.yourdomain.com in Nginx:
  9. /etc/sites-available/mta-sts.yourdomain.com
    server { listen 443 ssl; listen [::]:443 ssl; http2 on; charset UTF-8; access_log /var/log/nginx/access.mts-sta.yourdomain.com; error_log /var/log/nginx/error.mts-sta.yourdomain.com; include snippets/error_pages.conf; ssl_certificate /etc/letsencrypt/live/mta-sts.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/mta-sts.yourdomain.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams-2048.pem; include /etc/nginx/letsencrypt_path; root /var/www/html/mta-sts; index index.html index.htm; server_name mta-sts.yourdomain.com; location / { try_files $uri $uri/ =404; } } server { listen 80; listen [::]:80; server_name mta-sts.yourdomain.com; return 301 https://mta-sts.yourdomain.com; }
  10. ln -s /etc/nginx/sites-available/mta-sts.yourdomain.com
    /etc/nginx/sites-enabled/mta-sts.yourdomain.com
  11. Test with nginx -t and if all goes well start nginx systemctl start nginx

You can check your domain's MTA-STS at mxtoolbox.com

 

All this worked for me :-) I hope it does for you!

 


ยง