diff options
| author | Philip Wittamore <philip@wittamore.com> | 2026-07-12 11:08:50 +0200 |
|---|---|---|
| committer | Philip Wittamore <philip@wittamore.com> | 2026-07-12 11:08:50 +0200 |
| commit | 420ceb49b53a8fa9d8ba8443e42e50cd7bd7cea9 (patch) | |
| tree | 3009f0564d75e3f7d1ca38507e7ebb92deaded07 /articles/2025/Mailcow-with-Nginx-reverse-proxy.html | |
update
Diffstat (limited to 'articles/2025/Mailcow-with-Nginx-reverse-proxy.html')
| -rwxr-xr-x | articles/2025/Mailcow-with-Nginx-reverse-proxy.html | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/articles/2025/Mailcow-with-Nginx-reverse-proxy.html b/articles/2025/Mailcow-with-Nginx-reverse-proxy.html new file mode 100755 index 0000000..657b214 --- /dev/null +++ b/articles/2025/Mailcow-with-Nginx-reverse-proxy.html @@ -0,0 +1,207 @@ +<!doctype html> +<html lang="EN"> + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link rel="stylesheet" href="/style.css"> + <title>Mailcow with Nginx reverse proxy</title> + </head> + <body> + <header> + <h1>Bloggings</h1> + <a href="/index.php">Back</a> + + </header> + +<main> +<article> +<h2>Mailcow with Nginx reverse proxy (updated)</h2> +<h3>2025-04-19</h3> +<h4>Using other web services with Mailcow</h4> +<p>This is roughly what I did to have an Nginx web server on the same machine as dockerized +<a href="https://mailcow.email/">Mailcow</a></p> +<p><b>Note:</b> mail.xxxxx.com is your mail server (MX), yourdomain.com is whatever domain you wish to use.</p> +<ol> +<li>Change the default http and https ports (for example 8480 and 8443)<br> +and set <b>SKIP_LETS_ENCRYPT=y</b> in /opt/mailcow-dockerized/mailcow.conf</li> +<li>Restart Mailcow by executing <br> +<b>cd /opt/mailcow-dockerized;docker compose restart</b></li> +<li>Make sure the DNS entry mail.xxxxx.com points to the server</li> +<li><b>mkdir -p /var/www/html/letsencrypt/.well-known/acme-challenge</b></li> +<li>Create /etc/nginx/letsencrypt_path. This can be re-used in your other domains. +<pre># 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; +} +</pre> +</li> +<li>Stop the nginx server <b>systemctl stop nginx</b></li> +<li>Run <b>certbot certonly -d mail.xxxxx.com</b> (select 2 standalone)</li> +<li>Create /etc/nginx/sites-available/mail.xxxxx.com</li> +<pre>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; +} +</pre> +<li><b>ln -s /etc/nginx/sites-available/mail.xxxxx.com <br>/etc/nginx/sites-enabled/mail.xxxxx.com</b></li> +<li>Test with <b>nginx -t</b>, if all is well run <b>sudo systemctl start nginx</b></li> +<li>Pointing your browser to https://mail.xxxxx.com should show you the Mailcow login page.</li> +</ol> +<h3>Problem</h3> +<p>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.</p> +<pre>#!/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 +</pre> +<p>Inspiration: <a href="https://felixmoessbauer.com/blog-reader/mailcow-reverse-proxy-letsencrypt.html">Felix Moesbauer</a></p> +<p>Don't forget to check and correct if necessary your DANE and MTA-STS records if you use them</p> +<h4>DANE</h4> +<p>On your mail server:</p> +<pre>apt install hash-slinger -y</pre> +<p>Create a TLSA record:</p> +<pre>tlsa --create --selector 1 -p 25 --certificate /etc/letsencrypt/live/mail.xxxxx.com/fullchain.pem mail.xxxxx.com +</pre> +<p>The last line or the result will be something like:</p> +<pre>_25._tcp.mail.xxxxx.com. IN TLSA 3 1 1 443ac7c5c70fbfbc...</pre> +<p>Enter this TLSA line in your mail server's DNS record.</p> +<p>Do the same for the following ports:</p> +<pre>_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 +</pre> +<p>You can check your DANE records at <a href="https://www.huque.com/bin/danecheck-smtp">huque.com</a></p> +<p><img src="/images/blog/dane-success.png" alt="dane success"></p> +<h4>MTA-STS</h4> +<p>This is a fallback to DANE, you can run both.</p> +<p><b>Note:</b> https access to mta-sts.yourdomain.com is obligatory</p> +<ol> +<li>Create /var/www/html/mta-sts/.well-known/mta-sts.txt with the following content:<br> +<pre>version: STSv1 +mode: enforce +max_age: 172800 +mx: mail.xxxxx.com +</pre> +</li> +<li>Create the DNS record _mta-sts.mail.xxxxx.com as TXT with "v=STSv1; id=<b>INSERT AN ID, EX. 202511101644</b>"</li> +<li>Create the DNS record _mta-sts.yourdomain.com as CNAME pointing to _mta-sts.mail.xxxxx.com (your mail server)</li> +<li>Create the DNS record mta-sts.yourdomain.com pointing to the ip of the web server</li> +<li>You may need to wait for the DNS records to propagate</li> +<li>Stop Nginx with <b>systemctl stop nginx</b></li> +<li>Create a letsencrypt certificate with <b>certbot certonly -d mta-sts.yourdomain.com</b> (select 2 standalone)</li> +<li>Create mta-sts.yourdomain.com in Nginx:</li> +<li>/etc/sites-available/mta-sts.yourdomain.com<br> +<pre>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; +} +</pre> +</li> +<li><b>ln -s /etc/nginx/sites-available/mta-sts.yourdomain.com <br>/etc/nginx/sites-enabled/mta-sts.yourdomain.com</b></li> +<li>Test with nginx -t and if all goes well start nginx <b>systemctl start nginx</b></li> +</ol> +<p>You can check your domain's MTA-STS at <a href="https://mxtoolbox.com/mta-sts.aspx">mxtoolbox.com</a><p> +<p> </p> +<p>All this worked for me :-) I hope it does for you!</p> + +</article> +</main> +<footer> + +<p> </p> + +<p>ยง</p> + </footer> +</body> +</html> + + + |
