When manually uploading or restoring emails into a Dovecot-managed mailbox, you may encounter two common issues:
- Incorrect mailbox size or quota reporting because Dovecot’s index files aren’t updated automatically.
- Incorrect email dates in the mail client because the file modification time (mtime) is set to the upload time rather than reflecting the original email’s “Date” header.
This guide explains how to rebuild the Dovecot indexes and recalculate mailbox quotas, and then how to correct the email file timestamps so that the original sent/received dates are displayed correctly in clients.
Updating Dovecot Indexes and Recalculating Quotas
When emails are manually added to the mail storage (for example, via direct file upload), Dovecot may not immediately reflect the changes because its internal index files and quota databases remain unchanged. To fix this, you need to run a couple of commands:
1. Rebuild the Dovecot Indexes
Dovecot uses index files to cache metadata for faster mail access. If new emails are added, the indexes must be rebuilt. Run the following command:
doveadm index -A "*"
-A
processes all user mailboxes.- The quoted
"*"
prevents shell wildcard expansion from including unintended files (especially if you’re running the command in a directory with extra content).
For a specific user, use:
doveadm index -u user@example.com "*"
This command forces Dovecot to re-read the mailbox directories and update its internal data structures.
2. Recalculate Mailbox Quotas
After reindexing, the quota system may still show incorrect values. Recalculate the quota usage with:
doveadm quota recalc -A
Or for a specific user:
doveadm quota recalc -u user@example.com
These commands ensure that the storage usage is recalculated from the current state of the mailbox.
Fixing Incorrect Email Dates
After a manual upload, email files inherit the upload time as their file modification time (mtime), which Dovecot uses when indexing. As a result, emails may display a date corresponding to the upload rather than the original “Date” header inside the email.
The Fix: Update File Modification Times
You can fix this by updating each email file’s mtime to match its “Date” header. To automate this, we’ve created a Bash script that:
- Processes both the inbox and sent folders.
- Extracts the “Date” header using
formail
(part of the procmail package). - Uses the
touch
command to update the file’s mtime. - Provides verbose output for every file processed.
Your Environment
In our setup:
- Inbox path:
/home/vmail/<domain>/<user>/cur
- Sent folder path:
/home/vmail/<domain>/<user>/.Sent/cur
The Script
Save the following script as update_email_dates.sh
:
#!/bin/bash
# update_email_dates.sh
# Usage:
# ./update_email_dates.sh user@example.com # Process specific user's inbox and sent folder
# ./update_email_dates.sh "*" # Process all user mailboxes (inbox and sent folder)
# Function to process email files in a given directory
process_mailbox_dir() {
local dir="$1"
if [ ! -d "$dir" ]; then
echo "Warning: Directory $dir does not exist. Skipping."
return
fi
echo "Processing directory: $dir"
for file in "$dir"/*; do
if [ ! -f "$file" ]; then
echo "Skipping $file: Not a regular file."
continue
fi
# Extract the Date header from the email using formail.
email_date=$(formail -c -x Date < "$file")
if [ -z "$email_date" ]; then
echo "Skipping $file: Date header not found."
continue
fi
# Validate and parse the date using the date command.
parsed_date=$(date -d "$email_date" +"%Y-%m-%d %H:%M:%S" 2>/dev/null)
if [ $? -ne 0 ]; then
echo "Skipping $file: Date header '$email_date' is not parseable."
continue
fi
# Update the file's modification time to the extracted date.
touch -d "$email_date" "$file"
echo "Updated $file: set mtime to $parsed_date"
done
}
if [ $# -ne 1 ]; then
echo "Usage: $0 <username (user@domain) or '*' for all users>"
exit 1
fi
# Base directory where mailboxes are stored.
# For this setup:
# Inbox: /home/vmail/<domain>/<user>/cur
# Sent: /home/vmail/<domain>/<user>/.Sent/cur
BASE_DIR="/home/vmail"
if [ "$1" = "*" ]; then
# Process all domains and users.
for domain in "$BASE_DIR"/*; do
[ -d "$domain" ] || continue
echo "Processing domain: $(basename "$domain")"
for user in "$domain"/*; do
if [ -d "$user" ]; then
# Skip directories that start with a dot (if any)
if [[ $(basename "$user") == .* ]]; then
continue
fi
echo "Processing inbox for user: $(basename "$user")"
process_mailbox_dir "$user/cur"
# Process the sent folder for the user if it exists.
if [ -d "$user/.Sent/cur" ]; then
echo "Processing sent folder for user: $(basename "$user")"
process_mailbox_dir "$user/.Sent/cur"
else
echo "No sent folder found for user: $(basename "$user")"
fi
echo "-------------------------------------------"
fi
done
done
else
# Process a specific user.
# Expect argument in the form user@domain.
email_addr="$1"
user_part=$(echo "$email_addr" | cut -d '@' -f1)
domain_part=$(echo "$email_addr" | cut -d '@' -f2)
if [ -z "$user_part" ] || [ -z "$domain_part" ]; then
echo "Error: Invalid email address format."
exit 1
fi
user_inbox="$BASE_DIR/$domain_part/$user_part/cur"
echo "Processing inbox for $email_addr: $user_inbox"
process_mailbox_dir "$user_inbox"
user_sent="$BASE_DIR/$domain_part/$user_part/.Sent/cur"
if [ -d "$user_sent" ]; then
echo "Processing sent folder for $email_addr: $user_sent"
process_mailbox_dir "$user_sent"
else
echo "No sent folder found for $email_addr"
fi
fi
echo "Email date correction completed."
How to Use the Script
- Install Dependencies:
Ensureformail
is installed (it’s part of theprocmail
package).- On Rocky Linux/Alma Linux/RHEL:
dnf install procmail
- On Debian/Ubuntu:
apt-get install procmail
- On Rocky Linux/Alma Linux/RHEL:
- Save the Script:
Save the script above asupdate_email_dates.sh
. - Convert Line Endings if Necessary:
If you created the script on a Windows machine, convert CRLF to LF. For example, usingdos2unix
:dos2unix update_email_dates.sh
- Make the Script Executable:
chmod +x update_email_dates.sh
- Run the Script:
- For a specific user:
./update_email_dates.sh user@example.com
This will update the inbox at/home/vmail/example.com/user/cur
and the sent folder at/home/vmail/example.com/user/.Sent/cur
. - For all users:
./update_email_dates.sh "*"
This command processes every user’s inbox and their corresponding sent folder across all domains.
- For a specific user:
- Review the Output:
The script provides verbose feedback for every file it processes, indicating whether:- The file was updated (with the new modification time printed).
- The file was skipped (with a reason, such as missing or unparseable Date header).
- Rebuild Dovecot Indexes:
Once the modification times have been updated, force Dovecot to rebuild its indexes:- For a specific user:
doveadm index -u user@example.com "*"
- For all users:
doveadm index -A "*"
- For a specific user:
Rebuilding the indexes ensures that Dovecot now uses the corrected mtime values, so your email clients will display the original sent/received dates.
Final Thoughts
By following these steps, you can ensure that manually restored emails display the correct dates and that Dovecot’s indexes and quotas are accurate. This comprehensive approach—from reindexing and quota recalculation to correcting file modification times with a custom script—helps maintain a reliable and consistent mail environment.
Share this guide with fellow sysadmins to help them troubleshoot similar issues