Use Mailcow inside Tailscale network with ACME certificate.

At first install and configure Mailcow and Tailscale.

Change working directory.

$ cd /opt/mailcow-dockerized/

Edit Mailcow configuration file to alter HTTP port as it will be not used and listen on HTTP/S only on Tailscale IP address.

vim mailcow.conf
[...]
# ------------------------------
# HTTP/S Bindings
# ------------------------------

# You should use HTTPS, but in case of SSL offloaded reverse proxies:
# Might be important: This will also change the binding within the container.
# If you use a proxy within Docker, point it to the ports you set below.
# Do _not_ use IP:PORT in HTTP(S)_BIND or HTTP(S)_PORT
# IMPORTANT: Do not use port 8081, 9081 or 65510!
# Example: HTTP_BIND=1.2.3.4
# For IPv4 leave it as it is: HTTP_BIND= & HTTPS_PORT=
# For IPv6 see https://mailcow.github.io/mailcow-dockerized-docs/post_installation/firststeps-ip_bindings/

HTTP_PORT=7081
HTTP_BIND=100.200.100.50

HTTPS_PORT=443
HTTPS_BIND=100.200.100.50

[...]

Define basic Nginx configuration for ACME challenge.

$ tee data/conf/nginx/letsencrypt_site.conf <<EOF
server {
  server_name _;
  listen 80;
  listen [::]:80;

  server_tokens off;

  error_log  /var/log/nginx/error.log;
  access_log /var/log/nginx/access.log;
  root /web;

  location ^~ /.well-known/acme-challenge/ {
    allow all;
    default_type "text/plain";
  }
  location / {
    deny all;
    return 403;
  }
}
EOF
server {
  server_name _;
  listen 80;
  listen [::]:80;

  server_tokens off;

  error_log  /var/log/nginx/error.log;
  access_log /var/log/nginx/access.log;
  root /web;

  location ^~ /.well-known/acme-challenge/ {
    allow all;
    default_type "text/plain";
  }
  location / {
    deny all;
    return 403;
  }
}

Edit Docker compose file to append port 80.

vim docker-compose.yml
[...]

    nginx-mailcow:
      depends_on:
        - sogo-mailcow
        - php-fpm-mailcow
        - redis-mailcow
      image: nginx:mainline-alpine
      dns:
        - ${IPV4_NETWORK:-172.22.1}.254
      command: /bin/sh -c "envsubst < /etc/nginx/conf.d/templates/listen_plain.template > /etc/nginx/conf.d/listen_plain.active &&
        envsubst < /etc/nginx/conf.d/templates/listen_ssl.template > /etc/nginx/conf.d/listen_ssl.active &&
        envsubst < /etc/nginx/conf.d/templates/sogo.template > /etc/nginx/conf.d/sogo.active &&
        . /etc/nginx/conf.d/templates/server_name.template.sh > /etc/nginx/conf.d/server_name.active &&
        . /etc/nginx/conf.d/templates/sites.template.sh > /etc/nginx/conf.d/sites.active &&
        . /etc/nginx/conf.d/templates/sogo_eas.template.sh > /etc/nginx/conf.d/sogo_eas.active &&
        nginx -qt &&
        until ping phpfpm -c1 > /dev/null; do sleep 1; done &&
        until ping sogo -c1 > /dev/null; do sleep 1; done &&
        until ping redis -c1 > /dev/null; do sleep 1; done &&
        until ping rspamd -c1 > /dev/null; do sleep 1; done &&
        exec nginx -g 'daemon off;'"
      environment:
        - HTTPS_PORT=${HTTPS_PORT:-443}
        - HTTP_PORT=${HTTP_PORT:-80}
        - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
        - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
        - TZ=${TZ}
        - SKIP_SOGO=${SKIP_SOGO:-n}
        - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
        - ADDITIONAL_SERVER_NAMES=${ADDITIONAL_SERVER_NAMES:-}
      volumes:
        - ./data/web:/web:ro,z
        - ./data/conf/rspamd/dynmaps:/dynmaps:ro,z
        - ./data/assets/ssl/:/etc/ssl/mail/:ro,z
        - ./data/conf/nginx/:/etc/nginx/conf.d/:z
        - ./data/conf/rspamd/meta_exporter:/meta_exporter:ro,z
        - sogo-web-vol-1:/usr/lib/GNUstep/SOGo/
      ports:
        - "${HTTPS_BIND:-}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
        - "${HTTP_BIND:-}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
        - 80:80
      restart: always
      networks:
        mailcow-network:
          aliases:
            - nginx

[...]

Recreate and restart containers altered by the earlier changes.

$ docker-compose up -d
[+] Running 18/18
 ✔ Container mailcowdockerized-solr-mailcow-1       Running                0.0s 
 ✔ Container mailcowdockerized-memcached-mailcow-1  Running                0.0s 
 ✔ Container mailcowdockerized-redis-mailcow-1      Running                0.0s 
 ✔ Container mailcowdockerized-watchdog-mailcow-1   Running                0.0s 
 ✔ Container mailcowdockerized-dockerapi-mailcow-1  Running                0.0s 
 ✔ Container mailcowdockerized-olefy-mailcow-1      Running                0.0s 
 ✔ Container mailcowdockerized-unbound-mailcow-1    Running                0.0s 
 ✔ Container mailcowdockerized-php-fpm-mailcow-1    Running                0.0s 
 ✔ Container mailcowdockerized-clamd-mailcow-1      Running                0.0s 
 ✔ Container mailcowdockerized-sogo-mailcow-1       Running                0.0s 
 ✔ Container mailcowdockerized-mysql-mailcow-1      Running                0.0s 
 ✔ Container mailcowdockerized-nginx-mailcow-1      Started                1.1s 
 ✔ Container mailcowdockerized-postfix-mailcow-1    Running                0.0s 
 ✔ Container mailcowdockerized-dovecot-mailcow-1    Running                0.0s 
 ✔ Container mailcowdockerized-ofelia-mailcow-1     Running                0.0s 
 ✔ Container mailcowdockerized-netfilter-mailcow-1  Running                0.0s 
 ✔ Container mailcowdockerized-rspamd-mailcow-1     Running                0.0s 
 ✔ Container mailcowdockerized-acme-mailcow-1       Started                0.9s 

You can later restart Nginx container after altering site configuration.

$ docker-compose restat nginx-mailcow
ko-fi