8.2.26 Installation and Configuration of Mailcow on the Server

The Host Banner

Mailcow is a self-hosted mail server suite built around Docker containers. It is for those who are tired of trusting their correspondence to “big friendly corporations” and want to keep emails under their own control.

Key features:

  • Containerization: everything runs in Docker, each service is isolated (Postfix, Dovecot, Rspamd, ClamAV, MariaDB, Redis, etc.).
  • Web interface: a convenient admin panel on PHP/NGINX for managing domains, mailboxes, aliases, and antispam.
  • Security: built-in DKIM, SPF, DMARC, TLS support, automatic certificate renewal via ACME/Let’s Encrypt.
  • Antispam/antivirus: email filtering through Rspamd and ClamAV.
  • Flexibility: support for SOGo (web calendar, contacts, webmail), API for integration, extensibility by configuring services.

Server preparation

Requirements

  • A virtual or dedicated server.
  • Open incoming TCP ports: 25, 80, 443, 110, 143, 465, 587, 993, 995, 4190.
  • An active domain name for MAILCOW_HOSTNAME, and a correct reverse zone PTR pointing to this domain.
  • root access or a user with sudo privileges.

Environment variables

  • SERVER_IP — the external IP address of the server on which Mailcow runs.
  • MAILCOW_HOSTNAME — the fully qualified domain name that will be used as the main host of the mail service.
  • MAILCOW_TIMEZONE — the server time zone (for example, Europe/Kyiv), required for correct logging and scheduling.
  • ADMIN_EMAIL — the administrator’s email where notifications and reports will be sent.
  • BACKUP_DIR — the directory for storing backups (a separate disk or network storage is recommended).

Installing dependencies

Install the recommended dependencies:

sudo apt update && sudo apt install -y git openssl curl gawk coreutils grep jq

sudo_apt

Installing Docker and Compose

See the full guide in our article: Docker and Docker Compose

Minimum required versions: Docker >= 24.0.0, Docker Compose >= 2.0. Check:

docker --version
docker compose version

docker_version

If the versions are lower, update Docker/Compose according to the guide above before proceeding.

Cloning the Mailcow repository

Fetch the current Mailcow code from the official GitHub repository to deploy the mail stack in the latest stable version.

sudo mkdir -p /opt && cd /opt
sudo git clone https://github.com/mailcow/mailcow-dockerized
cd /opt/mailcow-dockerized

mkdir

Generating the configuration

Run the script to generate the initial configuration:

./generate_config.sh

Specify:

  • Mail server hostname (FQDN): enter MAILCOW_HOSTNAME (for example, mail.example.com).
  • Timezone: MAILCOW_TIMEZONE (for example, Europe/Kyiv).

The script will create mailcow.conf, where you can, if necessary, configure ports and additional SAN/server names.

Tip: if you need to make manual edits to the MailCow configuration file, run the command: nano /opt/mailcow-dockerized/mailcow.conf

Opening ports in the firewall

Allow the mail and web ports.

UFW -- allow required ports
sudo ufw allow 25,80,110,143,443,465,587,993,995,4190/tcp
sudo ufw reload
sudo ufw status numbered

UFW

iptables -- allow required ports
sudo iptables -A INPUT -p tcp -m multiport --dports 25,80,110,143,443,465,587,993,995,4190 -j ACCEPT
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

Configuring DNS records

For correct mail operation you will need to configure DNS records for your MAILCOW_HOSTNAME domain. You can find more details in our guide.

Starting the containers

Start the containers and pull the required images:

docker compose pull
docker compose up -d

After startup, open https://MAILCOW_HOSTNAME/admin and log in with username admin, password moohoo. Change the administrator password immediately.

web ui

After completing the steps above, you will land in the administrative panel:

admin panel welcome

Checking ports/availability

Make sure the required ports are listening and reachable from the outside (especially 25, 80, 443, 587, 993, 995, 110, 143, 4190):

ss -tlpn | grep -E -w '25|80|110|143|443|465|587|993|995|4190'

Checking listening ports

Quick breakdown of ss -lntp columns

  • LISTEN — the socket is waiting for incoming connections
  • Local Address:Port — where exactly the port is “bound”
  • 0.0.0.0:PORT — listening on all IPv4 interfaces
  • [::]:PORT or *:PORT — listening on all IPv6
  • 127.0.0.1:PORT — available locally only
  • users:(… pid=…) — which process holds the port (name and PID).

Service configuration

Adding a domain name

Create a domain and set it as the primary one for receiving and sending mail. After adding, the system will automatically offer to generate a DKIM key and will suggest the necessary DNS records for correct operation (MX, SPF, DKIM, DMARC).

To do this, go to E-mail → Configuration → Add domain

adding domain

Configuring additional features

Reverse proxy (Nginx/Traefik)

  • Bind the Mailcow web interface to 127.0.0.1 via HTTP_BIND/HTTPS_BIND, and publish it externally through the proxy.
  • If you use an interface domain different from MAILCOW_HOSTNAME, fill in ADDITIONAL_SERVER_NAMES (comma-separated, no spaces) and only then restart the stack.
docker compose down && docker compose up -d

SSL and Let’s Encrypt

By default the built-in ACME container will issue a certificate for MAILCOW_HOSTNAME and names from ADDITIONAL_SAN. To force certificate renewal, restart the ACME container:

docker compose restart acme-mailcow

Tip: to issue the certificate correctly, make sure port 80/TCP is open.

Backups and restore

Mailcow provides the helper-scripts/backup_and_restore.sh script for backup/restore. Run the specified command from the root of the MailCow project(/opt/mailcow-dockerized).

# Backup of all components with deletion of backups older than 14 days
sudo ./helper-scripts/backup_and_restore.sh backup all --delete-days 14 --target BACKUP_DIR

# Restore from the directory with backups
sudo ./helper-scripts/backup_and_restore.sh restore --target BACKUP_DIR

Schedule cron jobs and check the backup execution logs.

Security

Manage ban policies in Fail2Ban by going to System and selecting Configuration.

failtoBan

In the window that opens, select Options and open the Fail2Ban parameters section.

failtoBan

Updates

To perform updates in the project directory, run:

./update.sh

Common errors

Error / Symptom Cause Solution Diagnostics
ACME does not issue/renew the certificate Port 80 is closed or incorrect ADDITIONAL_SAN/proxy settings Open 80/TCP, adjust SAN/proxy and restart acme-mailcow docker compose logs -f acme-mailcow
No incoming mail 25/TCP is closed from outside or filtered by the provider Open 25/TCP; if blocked by the provider, use a suitable VPS telnet <SERVER_IP> 25 from an external network
Conflict 80/443 A web server is already listening on the host Use a reverse proxy, bind Mailcow to 127.0.0.1 ss -tlpn grep -E '80 / 443'
Incorrect HTTP(S) bindings The IP:PORT format was used for HTTP/HTTPS Use HTTP_PORT/HTTPS_PORT and HTTP_BIND/HTTPS_BIND separately Check mailcow.conf
Port error in Compose (:443:443) Incorrect port publishing syntax Specify the correct port value without an empty : prefix Validate with docker compose config
Cannot log in to the panel after update Login routes changed in 2025-03 Use https://<HOST>/admin for the administrator Check the release notes