Migrations

With mail server configuration best practices changing over time we might need to make changes that require you to complete manual migration steps before you can deploy a new version of NixOS mailserver.

The initial mailserver.stateVersion value should be copied from the setup guide that you used to initially set up your mail server. If in doubt you can always initialize it at 1 and walk through all assertions, that might apply to your setup.

NixOS 26.05

#5 Sieve script directory migration

Sieve scripts managed by users via ManageSieve were previously stored in /var/sieve (or via the now-removed option mailserver.sieveDirectory). This setup partially mirrored the mail directory structure in /var/vmail (mailserver.storage.path), which proved to be fragile and error-prone.

Thanks to a prior migration, we can now migrate these directories into each user’s home directory, aligning with upstream recommendations — almost nine years later.

This migration is only required if you have mailserver.enableManageSieve enabled.

  1. If you are coming from 25.11 and are using LDAP, temporarily disable mailserver.enableManageSieve by setting it to false, deploy, and only then continue with this migration.

    If you are not coming from 25.11 or are not using LDAP, continue with this migration as is.

  2. Copy the migration script to your mailserver and make it executable:

    cd /tmp
    wcurl https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/raw/main/migrations/nixos-mailserver-migration-05.py
    chmod +x nixos-mailserver-migration-05.py
    
  3. Stop the postfix.service.

    systemctl stop postfix.service
    
  4. Create a backup or snapshot of your mailserver.sieveDirectory, so you can restore should anything go wrong.

  5. Run the migration script and pass your mailserver.sieveDirectory as argument:

    The script should be run under the user who owns the mailserver.sieveDirectory. If run as root it will automatically switch into the appropriate user by itself.

    The script will not modify your data unless called with --execute.

    The migration script finds all Sieve script directories in /var/sieve/ (or any other mailserver.sieveDirectory), for example that of bob at /var/vmail/bob@example.com.

    It then takes bob@example.com and looks up the home directory for bob. Finally, it starts suggesting the necessary move operations to migrate the Sieve directory to /var/vmail/example.com/bob/sieve and symlinks the active script to /var/vmail/example.com/bob/.dovecot.sieve.

    Example:

    ./nixos-mailserver-migration-05.py \
       /var/sieve
    
  6. Review the script output.

    The script can highlight various inconsistencies and problems, that should be reviewed and acted upon.

    If in doubt, join our community chat for help before applying any changes.

  7. Rerun the command with --execute or run the proposed commands manually.

  8. Update the mailserver.stateVersion to 5.

  9. The previous Sieve directory (mailserver.sieveDirectory) should now be safe to delete.

  10. If you temporarily disabled mailserver.enableManageSieve in step 1, re-enable it now by setting it back to true.

#4 Dovecot LDAP UUID-based home directories

LDAP Support in NixOS mailserver was introduced during the 23.11 release cycle and came with a number of flaws that we are correcting now, three years later.

This particular migration is needed because up until now we were relying on email addresses to construct the Dovecot home directory path (var/vmail/ldap/user@example.com) which is fragile: addresses can change, requiring manual homedir relocation. Switching to UUID-based homedirs (/var/vmail/ldap/<uuid>) ensures stable, unique paths and applies well-known best practices to mailserver management.

