Mailcow is a powerful and flexible open-source mail server suite that allows you to manage your email communications securely and efficiently. With Mailcow, you can create multiple email domains and accounts, manage users, and leverage features like spam filtering and encryption, giving you full control over your email infrastructure. This guide will show you how to set up Mailcow using Docker Compose behind an existing Traefik reverse proxy.

Changelog

DateChange
2025-09-18Initial Version: First version of this guide was created.

1. Prerequisites

This guide assumes you have a fully functional server environment with the following components already set up:

  • Traefik v3 and CrowdSec with Docker Compose: This is the foundation for our reverse proxy, security, and certificate management.
  • Docker and Docker Compose: Must be installed on your server.
  • Required Tools: You will need git and jq. Install them with:
    sudo apt update && sudo apt install -y git jq
    
  • sudo or root access.

2. Directory Structure

First, create a dedicated directory for your Mailcow installation.

sudo mkdir -p /opt/containers/mailcow

3. Clone the Mailcow Repository

Clone the latest version of the Mailcow Dockerized project from GitHub into the directory you just created.

sudo git clone https://github.com/mailcow/mailcow-dockerized /opt/containers/mailcow

4. Generate the Configuration File

Navigate into the new directory and run the configuration generation script.

cd /opt/containers/mailcow
./generate_config.sh

You will be prompted for the following information:

  • Mail server hostname (FQDN): Enter the fully qualified domain name for your mail server, for example, mail.your-domain.com.
  • Timezone: Press Enter to accept the default or provide your own.
  • Available Branches: Press 1 to select the master branch.

5. Configure for Traefik Integration

To make Mailcow work with our external Traefik instance, we need to create an override file and modify the main configuration.

5.1. Create docker-compose.override.yml

This file contains all our customizations, telling Mailcow to use the external proxy network and defining labels for Traefik to route traffic correctly. It also sets up a certdumper service, which is crucial for sharing Traefik’s Let’s Encrypt certificates with Mailcow’s services (Postfix and Dovecot).

Create the file:

sudo tee docker-compose.override.yml > /dev/null << 'EOF'
networks:
  proxy:
    name: proxy
    external: true

services:
  certdumper:
    image: ghcr.io/kereis/traefik-certs-dumper:latest
    restart: unless-stopped
    network_mode: none
    command: --restart-containers mailcowdockerized-postfix-mailcow-1,mailcowdockerized-dovecot-mailcow-1
    volumes:
      - /opt/containers/traefik-stack/traefik/certs:/traefik:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data/assets/ssl:/output:rw
    environment:
      DOMAIN: ${MAILCOW_HOSTNAME}
      ACME_FILE_PATH: "/traefik/acme.json"
    healthcheck:
      test: ["CMD", "/usr/bin/healthcheck"]
      interval: 30s
      timeout: 10s
      retries: 5

  nginx-mailcow:
    ports: !reset
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"

      # HTTP Router (for ACME challenge and redirect)
      - "traefik.http.routers.nginx-mailcow.entrypoints=web"
      - "traefik.http.routers.nginx-mailcow.rule=Host(`mail.your-domain.com`, `autodiscover.your-domain.com`, `autoconfig.your-domain.com`, `mta-sts.your-domain.com`)"

      # Middleware for redirect
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
      - "traefik.http.routers.nginx-mailcow.middlewares=redirect-to-https@docker"

      # HTTPS Router
      - "traefik.http.routers.nginx-mailcow-secure.entrypoints=websecure"
      - "traefik.http.routers.nginx-mailcow-secure.rule=Host(`mail.your-domain.com`, `autodiscover.your-domain.com`, `autoconfig.your-domain.com`, `mta-sts.your-domain.com`)"
      - "traefik.http.routers.nginx-mailcow-secure.tls=true"
      - "traefik.http.routers.nginx-mailcow-secure.tls.certresolver=tls_resolver"
      - "traefik.http.routers.nginx-mailcow-secure.service=nginx-mailcow"
      - "traefik.http.routers.nginx-mailcow-secure.middlewares=security-headers@file,crowdsec-bouncer@docker"

      # Service Definition
      - "traefik.http.services.nginx-mailcow.loadbalancer.server.port=8080"
