A mail server is harder to move than a website: mail arrives around the clock, other servers cache your DNS records, and a change of IP address affects deliverability. The good news is that SMTP was built for this. A sending server that cannot reach you keeps the message queued and retries for days, so a well planned migration loses nothing. In this tutorial you will move an existing Postfix and Dovecot server with Maildir storage to a new Ubuntu 24.04 server, copy its configuration, mailboxes and DKIM keys, and switch DNS with a short, controlled cutover.
Prerequisites
To follow this tutorial you need:
- The old server, running Postfix and Dovecot with mail stored in Maildir format (for example under
/var/vmailor in each user's~/Maildir). - A new server running Ubuntu 24.04 LTS, for example a CubePath VPS, with a non-root user with
sudoprivileges and SSH access from the new server to the old one as root or asudouser. - Access to the DNS zone of every mail domain, and to the reverse DNS (PTR) setting of the new server's IP address.
- Outbound TCP port 25 allowed on the new server. Many providers block it on new servers by default, so check before the cutover.
In the commands, replace example.com, mail.example.com, old_server_ip and new_server_ip with your own values.
Step 1 - Lowering DNS TTLs in advance
At least 24 to 48 hours before the migration, lower the TTL of the records that will change, so that other servers pick up the new values quickly on cutover day. These are usually the A (and AAAA) record of mail.example.com and the SPF TXT record if it contains the old IP.
Check the current values:
dig +noall +answer example.com MX
dig +noall +answer mail.example.com A
dig +noall +answer example.com TXT
example.com. 3600 IN MX 10 mail.example.com.
mail.example.com. 3600 IN A 203.0.113.10
example.com. 3600 IN TXT "v=spf1 mx ip4:203.0.113.10 -all"
Set the TTL of these records to 300 (five minutes) in your DNS provider. The old TTL (here 3600 seconds) must expire before the change takes effect, which is why you do this in advance.
If the MX record points to a host name such as mail.example.com, you only need to change that host's A record, not the MX record itself.
Step 2 - Installing the same software on the new server
Take note of the versions on the old server:
postconf mail_version
dovecot --version
Install Postfix and Dovecot on the new server. When the Postfix installer asks for the configuration type, choose No configuration; you will copy the old configuration instead:
sudo apt update
sudo apt install postfix dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd
Add the packages your old setup uses, for example postfix-mysql and dovecot-mysql for users stored in MySQL, opendkim for DKIM signing, or dovecot-sieve dovecot-managesieved for filters. You can list them on the old server:
dpkg -l | grep -E 'postfix|dovecot|opendkim|rspamd|spamassassin|clamav' | awk '{print $2}'
ImportantUbuntu 24.04 ships Dovecot 2.3, which reads 2.2 and 2.3 configuration files. Dovecot 2.4 changed the configuration syntax substantially. If the two servers use different major versions, review the upstream upgrade notes before copying
/etc/dovecot.
Step 3 - Copying users, configuration and certificates
If your mailboxes belong to a virtual mail user (commonly vmail), create it on the new server with the same UID and GID as on the old one, so file ownership stays correct. Check them on the old server:
id vmail
uid=5000(vmail) gid=5000(vmail) groups=5000(vmail)
Create the user on the new server with those numbers:
sudo groupadd -g 5000 vmail
sudo useradd -u 5000 -g vmail -d /var/vmail -s /usr/sbin/nologin -M vmail
If your mail users are regular system accounts instead, recreate them with the same names and UIDs, and copy their password hashes from /etc/shadow.
Copy the configuration directories from the old server. Run this on the new server; --rsync-path="sudo rsync" lets rsync read root-owned files on the old server (it needs passwordless sudo there, or log in as root):
sudo rsync -aAX --rsync-path="sudo rsync" your_user@old_server_ip:/etc/postfix/ /etc/postfix.old/
sudo rsync -aAX --rsync-path="sudo rsync" your_user@old_server_ip:/etc/dovecot/ /etc/dovecot.old/
Copying to .old directories lets you compare before replacing. If both servers use similar versions, you can move them into place:
sudo rsync -a /etc/postfix.old/ /etc/postfix/
sudo rsync -a /etc/dovecot.old/ /etc/dovecot/
Also copy anything your configuration refers to:
- TLS certificates, for example
/etc/letsencrypt/(copy the whole directory to keep thelive,archiveandrenewalstructure intact). - DKIM keys and configuration:
/etc/opendkim.confand/etc/opendkim/, or the equivalent for Rspamd. - Lookup tables outside
/etc/postfix, SQL connection files and Sieve scripts. /etc/aliases, then rebuild it withsudo newaliases.
Rebuild the hash maps, because the binary .db files are not portable between versions:
sudo postconf -n | grep -oE 'hash:[^ ,]+' | sort -u
hash:/etc/postfix/virtual
hash:/etc/postfix/sasl_passwd
sudo postmap /etc/postfix/virtual
sudo postmap /etc/postfix/sasl_passwd
Replace any reference to the old IP address with the new one, for example in mynetworks or inet_interfaces:
sudo grep -rn "old_server_ip" /etc/postfix /etc/dovecot
Check both configurations for errors:
sudo postfix check
sudo doveconf -n > /dev/null
Both commands print nothing when the configuration is valid. Start the services:
sudo systemctl restart postfix dovecot
sudo systemctl status postfix dovecot --no-pager
Open the mail ports in the firewall:
sudo ufw allow 25,465,587/tcp
sudo ufw allow 993,995/tcp
Step 4 - Copying the mailboxes
Do a first full copy of the mail storage while the old server is still in production. It moves the bulk of the data; later copies only transfer what changed. Run it on the new server:
sudo rsync -aHAX --numeric-ids --info=progress2 \
--rsync-path="sudo rsync" \
your_user@old_server_ip:/var/vmail/ /var/vmail/
--numeric-ids keeps the numeric owners, which match because you created vmail with the same UID. If mail lives in users' home directories, copy /home/ instead.
Dovecot index and cache files (dovecot.index*) are copied too. They are rebuilt automatically if needed, so this is safe.
Step 5 - Setting reverse DNS and SPF for the new IP
Receiving servers check that the sending IP has a PTR record matching the host name it announces. Set the PTR record of new_server_ip to mail.example.com in your provider's control panel, and make sure Postfix announces the same name:
postconf myhostname
myhostname = mail.example.com
Verify the PTR record:
dig +short -x new_server_ip
mail.example.com.
If your SPF record lists the old IP explicitly, add the new IP now (keep the old one until the migration is finished):
v=spf1 mx ip4:203.0.113.10 ip4:198.51.100.20 -all
DKIM keeps working unchanged as long as you copied the private keys, because the public key in DNS does not depend on the IP.
Step 6 - Cutting over
This step is the only one with a brief interruption. Other servers will queue mail and retry while it happens.
-
On the old server, flush the queue and check that it is empty. Messages still waiting for a remote server would otherwise stay behind:
sudo postqueue -f sudo postqueue -pMail queue is emptyIf some messages cannot be delivered (for example a remote server that keeps rejecting them), note them; they stay in
/var/spool/postfixon the old server. -
Stop Postfix on the old server, then Dovecot. From now on, senders get a connection refused and keep the mail in their own queues, and users can no longer change their mailboxes:
sudo systemctl stop postfix sudo systemctl stop dovecot -
On the new server, run the final mailbox sync.
--deleteremoves messages that users deleted on the old server since the first copy. Do this before changing DNS, so that no mail has been delivered to the new server yet, otherwise--deletewould remove it:sudo rsync -aHAX --numeric-ids --delete \ --rsync-path="sudo rsync" \ your_user@old_server_ip:/var/vmail/ /var/vmail/ -
Change the
Arecord ofmail.example.comtonew_server_ipin DNS. With the TTL lowered to 300 seconds, most senders switch within minutes and deliver the mail they queued. -
Check that the new server is running and receiving connections:
sudo systemctl status postfix dovecot --no-pager
Keep the old server stopped but intact for at least a week, in case you need to recover something.
Step 7 - Verifying the new server
Wait for the new DNS record to be visible:
dig +short mail.example.com A
Test IMAP over TLS and check that the certificate is the right one:
openssl s_client -connect mail.example.com:993 -quiet
depth=0 CN = mail.example.com
verify return:1
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ AUTH=PLAIN AUTH=LOGIN] Dovecot (Ubuntu) ready.
Press Ctrl+C to exit. Then send a test message from an external mailbox (for example a Gmail account) to a user on your domain and follow it in the log:
sudo journalctl -u postfix -f
A successful delivery ends with status=sent. Reply from your domain to the external mailbox to test outgoing mail, then open the received message headers and check that SPF, DKIM and DMARC show pass.
Finally, log in with an email client and confirm that folders and old messages are there.
Troubleshooting
Mailboxes are empty or users get permission errors: the UID or GID of the files does not match the mail user. Check with ls -ln /var/vmail and fix ownership with sudo chown -R vmail:vmail /var/vmail.
warning: database /etc/postfix/virtual.db is older than source file: a lookup table was not rebuilt. Run sudo postmap on it and reload Postfix with sudo postfix reload.
Outgoing mail stays in the queue with Connection timed out on port 25: outbound port 25 is blocked on the new server. Check with nc -vz gmail-smtp-in.l.google.com 25 and ask your provider to open it.
Messages rejected by Gmail or Outlook as unauthenticated: the PTR record, SPF record or DKIM signature is wrong. Check the rejection text in journalctl -u postfix, which names the failing check.
Conclusion
You moved a Postfix and Dovecot server to a new host, copied its configuration, keys and mailboxes, and switched DNS without losing messages, since senders queued mail during the short cutover. When everything has worked for a few days, remove the old IP from your SPF record, raise the DNS TTLs back to their usual values, and decommission the old server. Setting up automated backups of /var/vmail and monitoring your IP on public blocklists are good next steps.
