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/vmail or in each user's ~/Maildir).
  • A new server running Ubuntu 24.04 LTS, for example a CubePath VPS, with a non-root user with sudo privileges and SSH access from the new server to the old one as root or a sudo user.
  • 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}'

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 the live, archive and renewal structure intact).
  • DKIM keys and configuration: /etc/opendkim.conf and /etc/opendkim/, or the equivalent for Rspamd.
  • Lookup tables outside /etc/postfix, SQL connection files and Sieve scripts.
  • /etc/aliases, then rebuild it with sudo 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.

  1. 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 -p
    
    Mail queue is empty
    

    If some messages cannot be delivered (for example a remote server that keeps rejecting them), note them; they stay in /var/spool/postfix on the old server.

  2. 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
    
  3. On the new server, run the final mailbox sync. --delete removes 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 --delete would remove it:

    sudo rsync -aHAX --numeric-ids --delete \
      --rsync-path="sudo rsync" \
      your_user@old_server_ip:/var/vmail/ /var/vmail/
    
  4. Change the A record of mail.example.com to new_server_ip in DNS. With the TTL lowered to 300 seconds, most senders switch within minutes and deliver the mail they queued.

  5. 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.