EOF
⚠️ IMPORTANT

You must replace your-domain.com with your actual domain name. You can do this manually or with the following command:

sudo sed -i "s/your-domain.com/your-actual-domain.com/g" docker-compose.override.yml

5.2. Adjust mailcow.conf

Next, edit the main configuration file to integrate with Traefik and prevent conflicts.

sudo nano /opt/containers/mailcow/mailcow.conf

Make the following critical changes:

  1. Change Internal Ports: Prevent conflicts with Traefik’s ports 80 and 443. This tells Mailcow’s internal Nginx to listen on different ports, which Traefik will then route to.

    HTTP_PORT=8080
    HTTPS_PORT=8443
    
  2. Disable Mailcow’s Let’s Encrypt: Traefik is responsible for all certificate management.

    SKIP_LETS_ENCRYPT=y
    
  3. Add SAN for Internal TLS: This ensures Mailcow’s internal services trust the Traefik-provided certificate for the mail server hostname.

    ADDITIONAL_SAN=${MAILCOW_HOSTNAME}
    

5.3. Activate MTA-STS

MTA-STS (Mail Transfer Agent-Strict Transport Security) is a security standard that helps prevent man-in-the-middle attacks by ensuring emails are transmitted over secure TLS connections. We’ll create a policy file that Mailcow will serve.

First, create the required directory:

sudo mkdir -p /opt/containers/mailcow/data/web/.well-known/

Now, create the policy file. We will start with an enforce policy for production use.

sudo tee /opt/containers/mailcow/data/web/.well-known/mta-sts.txt > /dev/null << 'EOF'
version: STSv1
mode: enforce
max_age: 15552000
mx: mail.your-domain.com
EOF
⚠️ IMPORTANT

You must replace your-domain.com with your actual domain name in the file above. You can do this with the following command:

sudo sed -i "s/your-domain.com/your-actual-domain.com/g" /opt/containers/mailcow/data/web/.well-known/mta-sts.txt

6. Launch Mailcow

You can now start the Mailcow stack.

cd /opt/containers/mailcow
docker compose up -d
💡 TROUBLESHOOTING: POOL OVERLAPS

If you encounter an error like Pool overlaps with other one on this address space, it means another Docker container is already using the default IP range Mailcow wants (e.g., 172.22.0.0/16). The quickest solution is to stop the conflicting container, start Mailcow first to claim the network, and then restart the other container.

7. DNS Configuration

Correct DNS setup is critical for a mail server to function.

7.1. Reverse DNS (PTR Record)

Your server’s Reverse DNS (PTR) record must match the hostname you configured in mailcow.conf (mail.your-domain.com). This is usually set in your server provider’s control panel and is essential for not being marked as spam.

7.2. DNS Records

In your domain’s DNS management panel, add the following records. We start with a non-restrictive DMARC policy (p=none) to prevent legitimate emails from being rejected during the initial setup. You can switch to p=quarantine or p=reject later once you have confirmed that all your email sources are correctly configured.

NameTypeValue
mailAyour-server-ip
autodiscoverCNAMEmail.your-domain.com.
autoconfigCNAMEmail.your-domain.com.
imapCNAMEmail.your-domain.com.
pop3CNAMEmail.your-domain.com.
smtpCNAMEmail.your-domain.com.
@MX 0mail.your-domain.com.
SPF
@TXTv=spf1 mx a -all
DMARC
_dmarcTXTv=DMARC1; p=none; rua=mailto:admin@your-domain.com
MTA-STS
mta-stsCNAMEmail.your-domain.com.
_mta-stsTXTv=STSv1; id=2025091401
_smtp._tlsTXTv=TLSRPTv1; rua=mailto:admin@your-domain.com
Service Records
_autodiscover._tcpSRV0 1 443 mail.your-domain.com.
_caldavs._tcpSRV0 1 443 mail.your-domain.com.
_carddavs._tcpSRV0 1 443 mail.your-domain.com.
_imaps._tcpSRV0 1 993 mail.your-domain.com.
_pop3s._tcpSRV0 1 995 mail.your-domain.com.
_submission._tcpSRV0 1 587 mail.your-domain.com.
_smtps._tcpSRV0 1 465 mail.your-domain.com.

