Skip to content
0

Local Store

This guide outlines a robust workflow for mirroring emails from a remote IMAP server to a local store archive, and archiving old emails to local storage.

IMAP stands for Internet Message Access Protocol, a standard protocol that lets you access and manage your emails on a remote server from multiple devices, keeping everything synchronized (reading, deleting, organizing) across your phone, computer, and webmail.

The core principle is to maintain a local mirror of your remote mailbox using mbsync, perform the archival (moving files) on the local mirror, and then use mbsync again to propagate the local changes (the "deletions" from the mirrored folder) back to the remote server.

Prerequisites

  1. isync Installed: You must have isync (which provides the mbsync command) installed on your system.
  • mbsync/isync
  • notmuch
bash
brew install isync

Create config file

  1. Configured isyncrc: A working ~/.config/isyncrc file must be set up for the target email account. The configuration must include Expunge Both to ensure that deletions on the local mirror are synced to the remote server.

Create either file is acceptable:

  • ~/.mbsyncrc (older style but acceptable)
  • ~/.config/isyncrc I use this config file

Example isyncrc Channel configuration:

Channel twine
Far :twine-remote:
Near :twine-local:
Patterns *
Sync All
Expunge Both  # <-- This is critical for the workflow
Create Near
Remove Near
SyncState *

Making the directories

Create a folder as the local store for each email account.

bash
~/.maildir/twine
~/.maildir/soundfreaq
~/.maildir/biaget

下面我们以 twine 为例进行配置。

Directory Structure

~/.maildir/
├── twine/           # Active IMAP sync folder (mbsync manages this)
│   ├── INBOX/
│   ├── Sent/
│   ├── Drafts/
│   └── Trash/
├── 2025/            # Local archive for 2025 emails (YOU manage this)
│   ├── cur/
│   ├── new/
│   └── tmp/
└── 2026/            # Local archive for 2026+ emails (YOU manage this)
    ├── cur/
    ├── new/
    └── tmp/

Key Point: Only twine/ is synced with the remote IMAP server. Archives (2025/, 2026/) are local-only.

Why Archive?

ProblemSolution
IMAP storage quota filling upMove old emails to local archives
Slow mailbox performanceKeep only recent emails synced
Need to retain old emails for referenceLocal archives remain accessible in NeoMutt
mbsync syncs everythingArchives are excluded from sync

The Archiving Process

Step 1: Perform a Full Initial Sync

Before making any changes, ensure your local mail mirror is perfectly up-to-date with the remote server. This is a critical safety step.

Run mbsync for your account (replace twine with your channel name if different):

bash
mbsync twine

Wait for this command to complete successfully. Your local ~/.maildir/twine/ directory now mirrors the remote server.

Step 2: Create the Local Archive Folder

Create a new directory on your local machine to store the archived emails. It's important to create it in the Maildir format, which requires cur, new, and tmp subdirectories.

bash
# Create an archive folder for emails from 2025
mkdir -p ~/.maildir/2025/{cur,new,tmp}

Step 3: Move Emails Locally

Understanding Maildir Timestamps

Maildir filenames start with a Unix timestamp (seconds since 1970-01-01):

1769655740.80796_1.AX-Mac-mini,U=429:2,S

└─┬─┘

  Timestamp = 1769655740 → Thu Jan 29, 2026 11:02:20 CST

Convert timestamps to dates:

bash
# Single timestamp
date -r 1769655740

