Files
docs/deployments/services/email/mailcow.md
T
nicole a46f7a3e54
Automatic Documentation Deployment / Sync Docs to https://kb.bunny-lab.io (push) Successful in 8s
Update deployments/services/email/mailcow.md
2026-06-19 15:37:53 -06:00

6.0 KiB


tags:

  • Mailcow
  • Email
  • Docker

!!! warning "Under Construction" The deployment of Mailcow is mostly correct here. Mail protocol ports should be forwarded directly to the Mailcow server from the firewall. Traefik should only sit in front of Mailcow for web traffic, and it should pass HTTPS through transparently so Mailcow can manage and serve its own certificates.

Purpose

The purpose of this document is to illustrate how to deploy Mailcow in a dockerized format.

!!! note "Assumptions" It is assumed that you are deploying Mailcow into an existing Ubuntu Server environment. If you are using a different operating system, refer to the official documentation.

Setting Up Docker

Go ahead and set up docker and docker-compose with the following commands:

sudo su # (1)
curl -sSL https://get.docker.com/ | CHANNEL=stable sh # (2)
apt install docker-compose-plugin # (3)
systemctl enable --now docker # (4)
  1. Make yourself root.
  2. Install Docker
  3. Install Docker-Compose
  4. Make docker run automatically when the server is booted.

Download and Deploy Mailcow

Run the following commands to pull down the mailcow deployment files and install them with docker. Go get a cup of coffee as the docker compose pull command may take a while to run.

!!! note "Potential Docker Compose Issues" If you run the docker-compose pull command and it fails for some reason, change the command to docker compose pull instead. This is just the difference between the plugin version of compose versus the standalone version. Both will have the same result.

cd /opt
git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized
./generate_config.sh # (1)
docker-compose pull # (2)
docker-compose up -d
  1. Generate a configuration file. Use a FQDN (host.domain.tld) as hostname when asked.

  2. If you get an error about the ports of the nginx-mailcow service in the docker-compose.yml stack, change the ports for that service as follows:

    ports:
      - "${HTTPS_BIND:-0.0.0.0}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
      - "${HTTP_BIND:-0.0.0.0}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
    

Firewall / NAT Configuration

Forward Mailcow service ports as follows:

WAN :80  -> Traefik :80
WAN :443 -> Traefik :443

WAN :25   -> Mailcow :25
WAN :465  -> Mailcow :465
WAN :587  -> Mailcow :587
WAN :993  -> Mailcow :993
WAN :995  -> Mailcow :995
WAN :110  -> Mailcow :110
WAN :143  -> Mailcow :143
WAN :4190 -> Mailcow :4190

Mail protocol ports should be sent directly to the Mailcow server. Traefik should not terminate or proxy the SMTP, SMTPS, Submission, IMAP, IMAPS, POP3, POP3S, or ManageSieve ports.

Reverse-Proxy Configuration

For the purposes of this document, it will be assumed that you are deploying Mailcow behind Traefik for web traffic only. Traefik should pass HTTPS through transparently, allowing Mailcow to manage and serve its own certificates.

You can use the following dynamic configuration file to achieve this:

# =====================================================================
# Mailcow / Traefik Dynamic Configuration
# Hostname: mail.bunny-lab.io
#
# Mailcow owns certificates.
# Traefik forwards HTTP and passes HTTPS through.
# Mail protocol ports are handled directly by pfSense -> Mailcow.
# =====================================================================

http:
  routers:
    mailcow-http:
      entryPoints:
        - web
      rule: Host(`mail.bunny-lab.io`)
      service: mailcow-http
      priority: 100

  services:
    mailcow-http:
      loadBalancer:
        passHostHeader: true
        servers:
          - url: "http://192.168.3.61:80"

tcp:
  routers:
    mailcow-https-passthrough:
      entryPoints:
        - websecure
      rule: HostSNI(`mail.bunny-lab.io`)
      service: mailcow-https
      tls:
        passthrough: true

  services:
    mailcow-https:
      loadBalancer:
        servers:
          - address: "192.168.3.61:443"

Traefik-Specific Configuration

Traefik only needs the standard HTTP and HTTPS entrypoints for Mailcow web traffic. Mail protocol ports should not be exposed through Traefik if the firewall is forwarding those ports directly to Mailcow.

#Entrypoints
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"

#Ports
- "80:80"
- "443:443"

Do not add Mailcow mail protocol entrypoints or port bindings to Traefik unless you intentionally want Traefik to proxy those ports.

Certificate Validation

Mailcow should manage and serve the certificate for mail.bunny-lab.io.

To verify the active Mailcow certificate on disk, run the following on the Mailcow server:

cd /opt/mailcow-dockerized

openssl x509 \
  -in /opt/mailcow-dockerized/data/assets/ssl/cert.pem \
  -noout -subject -issuer -dates -serial -fingerprint -sha256

If the certificate has renewed but services are still presenting an old certificate, restart the Mailcow services that serve TLS:

cd /opt/mailcow-dockerized
docker compose restart postfix-mailcow dovecot-mailcow nginx-mailcow

Login to Mailcow

At this point, the Mailcow server has been deployed so you can log into it.

  • Administrators: https://${MAILCOW_HOSTNAME}/admin (Username: admin | Password: moohoo)
  • Regular Mailbox Users: https://${MAILCOW_HOSTNAME} (FQDN only)

Mail-Client Considerations

You need to ensure that you generate an app password if you have MFA enabled within Mailcow. (MFA is non-functional in Roundcube/SoGo, you set it up via Mailcow itself). You can access it via the Mailcow configuration page: https://mail.bunny-lab.io/user, then look for the "App Passwords" tab.

Running Updates

If you want to run updates, just SSH into the server, and navigate to /opt/mailcow-dockerized and run ./update.sh. I recommend avoiding the IPv6 implementation section. Be patient, and the upgrade will be fully-automated.