Move Older dbox Mail to Alternative Storage with doveadm altmove
A primary mail volume filling up is how teams first discover doveadm altmove: it shifts old dbox messages onto a second volume without users noticing. This guide covers a controlled move and, if needed, moving the same messages back. The examples target the installed dovecot-core 1:2.3.21+dfsg1-2ubuntu6.5, whose manual describes the Dovecot v2.3 interface.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow 20 to 30 minutes for a first run. You need access to the Dovecot host, a working sdbox or mdbox mailbox, an alternative filesystem mounted at its final path, and permission to run administrative doveadm commands. This changes message storage, so back up or snapshot a production account before testing on it.
1. Check that alternative storage is actually configured
doveadm altmove only works with dbox storage. It won't turn an ordinary Maildir into dbox, and it won't invent an alternative path for you. In the Dovecot 2.3 configuration style, the ALT= component of mail_location supplies that path:
mail_location = mdbox:~/mdbox:ALT=/nfsmount/%h/mdbox
Here ~/mdbox is the normal location and /nfsmount/%h/mdbox the alternative one. The exact path and variable expansion belong to your own configuration: don't paste this line into production just because the example uses it.
Check the effective configuration as an ordinary read-only step; it may need elevated privileges if Dovecot's configuration isn't readable by your account:
$ sudo doveconf -n | grep -E '^(mail_location|mail_driver|mail_path|mail_alt_path)'
Newer Dovecot documentation expresses the same idea with separate mail_driver, mail_path and mail_alt_path settings. Don't mix that newer model into a 2.3 host without checking that host's own documentation and config parser.
Checkpoint
Identify the primary and alternative paths, confirm the alternative mount is present, and confirm the mailbox format is sdbox or mdbox. If any of that is uncertain, stop here.
2. Start with one user and a narrow search
The command moves every message matching its search query, and there's no dry-run option in the installed doveadm-altmove(1) interface. Start with one account and a deliberately small, reviewable query; here's the documented shape for messages seen more than a week ago:
$ sudo doveadm altmove -u '[email protected]' seen savedbefore 1w
-u limits the operation to the named user. The words after the user are the search query, not shell options, interpreted by Dovecot's own search-query language. Quote a username or wildcard the shell could expand: -u '*@example.com', for example, matches every user at that domain.
The command is normally quiet unless verbosity is on. Add -v for a progress counter, or -D for debug output:
$ sudo doveadm -v altmove -u '[email protected]' seen savedbefore 1w
A clean return isn't proof the right messages got selected. Before a production move, run the equivalent search with a non-mutating mailbox inspection tool from your Dovecot setup, record the matching count, and test the query on a test account first: altmove itself has no count-only mode.
3. Move the selected data
Once the path, mount and query check out, run the narrow command as whoever is authorised to administer Dovecot, often root:
$ sudo doveadm altmove -v -u '[email protected]' seen savedbefore 1w
$ printf 'exit status: %s\n' "$?"
exit status: 0
Status 0 means it completed. It won't print a portable list of moved files, and the exact progress output depends on version and workload. The message stays part of the mailbox: only its dbox message data moves. Dovecot's own dbox documentation notes that indexes and other metadata stay in the primary location.
For mdbox, several messages can share one storage file, and Dovecot rewrites the data into an alternative file and updates the map so individual messages move without you touching dbox files by hand. Don't reach for cp or mv as a shortcut: the metadata updates have to stay coordinated.
Warning
This is a state-changing operation. If the alternative filesystem is read-only, missing, full, wrongly mounted, or reachable through a different path than the config expects, stop and fix that before retrying. Don't rerun a failed storage move repeatedly without checking Dovecot's error output and filesystem state first.
4. Verify mailbox access and the storage location
First confirm the command and service still see the mailbox: use the same narrow search with a read-only listing or search tool, and check the message is still visible to the user. Its absence from the primary directory isn't a failure on its own, that's the point of a successful move.
Then check the alternative tree with filesystem tools. Substitute your own expanded path rather than copying the placeholder:
$ sudo find '/nfsmount/EXPANDED-USER-PATH/mdbox' -maxdepth 1 -type f -printf '%f\n' | head
$ sudo df -h '/nfsmount/EXPANDED-USER-PATH/mdbox'
These show the mount is reachable and has storage files, not that one particular message sits in one particular file: dbox filenames and packing vary. The check that matters is that Dovecot can still open the mailbox and return the moved message through its normal search and delivery path.
5. Reverse the move once the primary path is ready
-r flips the direction: matching messages move from alternative storage back to the default location. Keep the same user restriction and query while recovering or testing:
$ sudo doveadm -v altmove -r -u '[email protected]' seen savedbefore 1w
$ printf 'exit status: %s\n' "$?"
exit status: 0
This is the practical undo for messages the original query selected, as long as alternative storage is still reachable and the primary volume has room. It isn't a general rollback log: if the original query now matches a different set of messages, the reverse command acts on that different set. Record the account and query for every run.
Don't delete alternative files by hand after reversing a move. Let Dovecot finish its storage and map updates, then verify access and free space on both paths.
6. Scale out only once a single user works cleanly
-A runs the command for every user the user database discovers, and that discovery depends on the iteration settings. The manual specifically warns about system users below first_valid_uid, and needs a matching iterate_query for SQL, or iterate_attrs and iterate_filter for LDAP. A bad iteration setup can miss users or catch accounts you didn't mean to touch.
$ sudo doveadm -v altmove -A seen savedbefore 1w
Use -F when an explicit file is safer than database iteration; it takes one username per line:
$ sudo doveadm -v altmove -F /root/dovecot-altmove-users.txt seen savedbefore 1w
Build and review that file separately, protect it from casual edits, and keep it as the audit record for the run. Both forms are still destructive storage changes: schedule them only after monitoring a single-user run, checking mount capacity, and agreeing how to recover a partial operation.
Done means
- Confirmed a reachable alternative path for
sdboxormdboxin the effective configuration. - Reviewed a single user's narrow search before running any move.
- Moved cleanly: status 0, and the mailbox still exposes the messages normally.
- Checked the alternative filesystem without assuming a particular dbox filename.
- Recorded the account and query, with
-rready for a controlled reversal. - Backed bulk
-Aor-Fruns with verified user selection and a recovery plan.