Understanding the Mail Flow
Postfix is primarily an Mail Transfer Agent (MTA). It receives email from the internet and hands it off to a Mail Delivery Agent (MDA) for local storage. On modern Ubuntu systems, this role is typically filled by Dovecot. When a user connects via POP3, they are interacting with Dovecot, not Postfix directly. Therefore, any logic regarding deleting old emails must be implemented within the Dovecot configuration or through external scripts that manage the maildir structure Dovecot uses.
This separation of concerns is critical. If you attempt to force Postfix to delete messages upon receipt based on age, it is technically impossible because the message has no age at the moment of arrival. Deletion is a lifecycle management task performed after storage. This approach differs significantly from IMAP Gmail email download and removal, where client-side rules often dictate server-side state changes differently than pure POP3 retrieval.
Configuring Dovecot for POP3
Before implementing deletion strategies, ensure Dovecot is correctly configured to serve POP3. Edit the main configuration file, typically located at /etc/dovecot/dovecot.conf. You need to enable the POP3 protocol in the protocols directive:
protocols = imap pop3 lmtp
Next, verify that the mail location is set correctly. Most Ubuntu installations use Maildir format, which stores each email as a separate file. This makes automated deletion much easier than mbox formats, which store all emails in a single large file. Check the mail_location setting:
mail_location = maildir:~/Maildir
Ensure that the pop3_uidl_format is defined if you want clients to track which messages have been seen, although this does not directly affect deletion. The key here is that every user's emails reside in their respective home directory under ~/Maildir/new, ~/Maildir/cur, and ~/Maildir/tmp.
Implementing Automated Deletion Scripts
Since Postfix and Dovecot do not natively support "delete emails older than X days" as a built-in rule for POP3 without complex sieve scripting, the most robust method is a cron job that iterates over system users. This addresses the common pitfall of assuming a single user exists on the server.
Create a script, for example, /usr/local/bin/clean-old-emails.sh. This script will find all mail directories for system users and remove files older than a specified number of days. Here is a safe implementation:
#!/bin/bash
# Days to keep emails
DAYS_TO_KEEP=30
# Iterate over all users who have a real home directory
for user_home in /home/*/; do
# Get the username from the path
username=$(basename "$user_home")
# Define the maildir path
maildir="${user_home}Maildir"
# Check if maildir exists
if [ -d "$maildir" ]; then
echo "Cleaning maildir for $username..."
# Find and delete old files in 'new' and 'cur' directories
# We exclude 'tmp' to avoid deleting active deliveries
find "${maildir}/new" -type f -mtime +${DAYS_TO_KEEP} -delete
find "${maildir}/cur" -type f -mtime +${DAYS_TO_KEEP} -delete
# Optional: Clean empty directories
find "${maildir}" -type d -empty -delete
fi
done
This script safely handles multiple accounts. It checks for the existence of the Maildir before attempting operations, preventing errors if a user has no mailbox yet. Make the script executable:
sudo chmod +x /usr/local/bin/clean-old-emails.sh
Scheduling with Cron
To automate this process, add the script to the root crontab. Open the crontab editor:
sudo crontab -e
Add a line to run the script daily at 3 AM:
0 3 * * * /usr/local/bin/clean-old-emails.sh >> /var/log/email-cleanup.log 2>&1
This ensures that old emails are removed consistently without impacting live mail delivery. Logging output to /var/log/email-cleanup.log allows you to monitor the script's activity and troubleshoot any permission issues.
Security and Permissions Considerations
Running this script as root is necessary to access other users' home directories. However, you must ensure that the script does not inadvertently delete files belonging to services or system processes. The check for ~/Maildir provides a basic safeguard. Additionally, consider restricting the script's execution to only specific users if your server hosts mixed workloads.
If you are managing a small team, you might prefer manual cleanup tools. For more advanced threading and organization needs, refer to Combine Messages in Single Thread by Default, which discusses how clients organize stored messages, though this is largely a client-side feature independent of server-side deletion.
Frequently Asked Questions
Does Postfix itself delete old emails?
No, Postfix is an MTA and does not store emails long-term. It passes messages to Dovecot or another MDA. Deletion logic must be handled by the storage layer, such as Dovecot, or via external scripts managing the filesystem.
Will deleting old emails affect active POP3 connections?
Generally, no. The script deletes files from the filesystem. If a user is currently downloading an email, the file is locked. The find command may skip locked files depending on permissions, but typically, deletion happens when the file is not open. It is safest to run cleanup during low-traffic hours.
Can I use Sieve scripts for deletion instead of cron?
Yes, Dovecot Sieve allows server-side filtering. You can write a script to move old emails to a Trash folder or delete them upon arrival. However, this applies to new mail processing. For cleaning up existing backlog, a cron job scanning the filesystem is often simpler and more reliable for bulk operations.
What happens to emails in the Trash folder?
The provided script targets new and cur directories. Emails moved to Trash by a client remain in the Trash folder until manually deleted or until a separate cleanup routine targets the Trash directory specifically. Adjust the find paths in the script if you wish to include Trash.
How do I verify the script worked?
Check the log file specified in the crontab entry (/var/log/email-cleanup.log). You can also manually run the script as root and observe the output. Verify that the timestamp of remaining files in ~/Maildir/new is newer than the DAYS_TO_KEEP threshold.