Why Self-Host Your Email in 2026
Microsoft's price increase for Microsoft 365, effective 1 July 2026 — up to 43% depending on the plan was a wake-up call for many SMBs. An organization with 20 employees that was paying around €200 per month now faces bills of €285 or more, with no meaningful improvement in features. And that is not all: Microsoft has officially scheduled the shutdown of Exchange Web Services (EWS) for October 2026, making obsolete dozens of third-party integrations that relied on this protocol — shared calendars, CRM tools, in-house automations.
Faced with these pricing and technical disruptions, self-hosted email has returned to the conversation with renewed legitimacy. Hosting your own mail server on a VPS offers three major advantages: full cost control (a properly sized VPS costs between €15 and €40 per month for 20 to 50 mailboxes), data sovereignty (your messages do not transit through a hyperscaler's datacenters), and simplified GDPR compliance (you know exactly where your data is stored and processed). For law firms, healthcare professionals, communication agencies, or any organization handling sensitive data, this last argument is often decisive.
What Mailcow Includes Out of the Box
- Postfix + Dovecot: the time-tested open-source email duo — Postfix handles inbound and outbound SMTP, while Dovecot provides IMAP and POP3 access for your email clients.
- Rspamd: a next-generation spam filtering engine far more accurate than SpamAssassin, with adaptive Bayes, greylisting, DKIM, ARC, and multi-factor scoring built in.
- SOGo: a full-featured webmail client (calendars, contacts, tasks) with ActiveSync synchronization for Outlook and mobile devices, without needing Exchange.
- Web administration panel: a modern control panel to manage domains, mailboxes, aliases, quotas, mailing lists, and DKIM policies in just a few clicks.
- WebAuthn / two-factor authentication: enhanced security via hardware keys (YubiKey, etc.) or TOTP to protect admin access and individual user mailboxes.
- Full REST API: all administration actions are exposed through a documented API, enabling automation of mailbox and alias creation, or integration into your own customer portal.
- Built-in IMAP synchronization: the interface's sync jobs (*E-Mail Configuration → Synchronizations*) copy messages from a source server (Gmail, Microsoft 365, cPanel…) into a Mailcow mailbox, without any command-line work.
- Automatic TLS certificates: Let's Encrypt is natively integrated — Mailcow renews certificates for your mail domain with no manual intervention required.
Technical and Network Prerequisites
Before starting the installation, make sure your VPS and network configuration meet the following requirements. On the hardware side, the Mailcow documentation asks for at least 6 GB of RAM plus 1 GB of swap in the default configuration: below that, you are outside what the authors plan for, and the hungriest containers (antispam, antivirus, database) are the first to suffer. Plan for 2 vCPUs and at least 20 GB of disk space for containers and emails — 50 GB is a reasonable starting point for a small organization.
On the network side, several points are non-negotiable. First, port 25 must be open outbound: many hosting providers block it by default to fight spam. Check with your provider before ordering. Second, you must be able to configure the rDNS (reverse DNS / PTR record) for your IP: this allows recipient servers to verify that your IP actually maps to your mail hostname — without a correct PTR, your emails will land in spam at most major providers. Third, you need a domain name whose DNS you control (adding MX, A, and TXT records). Finally, a VPS running Debian 12 or Ubuntu 24.04 LTS is recommended for optimal compatibility with Docker and the Mailcow scripts. The Mailcow documentation lists Debian 11 to 13, Ubuntu 22.04 and newer, AlmaLinux 8 and 9, and Rocky Linux 9 as compatible; Alpine appears only with limited compatibility and manual adjustments.
Installing Mailcow in 8 Steps
Provision the VPS and Configure rDNS
Order a VPS with at least 6 GB of RAM, 2 vCPUs, and a dedicated IP. In your hosting control panel, navigate to the rDNS (or PTR record) section and map your IP to
mail.yourdomain.com. This setting is critical: without a correct PTR, your emails will be systematically rejected or marked as spam by Gmail, Outlook, and other major providers. Allow up to 24 hours for PTR propagation.Point Your Domain's DNS Records
In your DNS manager, create an A (or AAAA) record for
mail.yourdomain.compointing to your VPS IP. Add an MX record onyourdomain.comwith the valuemail.yourdomain.comand a priority of 10. Also prepare your SPF record (v=spf1 mx ~all) and DMARC record (_dmarc.yourdomain.com) — DKIM will be automatically generated by Mailcow when it first starts.Install Docker and Docker Compose
Connect to your VPS via SSH as root. Install Docker using the official script:
curl -fsSL https://get.docker.com | sh. Docker Compose v2 is included in modern Docker packages. Verify the versions withdocker --versionanddocker compose version. Ensure the Docker service starts automatically withsystemctl enable --now docker.Clone the Repository and Generate the Configuration
Clone the official repository into
/opt/mailcow-dockerized:git clone https://github.com/mailcow/mailcow-dockerized /opt/mailcow-dockerized. Enter the directory and run./generate_config.sh. The script asks for your mail hostname (e.g.,mail.yourdomain.com) and timezone. It generates themailcow.conffile with secure random keys — do not manually modify these keys afterward.Start Mailcow
From
/opt/mailcow-dockerized, rundocker compose pullto download all images (approximately 2 to 4 GB depending on your connection), thendocker compose up -dto start all containers in the background. The first startup takes 2 to 5 minutes. Check container status withdocker compose ps— all should show a status ofUporUp (healthy).Access the UI and Create Your First Domain
Open your browser at
https://mail.yourdomain.com. Default credentials areadmin/moohoo. Change the admin password immediately. Under *Configuration → Domains*, add your domain. Then create your first mailbox under *Configuration → Mailboxes*. The interface is intuitive and well-organized.Enable DKIM and Test Deliverability
Under *Configuration → ARC/DKIM Keys*, select your domain and click *Generate new key* (2048-bit length recommended). Copy the generated TXT record and add it to your DNS manager (TXT record on
dkim._domainkey.yourdomain.com). Then send a test email from your new mailbox to[email protected], or usemail-tester.comfor a comprehensive deliverability score.Set Up Automated Backups
Mailcow provides an official backup script at
helper-scripts/backup_and_restore.sh. Schedule a daily run via cron:0 3 * * * MAILCOW_BACKUP_LOCATION=/opt/backup /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup all. TheMAILCOW_BACKUP_LOCATIONvariable skips the interactive question about the location; then ship that folder to external storage (NFS mount, S3 bucket, remote directory via rsync). Add--delete-days 3to the command to purge older copies. The documentation asks you not to copy the script elsewhere: call it from its original folder. Test a restore before considering the backup operational.
Deliverability: Pitfalls to Avoid
A technically correct mail server can still have its messages land in spam if certain network and DNS conditions are not met. Here are the most common mistakes.
Blacklists: your VPS IP may be listed in blacklists like Spamhaus or Barracuda if it was previously used for spam — this is common with datacenter IP ranges. Check your IP on mxtoolbox.com/blacklists.aspx before installation. If listed, request delisting from each organization or switch IPs.
Port 25 blocked: some hosting providers block outbound port 25 on all new IPs for a probation period. Contact support before starting.
Missing or misconfigured DKIM: an email without a valid DKIM signature is treated with suspicion by Gmail and Outlook. Verify with dig TXT dkim._domainkey.yourdomain.com that the record is visible.
DMARC too permissive: start with p=none to collect reports without blocking, then move to p=quarantine and p=reject once you are confident all legitimate mail streams are covered by SPF and DKIM.
Missing PTR record: the most commonly forgotten point. Without a PTR record resolving to your mail hostname, Microsoft Outlook will reject your emails with a 550 error.
Mailcow behind Nginx Proxy Manager: if your VPS hosts other web services (website, application), you can run Mailcow alongside Nginx Proxy Manager on the same IP. The key is to let NPM handle only ports 80 and 443 (HTTP/HTTPS) as a reverse proxy, while Mailcow retains direct control over SMTP ports (25, 465, 587) and IMAP/POP3 (993, 995). In mailcow.conf, set HTTP_PORT=8080, HTTP_BIND=127.0.0.1, HTTPS_PORT=8443 and HTTPS_BIND=127.0.0.1, apply with docker compose up -d, then configure NPM to proxy to http://127.0.0.1:8080 (the port used by the Nginx example in the Mailcow documentation). Never try to route port 25 through an HTTP reverse proxy — SMTP does not work that way.
Migrating from Google Workspace or Microsoft 365 to Mailcow
Migrating existing emails is often what holds organizations back. Mailcow includes IMAP sync jobs directly in its interface, making the operation accessible without advanced technical skills.
The basic procedure is: (1) create the destination mailboxes in Mailcow, (2) open *E-Mail Configuration → Synchronizations* in the admin interface (a user has the same tool under *User Settings → Sync Jobs*), (3) enter the host, port, username and password of the source server, choose the encryption — TLS usually fits port 143, SSL fits port 993, and PLAIN is strongly discouraged — then enable the job. It copies messages folder by folder. The migration can be run multiple times before the final cutover (already-copied messages are skipped on subsequent passes), enabling a progressive cutover: you continue receiving mail on the old address while data migrates, then switch the MX record in a single operation. Once the switch is done, disable or delete the job.
For Microsoft 365, do not count on EWS: according to Microsoft's documentation, it starts being disabled for all organizations in October 2026 and will be fully disabled in April 2027, so migration tools that rely on it will stop working. Mailcow uses IMAP, not EWS, and is therefore unaffected — yet another reason to migrate early.
For email clients, Thunderbird, Apple Mail, and mobile apps (iOS, Android) configure via standard IMAP/SMTP. Outlook works too, including calendar and contact sync via ActiveSync (SOGo).
Comparison: Mailcow vs Mailu vs iRedMail
Mailcow is not the only Docker-based self-hosted email solution. Here is how it compares to its main competitors.
Mailu is lighter on resources (runs with 1 GB of RAM) and suits personal use or very small organizations. Its interface is minimalist, it does not include SOGo, and its feature set is more limited. It is an excellent choice when simplicity is a priority over features.
iRedMail is a mature solution, available as a community edition and a paid Pro version (iRedMail Pro). It supports multiple database backends (MySQL, PostgreSQL, LDAP) and installs directly on the host system without Docker. Its configuration curve is steeper, and updates are less automated. iRedMail Pro adds a paid web management interface that competes with Mailcow's.
Mailcow stands out for the richness of its features, the quality of its administration interface, the frequency of its updates (monthly releases), and the size of its community (13,200+ GitHub stars). It is the natural choice for an SMB that wants a complete, actively maintained solution with abundant documentation. The trade-off is RAM consumption: 6 GB minimum is a non-negotiable requirement.
Maintenance and Version Upgrades
Mailcow publishes one release per month, and its tag carries the year and month (2026-10 for the one of 21 October 2026). They fix security vulnerabilities, improve spam filters, and add features. The official update procedure is straightforward and well-documented.
From the /opt/mailcow-dockerized directory, run ./update.sh. This script pulls the new Docker images, applies any configuration migrations, and restarts the affected containers. The operation typically takes 5 to 15 minutes with less than a minute of service interruption for the restarted containers.
Do not use Watchtower (the automatic Docker container update tool) with Mailcow. Mailcow updates sometimes involve database migrations or configuration changes that require running update.sh in the correct order. Letting Watchtower pull and restart images autonomously can corrupt your installation.
Schedule updates during a known maintenance window (for example, Saturday morning), read the release note on the Mailcow repository's releases page before applying, and verify container status with docker compose ps after each update. Take a full backup right before each update. When the release is a security update, do not wait: the next section gives the emergency procedure.
Updating Mailcow urgently: the PHP2Shell case
Mailcow is not a PHP application sitting on your system: it ships its own PHP, in the php-fpm-mailcow container, which runs the admin interface. Your distribution's PHP fixes therefore do not apply to it, and an apt upgrade on the VPS changes nothing about the version Mailcow uses. That is what prompted thread #7533, opened on 21 October 2026 under the title "[UPDATE NOW] PHP RCE Vulnerability (CVE-2026-109618 and CVE-2026-109619)": administrators there ask for a fix from Mailcow itself. Both flaws are detailed in our article [PHP2Shell: fixing CVE-2026-109618 and CVE-2026-109619](/blog/php2shell-cve-2026-109618-109619-mettre-a-jour-php).
On the version side, the 2026-10 release, published on 21 October 2026, is titled "Security Update: PHP 8.2.35, SOGo 5.12.12 & Nginx 1.30.6". Its notes do not cite the CVE numbers: the link is made through the PHP version, since 8.2.35 is the fixed version of the 8.2 branch in the PHP project's advisory of the same day, and the phpfpm image shipped with that release carries the tag 8.2.35-1. Read the release note before acting: a newer release may have changed these numbers.
Emergency procedure in six steps
Back up first
From any folder, run
MAILCOW_BACKUP_LOCATION=/opt/backup /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup all. Check that the destination folder actually contains files before moving on, and keep a copy off the VPS: a backup that lives on the machine you are about to modify protects nothing.Read what the update will change
In
/opt/mailcow-dockerized,./update.sh --checktells you whether an update exists and what it changes, without applying anything. Open the release note alongside and look for the "Security" heading: if the PHP numbers are not there, it is up to you to compare them with the PHP project's advisory.Apply the update
Run
./update.shfrom the same folder and answer the script's questions. Do not use--force, which the documentation presents as unsupported, nor--ours, which it advises against: they hide file conflicts instead of resolving them. Plan for a few minutes of downtime.Check the PHP version inside the container
docker compose exec php-fpm-mailcow php -vmust show 8.2.35 or a newer version.docker compose images php-fpm-mailcowgives the image and its tag (8.2.35-1for the2026-10release). Thephp -vof the VPS itself, outside the container, says nothing about Mailcow: the check inside the container is the one that counts.Check the state of the stack
docker compose psmust show every container asUporUp (healthy). Then rundocker compose logs --tail 50 php-fpm-mailcowto look for a startup error, open the web interface and send a test message from a mailbox to an external address.If you cannot update right away
Thread #7533 listed stopgaps before the fix was released:
session.upload_progress.enabled = Off(judged incomplete by the thread's author), an nginx rule that answers 403 tomultipartrequests, and binding the interface to 127.0.0.1 viaHTTP_BINDandHTTPS_BIND, which also cuts direct access to the interface and, one participant points out, makes MTA-STS unavailable. These buy a few hours, they are not a fix. If the update goes wrong, the documentation provides a rollback:docker compose down,git checkout <commit ID>,docker compose pull, thendocker compose up -d;update.shleaves a "Before update" commit to find withgit log.
Troubleshooting: the netfilter container restarts in a loop
On a VPS that already hosts other Docker containers, the netfilter-mailcow container can start restarting endlessly. The visible symptom: the web interface stops responding, the unbound-mailcow container is reported as failing ("dependency failed to start: container mailcowdockerized-unbound-mailcow-1 is unhealthy") and netfilter's logs repeat "MAILCOW target not found in ip forward table, restarting container to fix it...".
Reported cause: at startup, netfilter checks the position of its MAILCOW chain in the FORWARD table. According to thread #6795, the code in NFTables.py assumes the chain sits in first position and places it by insertion; if Docker has already created its own chains, the check fails, the container exits, and each restart adds one more MAILCOW jump. In #6995, one participant counts more than 9,000 of them, and another describes the same effect after a Docker update on Debian.
What we know about the status: #6795, opened in September 2025, was closed as "not planned" in December 2025 after a "stale" label; #6995, opened in January 2026, was closed the same way in October 2026. No maintainer confirms a fix there, and one participant believes the root cause needs a refactor. What follows is therefore a community workaround, not an official procedure.
Diagnose, then work around, from safest to riskiest
Confirm the duplicates
docker compose logs --tail 50 netfilter-mailcowshows the messages above.iptables -S FORWARD | grep -c MAILCOWcounts the jumps to theMAILCOWchain: one is expected, and more confirms the diagnosis. Repeat the test withip6tablesif IPv6 is enabled.Avoid the problem at the source
The safest course is not to run Mailcow alongside other Docker stacks: a machine dedicated to mail avoids the collision. The Mailcow documentation also flags several problems with an active ufw or firewalld, recommends disabling it and moving your rules to the
DOCKER-USERchain, and states thatINPUTrules have no effect on Mailcow.Clean up the duplicates
List the rules with
iptables -t filter -L FORWARD --line-numbers, spot the lines that jump toMAILCOW, then delete them withiptables -t filter -D FORWARD <number>, recounting after each deletion because the numbers shift. Restart the stack withdocker compose down && docker compose up -d. Do not touch theDOCKERrules of your other containers. This cleanup is temporary: if the cause is still there, the duplicates come back.Hook MAILCOW into DOCKER-USER
In #6995, one participant reports keeping bans working by hooking the chain into
DOCKER-USER:iptables -I DOCKER-USER 1 -m comment --comment "mailcow bans" -j MAILCOW, with the equivalentip6tablescommand. Another proposes an unofficial netfilter test image, proven only with nftables. Upstream validates neither route: try them on a disposable VM, with a snapshot, before touching production.Remove the isolation rule, as a last resort
If netfilter's isolation rule blocks legitimate flows,
DISABLE_NETFILTER_ISOLATION_RULE=yinmailcow.conf, followed bydocker compose up -d, removes it. The file's comment warns that this may expose container ports to neighbors on the same subnet, even when they are bound to localhost: keep this setting for a machine dedicated to Mailcow.
A VPS for mail alone: the sizing of 6 GB plus swap assumes Mailcow has the machine to itself. On a VPS shared with a showcase site or a containerized application, you add up the RAM of each stack, the firewall-rule collision risk described above, and one more PHP to watch at every security advisory. Separating mail from the rest costs one more VPS, but removes all three issues at once.
When Self-Hosting Email Is Not Advisable
Self-hosting email is not a universal answer. There are situations where Google Workspace or Microsoft 365 remains the more sensible choice, even after the price increase.
If you lack in-house technical expertise: managing a mail server requires regular security monitoring, proactive updates, and the ability to diagnose deliverability issues. Without these skills, you risk being left without email during a critical incident.
If deliverability is absolutely critical: large-scale transactional email operations (banks, insurers, high-volume e-commerce platforms) benefit from the reputation of shared IPs professionally managed by ESPs (Mailgun, Postmark, Brevo). A solo VPS starts with a fresh IP whose reputation must be built from scratch.
If your hosting provider blocks port 25: some cloud operators (particularly on entry-level plans) permanently block this port to combat spam. Verify before committing.
If you operate in a strictly regulated industry: some sectors (finance, healthcare) impose certification or audit requirements for email infrastructure that can be difficult to satisfy with self-hosting without specialized support.
Conclusion: An Email Server That Belongs to You
Self-hosting your email server with Mailcow on a VPS is now within reach of any organization with a minimum of technical maturity. The solution is mature, actively maintained, and delivers a functional level that honestly competes with commercial offerings — at a fraction of the cost.
Microsoft 365's price increase and the end of EWS in 2026 have transformed self-hosting from a fringe option into a strategic alternative for thousands of SMBs and agencies. With a properly sized VPS (6 GB RAM, dedicated IP with configured rDNS), a Mailcow installation takes less than an hour, and the migration of existing mailboxes can be done progressively without service interruption.
Three things to remember before getting started: verify that port 25 is open with your hosting provider, configure the PTR record for your IP before sending your very first email, and set up external backups from day one. With these foundations in place, your email server will be just as reliable — and far more cost-effective — than a cloud subscription.
Add a fourth reflex to these three: Mailcow carries its own PHP, so every PHP security advisory is handled with ./update.sh followed by a check inside the container, as with PHP2Shell in October 2026.