Backup, copy and move your emails between IMAP servers, or between an IMAP server and your local machine.
A single imap binary, with no runtime dependencies, that can:
- copy every mailbox of an account to another account, incrementally: rerunning the same copy only transfers new messages
- back up an IMAP account to a local Maildir or to a single compressed database file
- restore a local backup to an IMAP server
- list mailboxes and message counts
- find duplicate messages in an account
- update itself from GitHub releases
- Supported backends
- Installation
- Quick start
- Configuration file
- Commands
- How the incremental copy works
- Running in Docker
- Development
Every account in the configuration file has a type:
| Type | Description | Notes |
|---|---|---|
imap |
Remote IMAP server over TLS | Uses the UIDPLUS extension when the server supports it |
maildir |
Maildir folder on disk | Not supported on Windows |
local |
Single database file of compressed emails (bbolt) | Works on all platforms |
Any backend can be used as a source or a destination, so you can copy IMAP to IMAP, IMAP to local, local to IMAP, Maildir to local, and so on.
Download the archive for your platform from the releases page and put the imap binary somewhere in your PATH.
Binaries are built for Linux, macOS, Windows and FreeBSD on amd64, 386, arm and arm64 (not every combination is available, see the release assets).
Once installed, you can keep it up to date with:
imap selfupdateImages are published for linux/amd64 and linux/arm64 on both Docker Hub and the GitHub Container Registry:
creativeprojects/imapghcr.io/creativeprojects/imap
See Running in Docker for usage.
Requires Go 1.27 or later.
go install github.com/creativeprojects/imap@latestOr clone the repository and build with make:
git clone https://github.com/creativeprojects/imap.git
cd imap
make build-
Create a file named
imap.yamlin the current directory describing your accounts (see Configuration file):accounts: work: type: imap serverURL: imap.example.com:993 username: me@example.com password: secret backup: type: local file: ./work-backup.db
-
Check that the connection works and see what is in the account:
imap list work
-
Copy everything from the IMAP account to the local backup:
imap copy work backup
-
Run the same command again later: only messages received since the last copy are transferred.
imap copy work backup
The configuration is a YAML file. By default imap looks for imap.yaml in the current directory; use --config (or -c) to point to another file.
---
accounts:
imap-user:
type: imap
serverURL: localhost:993
username: user@example.com
password: pass
skipTLSverification: true
maildir-test:
type: maildir
root: ./maildir-test
local-test:
type: local
file: ./local/test.dbEach key under accounts is the name you will use on the command line.
| Field | Required | Description |
|---|---|---|
serverURL |
yes | Host and port of the server, for example imap.example.com:993. The connection always uses TLS (minimum TLS 1.2), so use the implicit TLS port. |
username |
yes | Login name |
password |
yes | Password |
skipTLSverification |
no | Set to true to accept a self-signed or otherwise invalid certificate. Defaults to false. |
| Field | Required | Description |
|---|---|---|
root |
yes | Directory containing the Maildir. It is created if it does not exist. |
| Field | Required | Description |
|---|---|---|
file |
yes | Path to the database file. It is created if it does not exist. |
imap [command] [flags]
| Flag | Default | Description |
|---|---|---|
-c, --config |
imap.yaml |
Configuration file |
-q, --quiet |
Only display warnings and errors | |
-v, --verbose |
Display debugging information, including the IMAP conversation |
Display the mailboxes of an account and the number of messages in each.
imap list <account>Copy all mailboxes and messages from one account to another. Mailboxes are created on the destination if they do not exist. The copy is incremental: see How the incremental copy works.
imap copy <source account> <destination account>Message flags (seen, flagged, answered, etc.) and the internal date of each message are preserved.
Display the history of copy operations recorded on an account, mailbox by mailbox.
imap history <account>With --verbose, it also shows the date from which the next copy will start for each source account.
Read every mailbox of an account and report messages that appear more than once (across all mailboxes). Messages are compared by a hash of their content.
imap duplicates <account>This command only reports duplicates, it does not delete anything. On an imap account, every message is downloaded to compute its hash, so it can take a while on a large account.
Download the latest release from GitHub and replace the current binary.
imap selfupdateEach copy records which messages were copied in a history saved on the destination account. The history associates the message IDs of the source with the message IDs created on the destination.
On the next run, the copy:
- only fetches messages from the source with an internal date later than the latest message in the history
- skips any message whose source ID is already in the history
Where the history is stored depends on the destination backend:
| Destination | History location |
|---|---|
local |
Inside the database file |
maildir |
A file <mailbox name>.history.json in the Maildir root |
imap |
A folder .cache/<account ID>/ in the current working directory, one <mailbox name>.history.json file per mailbox |
If you delete the history, the next copy starts from scratch and all messages are copied again.
Note that when the destination is an IMAP server, the history lives on the machine running imap, not on the server. Run the copy from the same working directory every time to keep it incremental.
Each account is given an account ID, which is recorded in the history so that several sources can be copied into the same destination. How the ID is generated depends on the backend:
| Backend | Account ID |
|---|---|
local |
Randomly generated when the database is created and stored inside it |
maildir |
Randomly generated when the Maildir is created and stored in .account.metadata.json in the Maildir root |
imap |
Derived from the server URL and the login name (not stored anywhere) |
If the connection to an IMAP server is lost in the middle of a copy, the messages copied so far are saved in the history. Rerun the copy command and it resumes from where it stopped.
The image has imap as its entrypoint and uses /imap as its working directory. Mount a local directory there containing your imap.yaml; the same directory will receive local backups, Maildirs and the .cache folder for IMAP history.
docker run --rm -v "$PWD:/imap" creativeprojects/imap list work
docker run --rm -v "$PWD:/imap" creativeprojects/imap copy work backupAny path in the configuration file should be relative to /imap (or simply relative, like ./backup.db).
make build # build the imap binary
make test # run the test suite
make coverage # run the tests and open the coverage report
make lint # run golangci-lint for darwin, linux and windowsA Dovecot image is provided for testing against a real IMAP server:
make dovecotThis builds the image and starts a container listening on ports 143 and 993. Any username is accepted with the password pass, and the server uses a self-signed certificate, so set skipTLSverification: true in your configuration:
accounts:
dovecot:
type: imap
serverURL: localhost:993
username: test
password: pass
skipTLSverification: true| Directory | Content |
|---|---|
cmd/ |
Command-line interface (one file per command) |
cfg/ |
Configuration file loading |
storage/ |
The Backend interface and its implementations: remote (IMAP), mdir (Maildir), local (bbolt) and mem (in-memory, for tests) |
mailbox/ |
Shared types: messages, properties, history |
lib/ |
Helpers: account tags, flags, UID handling, test email generator |
dovecot/ |
Dockerfile for the test IMAP server |