1. Diagnosis
I set up Exchange fifteen years ago. Now I’m shutting down the last server, and here’s the realization: migrating from Exchange to Linux isn’t some innovation celebration. It’s a cold calculation. Licenses keep getting more expensive, security updates are tied tighter and tighter to Microsoft’s cloud, and finding an admin willing to dig into EDB databases and AD schemas for Exchange gets harder every year.
A familiar picture: 300 to 700 mailboxes per organization, management doesn’t want 365 because of subscription costs and local data storage requirements, and Exchange is held together by duct tape and you alone. If this sounds like your infrastructure, this article covers the whole process.
What you’ll end up with:
- a working mail stack: Postfix + Dovecot + Rspamd, with spam filtering and DKIM out of the box
- SOGo as your Exchange groupware replacement: calendars, contacts, ActiveSync-like sync with Outlook and phones, no plugins needed
- Nextcloud for files and collaborative document work
- mailboxes migrated via imapsync, tested backups, and monitoring
Plan for 4 to 6 months total for 500 mailboxes, and that’s not just server setup, it’s pushing back against users who “have always lived in Outlook like this.” From experience: 70% of the time goes into people, not configs.
2. Reasons
Seven reasons companies are leaving Exchange right now, and why each one wears out your patience faster than your budget.
| Reason | Why it hurts |
|---|---|
| Rising CAL and Windows Server license costs | Gets more expensive every year, while alternatives are free |
| Security updates increasingly require a cloud tie-in | On-premises Exchange turns into a second-class product |
| In-country data storage requirements | 365 isn’t legally an option for some organizations |
| The one person who understood Exchange left the company | The infrastructure becomes a black box |
| Certificate issues and compatibility with modern clients | Old EWS doesn’t play well with newer Outlook and mobile versions |
| Sanctions risk and unpredictable licensing | Management wants data under their own control |
| Growing requirements for EDB database backups | Restoration becomes slow and fragile |
No single reason here is critical on its own. All seven together are reason enough to sit down and run the numbers.
3. The recipe
Architecture
The logic is simple. Mail goes through Postfix, where Rspamd filters spam and signs DKIM. Dovecot stores messages in Maildir. SOGo gives users a single interface: webmail, calendar, contacts, and most importantly, sync with Outlook and phones over open protocols, no connectors to install on each machine. Nextcloud lives separately and handles files.
System requirements
| Component | Version at time of writing |
|---|---|
| OS | Debian 13 (Trixie) |
| Postfix | 3.10.x |
| Dovecot | 2.4.x |
| Rspamd | 4.1.x |
| SOGo | 5.12.10 |
| Nextcloud | 34.0.4 |
| PostgreSQL | 16 or newer |
| Redis | 7.4.x (for Rspamd) |
These are the current versions at the time of writing. Check for newer releases before installing, this stack moves fast, especially Rspamd after its jump to the 4.x branch.
Port table
| Port | Protocol | Purpose | Exposed externally? |
|---|---|---|---|
| 25 | SMTP | Receiving mail from external servers | Yes |
| 587 | SMTP Submission | Authenticated client sending | Yes |
| 993 | IMAPS | Mailbox access over TLS | Yes |
| 443 | HTTPS | SOGo and Nextcloud web interface | Yes |
| 5432 | PostgreSQL | SOGo database | No, localhost or VPN only |
| 6379 | Redis | Rspamd cache and statistics | No |
| 11334 | HTTP | Rspamd web interface | No, SSH tunnel or VPN only |
Preparation
Dependencies: PostgreSQL for SOGo, Redis for Rspamd, a working domain with access to DNS records, and a Let’s Encrypt or corporate TLS certificate. Check your environment before starting.
apt update && apt install -y postfix dovecot-core dovecot-imapd \
rspamd redis-server postgresql sogo sogo-activesync
Result: packages installed, services not yet linked together. Next, we configure each one in sequence.
Step 1. Postfix as transport
Edit main.cf so Postfix accepts mail for your domain and hands it to Rspamd for inspection.
myhostname = mail.example.ru
mydomain = example.ru
myorigin = $mydomain
inet_interfaces = all
mydestination = localhost
virtual_mailbox_domains = example.ru
smtpd_milters = inet:127.0.0.1:11332
non_smtpd_milters = $smtpd_milters
milter_default_action = accept
Result: Postfix listens on ports 25 and 587, every message passes through the Rspamd milter on port 11332.
Step 2. Rspamd: spam filtering and DKIM
rspamadm dkim_keygen -s mail -d example.ru -k /etc/rspamd/dkim/example.ru.key
This command generates a private key and prints the public part, which you need to add as a DNS TXT record for the selector. Skip this step and mail still gets delivered, just unsigned, and some mail systems will start treating it with suspicion.
mail._domainkey.example.ru TXT "v=DKIM1; k=rsa; p=<public key>"
Step 3. Dovecot: storage
mail_location = maildir:/var/mail/vhosts/%d/%n
passdb {
driver = ldap
args = /etc/dovecot/dovecot-ldap.conf.ext
}
If you already have Active Directory (and most people migrating off Exchange do), hook up authentication through LDAP bind. Users won’t need new passwords, which cuts down resistance to the migration far more than any amount of persuading.
Step 4. SOGo: webmail, calendars, contacts
SOGoMailDomain = example.ru
SOGoIMAPServer = imap://127.0.0.1:143/?TLS
SOGoSMTPServer = smtp://127.0.0.1:587
OCSFolderInfoURL = "postgresql://sogo:pass@127.0.0.1:5432/sogo/sogo_folder_info"
Next, enable the ActiveSync module, it’s what gives you native sync with Outlook, iOS, and Android without plugins. Users log in with the same credentials as their mail, and calendars and contacts show up right away.
Step 5. Nextcloud: files
sudo -u www-data php occ maintenance:install \
--database "pgsql" --database-name nextcloud \
--admin-user admin --admin-pass "change_me"
Result: a working instance on port 443. Hook up the LDAP backend to the same Active Directory account, users don’t need two separate passwords.
Step 6. Migrating mailboxes from Exchange
The most nerve-wracking stage. Use imapsync, it can pull mail from Exchange over IMAP and drop it into Dovecot while preserving dates and folders.
imapsync --host1 exchange.example.ru --user1 ivanov --password1 'old_password' \
--host2 mail.example.ru --user2 ivanov --password2 'new_password' \
--ssl1 --ssl2 --automap
Run the script in a loop over all mailboxes, logging each user to a separate file. Do your first pass in a test window without deleting source mail, in case something goes wrong.
4. Verification
systemctl status postfix dovecot rspamd sogo
tail -f /var/log/mail.log
rspamc stat
Separately, check delivery using external SPF/DKIM/DMARC checking services: send a test message and check the Authentication-Results headers on the receiving end. If all three come back pass, delivery won’t fall into spam at major providers.
5. Complications
| Problem | Cause | Fix |
|---|---|---|
| Mail lands in spam at Gmail and Mail.ru | Incomplete SPF, DKIM, DMARC setup | Check all three records with an external checker, fix the DMARC policy |
| imapsync fails on large mailboxes | Default timeout too short | Add a flag to increase the timeout |
| SOGo doesn’t show the calendar after migration | OCSFolderInfo index wasn’t rebuilt | Rebuild indexes with sogo-tool |
| Outlook won’t connect via ActiveSync | sogo-activesync module not installed or not enabled | Install the package, restart sogod |
imapsync --timeout1 600 --timeout2 600 --host1 exchange.example.ru ...
sogo-tool update-autoreply -a -F
sogo-tool reindex sogo
6. Alternatives
| Option | When to pick it instead |
|---|---|
| Mailcow | Small infrastructure, up to 100-150 mailboxes, where deployment speed matters more than flexibility under load |
| Zimbra Open Source | If you want a single unified panel instead of separate components, with a smaller active community |
| Kopano | If MAPI compatibility with Outlook specifically is critical |
| Hybrid with Microsoft 365 | Some mailboxes stay in the cloud, some move on-premises, a compromise for the transition period |
Mailcow is easier to get started with, but at 500 mailboxes with real uptime requirements, we dropped it after a month of testing, too much magic happening under the hood where you need transparency instead. A component-by-component stack gives you control over every link in the chain.
7. Maintenance
- Monitor Postfix queues and Rspamd statistics through a Zabbix agent
- Back up Maildir and PostgreSQL databases separately
- Set up systemd timers for regular mail queue checks on boot
- Regularly check your IP’s reputation against blacklist aggregators
- Run a test restore from backup at least once a quarter
- Rotate DKIM keys once a year
- Monitor TLS certificate expiration automatically, not by hand
Backups
| What to back up | How often | Where to store it | How to restore |
|---|---|---|---|
| Maildir for all mailboxes | Daily | Separate server plus offsite | rsync back plus restart Dovecot |
| SOGo’s PostgreSQL database | Daily | Separate server | pg_restore from dump |
| Postfix/Dovecot/Rspamd configuration | On every change | Git repository | git checkout plus restart services |
| Nextcloud data | Daily | Separate server plus offsite | occ files:scan after restoring files |
Security
- UFW, explicitly allowing only the ports needed from the table above
- fail2ban on SSH and on SMTP auth brute-force attempts
- A separate system user for PostgreSQL with no shell access
- SSH key-only, no password login
- Restrict access to the Rspamd web interface by IP or through VPN
Updates
Before updating any component, take a disk snapshot or a database dump. Update one service at a time, not all at once. After updating Rspamd, verify DKIM signing still works correctly, major version jumps (like the move from 3.x to 4.x) can change parts of the config format. Roll back from a snapshot or from the config repo in git.
8. FAQ
Why doesn’t mail work after setup?
Nine times out of ten it’s an incomplete SPF, DKIM, DMARC chain, or a bad milter connection between Rspamd and Postfix. Check the Authentication-Results headers on a test message and the mail.log, it’ll show you exactly where the message dropped.
How do I verify the migration from Exchange went correctly?
Compare the message and folder counts between the old and new mailbox using imapsync with the dry-run flag before the real transfer. After migrating, ask a few users to check their recent mail and calendar on their phones, that’s faster than cross-checking logs by hand.
What do I do if Outlook doesn’t see the new calendar?
Check that the sogo-activesync module is installed and the service has been restarted. Then recreate the Outlook profile from scratch, don’t try to patch the old profile that still has the Exchange connection in it.
How is SOGo different from Mailcow?
SOGo is just the groupware layer on top of your own Postfix and Dovecot, you fully control the configuration. Mailcow is a ready-made Docker stack doing the same job, but with less flexibility for nonstandard requirements, like a custom backup scheme or integration with an existing Active Directory.
9. Outlook (the forecast, not the app)
After the migration, mail behaves more predictably than Exchange did in its final years. Less magic, more readable logs: when something breaks, you know where to look instead of guessing between the EDB database and the transport service.
The new stack will break too. It’ll have its own gotchas, its own late-night troubleshooting sessions. But now they’re your gotchas, not someone else’s. If something in this guide didn’t work for you, drop a comment and we’ll sort it out.
Оставайтесь на связи
Рецепты от IT-боли. Без воды, без рекламы, без маркетинговой шелухи.
Подписаться на IT-Аптеку →