A DKIM record will be added later after it’s generated by Mailcow.

8. Firewall Configuration

Your firewall must allow traffic on several ports for email services to be reachable.

PortServiceProtocol
25SMTPTCP
587SubmissionTCP
465SMTPSTCP
143IMAPTCP
993IMAPSTCP
110POP3TCP
995POP3STCP
4190ManageSieveTCP

Use ufw to open these ports:

sudo ufw allow 25/tcp
sudo ufw allow 587/tcp
sudo ufw allow 465/tcp
sudo ufw allow 143/tcp
sudo ufw allow 993/tcp
sudo ufw allow 110/tcp
sudo ufw allow 995/tcp
sudo ufw allow 4190/tcp
sudo ufw status

9. Initial Mailcow Setup

Navigate to your Mailcow UI at https://mail.your-domain.com.

  • Default Username: admin
  • Default Password: moohoo

First, change the administrator password under System -> Configuration -> Edit.

10.1. Add Your Domain

Go to Email -> Configuration and click “Add domain”. Enter your main domain (e.g., your-domain.com, not mail.your-domain.com).

10.2. Generate DKIM Key

A DKIM key is essential for email authentication.

  1. Go to System -> Configuration.
  2. Navigate to the Options -> ARC/DKIM keys tab.
  3. A 2048-bit key should already be generated for your domain. Copy the public key text from the text box.
  4. Go back to your DNS provider and add a new TXT record:
    • Name: dkim._domainkey
    • Value: Paste the entire copied key, including the v=DKIM1;k=rsa;p=... part.

11. Testing and Verification

After waiting for DNS propagation, thoroughly test your setup.

11.1. Internal DNS and Certificate Check

  • Mailcow DNS Check: In the Mailcow UI, go to Email -> Configuration -> DNS next to your domain for an internal check of your records.
  • Verify Certificates: Ensure that the certificates from Traefik have been correctly passed to Mailcow’s services. Run these commands, replacing mail.your-domain.com with your mail server’s FQDN:
    # Check SMTP certificate
    echo "Q" | openssl s_client -starttls smtp -crlf -connect mail.your-domain.com:587
    
    # Check IMAP certificate
    echo "Q" | openssl s_client -starttls imap -crlf -connect mail.your-domain.com:143
    
    In the output, you should see a certificate chain issued by Let’s Encrypt. If you see a “self-signed certificate”, there is a problem with the certdumper service or the ADDITIONAL_SAN variable in mailcow.conf. Check the certdumper logs:
    cd /opt/containers/mailcow
    docker compose logs certdumper
    

11.2. External Testing Tools

  • mail-tester.com: Send an email to the address provided on their site to get a score out of 10.
  • mxtoolbox.com: Provides various checks for your mail server.
  • checktls.com/TestReceiver: Use this to specifically verify your MTA-STS configuration.

12. Create Mailboxes and Log In

Under Email -> Mailboxes, you can add new users. Once a user is created, you can access the webmail interface (SOGo) by clicking Apps -> Webmail in the main UI or by going directly to https://mail.your-domain.com/SOGo/.

Conclusion

You have now successfully deployed a full-featured Mailcow e-mail server. By leveraging Docker for containerization and Traefik for reverse proxying and certificate management, you have a secure, robust, and maintainable mail solution. This setup provides you with complete control over your email, enhanced security through features like MTA-STS, and the flexibility to manage multiple domains and users with ease.

📚OFFICIAL MAILCOW DOCUMENTATION 🛡️TEST YOUR EMAIL SCORE