How to self host Mailcow on docker compose

Mailcow is a docker container that has all of tools you need to be your own email server just like gmail but self-hosted and actually easy to manage ;)

It's easy to host it but before I start I will assume that: * You are running debian 11 * You already have a VPS: * Your linux distro is up-to-date (sudo apt update && sudo apt upgrade) * You have a domain name: * Have sudo access or root account * Already installed docker and docker-compose: * Already installed Nginx: * Already have a reverse proxy conf file: * Already have certbot to issue cert: * Your VPS has SMTP port unlocked

Before initial setup

We need to make sure our VPS opened SMTP port for us so do a simple test by using telnet:

sudo apt install telnet

to install telnet first! and test if we have outgoing smtp port enabled

telnet 25

This pings google's gmail server and if we get timeout error it means that our VPS does not have 25 port open so you can ask them to open it – It depends on VPS for example vultr opens it after you verify yourself and hetnzer opens it after 1 month of using the service

And we need to make a “reverse dns” in our VPS so that websites like gmail when they try to check if our domain is spam or not, we pass the test – this changes from VPS to another so please get back to your provider

System Requirements

RAM: 800 MB – 1 GB CPU: 1GHz Disk: 5GB System type: x86 or x86_64

Changes in DNS (domain side)

We need to open 2 dns entries: 1. an A dns entry with mail.[domain name] and it's target is our VPS IP 2. a MX domain name with value of root domain and it's target it mailcow domain (ex: mail.[domain name]) And optional dns entries you can find them when you visit your mailcow domain and login as the admin and click configuration then select mail setup and from there click on “DNS” button (URL should look like, [mailcow domain]/mailbox), from there you will see list of DNSes so add them one by one in your domain.

Initial setup

We need to clone mailcow git repo and generate config so:

git clone
cd mailcow-dockerized

And generate config using FQDN (Fully qualified domain name, EX:


Change config if you need to:

nano mailcow.conf

Make sure to change ports if you have other running websites/applications on same port (bind HTTPS to on port 8443 and HTTP to on port 8080 for example)

Spin it up!

Now after we done editing, and everything is cool. We need to run our container so just run:

docker-compose up -d

the -d option does not let docker post logs of running application but if you want to see logs you can run:

sudo docker-compose logs -f -t

To check if there any weird behaviors or errors


Now after we make sure it's running well. We need to serve it over the internet (called reverse proxy) so without much talk, here is our server block for mailcow:

server {
        listen [::]:80;
        listen 80;
        server_name [domain name] ;

        location / {

                proxy_set_header Host $http_host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                client_max_body_size 0;



server_name: Change this to match domain name of mailcow proxy_pass: the IP and port of our running docker image

After this you should be up and running for mailcow! :) just do not forget to run certbot --nginx to make it secure with https://

Update it

Of course after some time the image will be outdated and you need to update and what I love about docker that it's easy to update, really just to do it run:

docker-compose down && docker-compose pull && docker-compose up -d

What it does is: 1) Stops the container, 2) Pull last update (download last update) and 3) Re-run the container back!


If you use firewall (ufw for example) you need to open these ports: TCP: 587 465 25 143 993 110 995 4190 80 443

#howto #selfhost #docker