This migration is only required if you have mailserver.ldap.enable enabled.

  1. Copy the migration script to your mailserver and make it executable:

    cd /tmp
    wcurl https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/raw/main/migrations/nixos-mailserver-migration-04.py
    chmod +x nixos-mailserver-migration-04.py
    
  2. Stop the dovecot.service.

    systemctl stop dovecot.service
    
  3. Create a backup or snapshot of your mailserver.storage.path, so you can restore should anything go wrong.

  4. Run the migration script and pass the required arguments to enable LDAP lookups:

    The script should be run under the user who owns the mailserver.storage.path. If run as root it will automatically switch into the appropriate user by itself.

    The script will not modify your data unless called with --execute.

    The migration script finds all Dovecot home directories in /var/vmail/ldap/ (or any other mailserver.storage.path), for example that of bob at /var/vmail/ldap/bob@example.com. It then takes bob@example.com and queries the LDAP server for mail=bob@example.com to retrieve the UUID attribute. Finally it starts suggesting the necessary move operations to arrive at /var/vmail/ldap/f3b4e8ea-087f-42cc-95f0-cbfd99386092 for bob.

    Example:

    ./nixos-mailserver-migration-04.py \
      --ldap-uri ldaps://ldap1.example.com
      --ldap-bind-dn cn=mail,ou=accounts,dc=example,dc=com \
      --ldap-bind-pw-file /run/keys/ldap-bind-pw \
      --ldap-base ou=people,ou=accounts,dc=example,dc=com \
      --ldap-scope sub \
      --ldap-filter "(mail=%s)" \
      --ldap-attr-uuid entryUUID \
       /var/vmail
    

    For the --ldap-attr-uuid parameter we expect a long-term stable identifier, ideally a UUID field. The exact attribute name depends on your LDAP implementation, for example:

    • Authentik: uid [1]

    • Kanidm: uuid [2]

    • Keycloak entryUUID

    • OpenLDAP: entryUUID (RFC4530)

    If your LDAP provider isn't listed you can determine the correct attribute by querying a user entry with ldapsearch. Finally, configure mailserver.ldap.attributes.uuid accordingly.

    Add --ldap-starttls if you use the the ldap:// URI scheme and require explicit TLS.

  5. Review the script output.

    It's primary job is to determine the UUID for an LDAP account, so that it can rename the Dovecot home directory from mail address to UUID within the same directory.

    The script can highlight various inconsistencies and problems, that should be reviewed and acted upon.

    If in doubt, join our community chat for help before applying any changes.

  6. Rerun the command with --execute or run the proposed commands manually.

  7. Update the mailserver.stateVersion to 4.

NixOS 25.11

#3 Dovecot mail directory migration

The way the Dovecot home directory for login accounts were previously set up resulted in shared home directories for all those users. This is not a supported Dovecot configuration.

To resolve this we migrated the home directory into the individual domain/localpart subdirectory below the mailserver.mailDirectory.

But since this now overlaps with the location of the Maildir, it must be migrated into the mail/ directory below the home directory. And while the LDAP home directory is not affected we use this migration to keep the Maildir configurations of LDAP users in sync with those of local accounts.

This is a big step forward, since we can now more cleanly colocate other data directories, like sieve in the home directory, which in turn simplifies backups.

This migration is required for every configuration.

For remediating this issue the following steps are required:

  1. Copy the migration script script to your mailserver and make it executable:

    wcurl https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/raw/main/migrations/nixos-mailserver-migration-03.py
    chmod +x nixos-mailserver-migration-03.py
    
  2. Stop the dovecot.service.

    systemctl stop dovecot.service
    
  3. Create a backup or snapshot of your mailserver.mailDirectory, so you can restore should anything go wrong.

  4. Run the migration script under your virtual mail user with the following arguments:

    • --layout default unless useFSLayout is enabled, then --layout folder

    • The value of mailserver.mailDirectory, which defaults to /var/vmail

    The script should be run under the user who owns the mailDirectory. If run as root it will try to switch into the appropriate user by itself.

    The script will not modify your data unless called with --execute.

    Example:

    ./nixos-mailserver-migration-03.py --layout default /var/vmail
    
  5. Review the commands. They should be

    • create a mail directory for each account,

    • move maildir contents from the parent directory into it,

    • suggest removal of files that do not belong to the maildir

      • their removal is not mandatory and the script will not remove them when called with --execute

      • review these items carefully if you want to remove them yourself

    • remove obsolete files from the old home directory location

  6. Rerun the command with --execute or run the commands manually.

  7. Update the mailserver.stateVersion to 3.

#2 Dovecot LDAP home directory migration

The Dovecot configuration for LDAP home directories previously did not respect the mailserver.mailDirectory setting.

This means that home directories were unconditionally located at /var/vmail/ldap/%{user}.

This migration is required if you both:

  • enabled the LDAP integration (mailserver.ldap.enable)

  • and customized the default mail directory (mailserver.mailDirectory != "/var/vmail")

For remediating this issue the following steps are required:

  1. Stop dovecot.service.

  2. Move /var/vmail/ldap below your mailserver.mailDirectory.

  3. Update the mailserver.stateVersion to 2.

#1 Initialization

This option was introduced in the NixOS 25.11 release cycle, in which case you can safely initialize its value at 1.

mailserver.stateVersion = 1;