Restore Selected Mail Safely with doveadm import
You will finish with a repeatable command for importing selected messages from a Dovecot Maildir or mdbox backup into a user's mailbox. The examples target the installed Dovecot 2.3.21 package, dovecot-core 1:2.3.21+dfsg1-2ubuntu6.5. Allow about fifteen minutes for a small test and longer for a real mailbox. You need a readable source mailbox, a configured Dovecot destination, and an account permitted to run doveadm mail commands.
The route
Jump straight to the step you need, or tick off Done means at the end.
This is a restore or merge operation, not a transport migration. It writes mail and can create mailboxes. Treat the source path, destination user and search query as a change plan. The command normally needs elevated privileges or an account with suitable Dovecot access, but the inspection commands below do not.
1. Check the installed command
Start with a read-only version and syntax check. The installed manpage is the contract for this host; do not copy options from a different Dovecot release without checking them:
$ doveadm --version
2.3.21
$ man doveadm-import
The command takes three positional arguments: source_location, dest_parent and search_query. The source uses the same style as Dovecot's mail_location setting, such as maildir:/backup/user/Maildir or mdbox:/backup/user/mdbox.
Checkpoint: write down the exact source path and destination user before proceeding. A typo in the source can turn a restore into an import from the wrong mailbox.
2. Make a narrow test plan
Choose the destination with -u. Put a non-empty parent mailbox such as Restore-2026-09 around the imported folders while testing. Dovecot creates that parent if it does not exist:
$ doveadm import -u '[email protected]' \
'maildir:/srv/backup/jane.doe/Maildir' \
'Restore-2026-09' \
'mailbox INBOX from [email protected]'
This example imports messages in the source INBOX whose sender matches [email protected]. Quote the query as one shell argument when it contains spaces. The query is a Dovecot search query, so use the search-query documentation for operators rather than guessing at shell syntax.
For a complete backup, use all as the query:
$ doveadm import -u '[email protected]' \
'mdbox:/srv/backup/jane.doe/mdbox' \
'Restore-2026-09' \
all
Do not use all as a first test against an uncertain source. A successful command can import considerably more mail than intended.
3. Decide which user opens the source
By default, the source is opened in the destination user's context. If the source belongs to another Dovecot user and the access model permits it, add -U:
# Run with the Dovecot administrator account or suitable privilege.
$ doveadm import -U '[email protected]' \
-u '[email protected]' \
'maildir:/srv/mail/backup-owner/Maildir' \
'Restore-2026-09' \
'mailbox INBOX all
-U changes the username used to open the source. It does not change the destination selected by -u. Check filesystem permissions and Dovecot's user database before running this form. If the source and destination have different system ownership, fix the access design rather than making a broad, permanent permission change just for one restore.
Checkpoint: with the command still on paper, confirm all four values: source user, destination user, source location and query. If any value is unknown, stop here.
4. Run one controlled import
Use -v when you want a progress counter. Add -s only if the destination parent and newly created mailboxes should be subscribed:
# Elevated operation: this creates mailboxes and copies messages.
$ doveadm -v import -u '[email protected]' -s \
'maildir:/srv/backup/jane.doe/Maildir' \
'Restore-2026-09' \
'mailbox INBOX from [email protected]'
import: copied messages
The exact progress text varies with the mailbox and installed build. The useful success signal is an exit status of zero followed by checking the destination through IMAP or a read-only doveadm command such as:
$ printf 'exit status: %s\n' "$?"
exit status: 0
$ doveadm search -u '[email protected]' mailbox 'Restore-2026-09/INBOX' ALL
The search output is host-specific. Check the message count and a few expected headers in a mail client before importing more. If you want the imported mail to appear in the user's normal view immediately, subscription is a separate choice from copying and should be deliberate.
5. Scale to a user list only after verification
For several known users, put one username per line in a file and use -F. Creating that file is an ordinary local shell action, but review it before the import:
$ sed -n '1,20p' /root/restore-users.txt
[email protected]
[email protected]
$ doveadm -v import -F /root/restore-users.txt \
'maildir:/srv/backup/common/Maildir' \
'Restore-2026-09' \
'mailbox INBOX from [email protected]'
-A gets users from the configured user database instead. Use it only when iteration is known to work. SQL needs a matching iterate_query, and LDAP needs matching iteration settings. The manpage also warns that a passwd user database can include system users below first_valid_uid. For a controlled recovery, -F is usually easier to audit than -A.
6. Handle duplicates and undo carefully
Do not assume a second run is harmless. Dovecot's import documentation says imports do not preserve UIDs or check whether a message is already present, although message flags are preserved. A retry after a timeout can therefore create duplicates. Record the query, source and destination before every run.
If the test used a dedicated parent such as Restore-2026-09, the clean recovery path is to stop, inspect it, and remove that imported mailbox using your normal Dovecot mailbox administration procedure only after confirming it contains no original mail. Mailbox deletion is destructive and is not an undo button for a mixed import. If you imported into an existing mailbox, there may be no safe automatic rollback; restore from a verified backup or remove only individually confirmed duplicate messages.
Done means
- The installed Dovecot version and exact source location were checked.
- The destination user, source user and search query were recorded and reviewed.
- A narrow import was run under a dedicated parent before any broad restore.
- The command returned status zero and the destination was checked for expected mail.
- You know whether new mailboxes should be subscribed.
- The import record is retained so a timeout is not blindly retried.