Archive mail by year and month with dovecot

I’ve quite an extensive mail history starting from 2002.

Some of my Mail Clients do have issues when there are like 1000+ Mails in one folders. Leading to a limited view when the mail archive is just one big pile of mails.

Also using archive folders for each year leads to the same issue of not displayed messages.

I am using some Mail Clients (MUA) that support “Archive Mail” which does just move mails to an “Archive” folder.

Mozilla Thunderbird does have a function to archive mails to “year/month” folders. Since I am not using Thunderbird as a MUA anymore (specially on iOS devices), I needed a different solution.

I had a script for quite some time that just moved mails from the generic “Archive” folder to a year based folder structure.

But that wasn’t sufficient. So I adapted the approach to the following solution.

(Disclamer: I am using a dovecot imap server running in an Alpine Linux based Docker Container, therefore I had to add the check for the coreutils date availability. You might want to adjust that part.)



# The path in dovecote mail folder to put the subfolders to

# The paths of the folders to be processed during archive
BOXES_TO_ARCHIVE=("INBOX.Archive" "Archive*")

# Define from what - relatine to now - date in the past the archiving should be started
# use date "-d '...' " notation
# eg for initial imports this could be set to "-20 year"

# Define until which - relative to now - date mails should be archived, mails younger that date that date are omitted
# use date "-d '...' " notation
RETENTION="-1 month"

# Check if coreutils date is present in Alpine Linux
# "-/+ x month" operations in date are not supported in busybox date implementation
# Therefore coreutils date is required
date '+%Y-%m-%d' -d "+1 month"
if [ $? -ne 0 ]; then
  # Install coreutils in Alpine Linux
  apk add --no-cache coreutils

date '+%Y-%m-%d' -d "+1 month"
if [ $? -ne 0 ]; then
  # install failed
  echo "installing coreutils date failed - exiting"
  exit 1

# Search for all existing subfolders in BOXES_TO_ARCHIVE
for BOX in ${BOXES_TO_ARCHIVE[@]}; do
    BOXES+=($(doveadm mailbox list -u ${USER} ${BOX}))

ENDDATE=$(date '+%s' -d "${RETENTION}")
isodateiter="$(date '+%Y-%m-%d' -d "${ARCHIVE_START_DATE}")"

while [[ $(date +%s -d $isodateiter) -le $ENDDATE ]]; do

  BEFORE="$(date '+%Y-%m-%d' -d "$isodateiter +1 month")"

  for BOX in ${BOXES[@]}; do
    # echo "# Check if there is anything to archive in BOX ${BOX} for period between BEFORE and SINCE"
    # echo "doveadm search -u ${USER} MAILBOX ${BOX} SENTBEFORE ${BEFORE} SENTSINCE ${SINCE} | wc -l"
    # Check if there is anything to archive in BOX ${BOX} for period between BEFORE and SINCE
    echo "checking for mails in ${BOX} SINCE ${SINCE} AND BEFORE ${BEFORE}"
    if [ $(doveadm search -u ${USER} MAILBOX ${BOX} SENTBEFORE ${BEFORE} SENTSINCE ${SINCE}  | wc -l) -gt 0 ]; then
      # Create and subscribe ARCHIVE subfolder if it doesn't exist
      # echo "# Create and subscribe ARCHIVE subfolder if it doesn't exist"
      doveadm mailbox status -u ${USER} messages ${ARCHIVE} >/dev/null 2>&1
      if [ $? -ne 0 ]; then
        echo "creating new ARCHIVE Folder ${ARCHIVE}"
        doveadm mailbox create -u ${USER} ${ARCHIVE}
        doveadm mailbox subscribe -u ${USER} ${ARCHIVE}
      # Move the mails to ARCHIVE subfolder
      # echo "# Move the mails to ARCHIVE subfolder"
      echo "Move mails to ARCHIVE folder ${ARCHIVE}"
      doveadm move -u ${USER} ${ARCHIVE} mailbox ${BOX} SENTSINCE ${SINCE} SENTBEFORE ${BEFORE}


Using a cron job to trigger this script periodically in the docker container does the magic for me.

You are also able to adjust the script to be run once to structure all you existing mails. You might want to adjust the “BOXES_TO_ARCHIVE” and “ARCHIVE_START_DATE” to match you existing mail archive setup for this.