# Preview all emails with dates in INBOX
for f in ~/.maildir/twine/INBOX/cur/*; do
    ts=$(basename "$f" | cut -d. -f1)
    echo "$ts → $(date -r "$ts" '+%Y-%m-%d')"
done | head -20

Common cutoff timestamps:

Archive DateCutoff TimestampDescription
End of 20251735689600Emails before Jan 1, 2026
End of Jan 20261769938460Emails before Feb 1, 2026
End of Feb 20261772530800Emails before Mar 1, 2026

Calculate your own:

bash
date -j -f "%Y-%m-%d %H:%M:%S" "2026-02-01 00:00:00" +%s

Method A: Manual Method using Yazi (File Manager)

If you prefer visual file browsing:

  1. Open Yazi (or Finder) and navigate to ~/.maildir/twine/INBOX/cur/
  2. Sort by name (filenames are timestamped, so oldest first)
  3. Select emails from January 2026 and earlier
  4. Move (not copy) selected files to ~/.maildir/2026/cur/
  5. Repeat for twine/INBOX/new/2026/new/ if any unread old emails exist

⚠️ Warning: Always move, never copy. Copying will cause duplicates and mbsync may re-upload them to the server.

Method B: Terminal Commands

Preview what will be moved:

bash
# Emails before Feb 1, 2026 (timestamp 1769938460)
CUTOFF=1769938460

echo "=== Emails to be archived ==="
count=0
for f in ~/.maildir/twine/INBOX/cur/*; do
    ts=$(basename "$f" | cut -d. -f1)
    if [ "$ts" -lt "$CUTOFF" ]; then
        echo "  $(date -r "$ts" '+%Y-%m-%d') : $(basename "$f" | head -c 50)..."
        count=$((count + 1))
    fi
done
echo "Total: $count emails"

Execute the move:

bash
CUTOFF=1769938460  # Adjust based on your needs

# Move from cur/
for f in ~/.maildir/twine/INBOX/cur/*; do
    ts=$(basename "$f" | cut -d. -f1)
    if [ "$ts" -lt "$CUTOFF" ]; then
        mv "$f" ~/.maildir/2026/cur/
    fi
done

# Move from new/ (if any unread old emails)
for f in ~/.maildir/twine/INBOX/new/*; do
    [ -f "$f" ] || continue
    ts=$(basename "$f" | cut -d. -f1)
    if [ "$ts" -lt "$CUTOFF" ]; then
        mv "$f" ~/.maildir/2026/new/
    fi
done

echo "Archive complete!"

Verify the move:

bash
# Count remaining in twine
echo "twine/INBOX/cur: $(ls -1 ~/.maildir/twine/INBOX/cur | wc -l)"
echo "twine/INBOX/new: $(ls -1 ~/.maildir/twine/INBOX/new | wc -l)"

# Count archived
echo "2026/cur:        $(ls -1 ~/.maildir/2026/cur | wc -l)"
echo "2026/new:        $(ls -1 ~/.maildir/2026/new | wc -l)"

Method C: Automation Script

Create a reusable archive script:

bash
#!/bin/bash
# ~/.local/bin/maildir-archive
# Usage: maildir-archive 2026-02-01

set -e

if [ -z "$1" ]; then
    echo "Usage: $0 <cutoff-date>"
    echo "Example: $0 2026-02-01"
    exit 1
fi

CUTOFF_DATE="$1"
CUTOFF_TS=$(date -j -f "%Y-%m-%d" "$CUTOFF_DATE" +%s)
ARCHIVE_DIR="$HOME/.maildir/2026"

echo "Archiving emails before $CUTOFF_DATE (timestamp < $CUTOFF_TS)"

# Create archive structure if needed
mkdir -p "$ARCHIVE_DIR"/{cur,new,tmp}

# Archive from INBOX
for folder in INBOX Sent Drafts; do
    src="$HOME/.maildir/twine/$folder"
    [ -d "$src/cur" ] || continue

    echo "Processing $folder/cur..."
    count=0
    for f in "$src/cur"/*; do
        [ -f "$f" ] || continue
        ts=$(basename "$f" | cut -d. -f1)
        if [ "$ts" -lt "$CUTOFF_TS" ]; then
            mv "$f" "$ARCHIVE_DIR/cur/"
            count=$((count + 1))
        fi
    done
    echo "  Moved $count emails"
done

echo "Done! Run 'mbsync twine' to sync changes to server."

Make it executable:

bash
chmod +x ~/.local/bin/maildir-archive

Usage:

bash
# Archive all emails before February 2026
maildir-archive 2026-02-01

# Archive all emails before March 2026
maildir-archive 2026-03-01

Step 4: Propagate Deletions to the Remote Server

This is the final and irreversible step. Run mbsync again.

bash
mbsync twine

mbsync will detect that hundreds of files are missing from your local Near mirror (~/.maildir/twine/INBOX). Because of the Expunge Both setting, it will interpret this as a command to delete those same emails from the Far remote server.

Once the command finishes, the old emails are now gone from the server and reside safely in your local archive.

Access the Archives in NeoMutt

Account Config with Local Archives

Update your maildir-twine config to add archives to the sidebar with visual labels:

bash
# Active folders (polled for new mail)
mailboxes -label "📥 INBOX"   -notify -poll "+INBOX"

# Standard folders (no poll, shown in sidebar)
mailboxes -label "📤 Sent"    -nonotify "+Sent" \
          -label "📝 Drafts"  -nonotify "+Drafts" \
          -label "🗑️ Trash"   -nonotify "+Trash"

# Archive folders (local only, no sync)
mailboxes -label "📁 2025"    -nonotify "~/.maildir/2025" \
          -label "📁 2026"    -nonotify "~/.maildir/2026"

Sidebar now shows:

│ 📥 INBOX   │  15
│ 📤 Sent    │   0
│ 📝 Drafts  │   3
│ 🗑️ Trash   │   0
│ 📁 2025    │ 450
│ 📁 2026    │ 120

Quick Navigation Macros

Add these to your config for instant archive access:

bash
# Press '25' for 2025 archive, '26' for 2026 archive
bind index,pager 2 noop
macro index,pager 25 "<change-folder>~/.maildir/2025<enter>" "Go to 2025 archive"
macro index,pager 26 "<change-folder>~/.maildir/2026<enter>" "Go to 2026 archive"

Using the Archives in NeoMutt

ActionKeyDescription
Toggle sidebar\ (backslash)Show/hide folder list
Navigate foldersCtrl+Up/DownMove through sidebar
Open folderEnterView selected folder's emails
Quick archive25 / 26Jump to 2025/2026 directly
Change folderCBrowse all folders
Return to INBOXq (until back)Navigate back to main inbox

Maintenance & Best Practices

Yearly Archive Rotation

At the start of each year, create a new archive:

bash
# January 2027: Create 2027 archive
mkdir -p ~/.maildir/2027/{cur,new,tmp}

# Add to maildir-twine config:
mailboxes -label "📁 2027" -nonotify "~/.maildir/2027"

Backup Your Archives

Archives are local-only, so backup is critical:

bash
# Time Machine (macOS) - automatic if ~/.maildir is included

# Or manual backup to external drive
rsync -av ~/.maildir/2025/ /Volumes/Backup/maildir-archives/2025/
rsync -av ~/.maildir/2026/ /Volumes/Backup/maildir-archives/2026/

Troubleshooting

IssueSolution
Archives don't appear in sidebarCheck paths are absolute (~/.maildir/2025 not +2025)
mbsync tries to sync archivesEnsure only twine/ is in mbsync config
Emails reappear after mbsyncYou copied instead of moved; delete originals
NeoMutt shows "unable to open mailbox"Verify folder structure: mkdir -p folder/{cur,new,tmp}

Disk Space Monitoring

bash
# Check archive sizes
du -sh ~/.maildir/2025 ~/.maildir/2026 ~/.maildir/twine

# Find largest emails in archive
find ~/.maildir/2026/cur -type f -exec du -h {} + | sort -rh | head -20

Resources

最近更新