Sign inSign up

unsalted1832/imap-backup

By unsalted1832

•Updated 4 days ago

Containerized mbsync IMAP backup & restore — multi-arch, CVE-scanned

Image
Databases & storage
0

3.3K

unsalted1832/imap-backup repository overview

⁠imap-backup

Containerized mbsync⁠ (isync) that mirrors a list of IMAP accounts — defined in a .env file — into local Maildir backups, and can restore those backups to a new IMAP server.

  • Lightweight: Alpine + isync, ~20 MB image, multi-arch (amd64 + arm64).
  • Backup: long-running container, one sync cycle every SYNC_INTERVAL.
  • Files on disk: one message = one file under <backup>/<account>/, restorable and greppable.
  • Restore/migrate: push a Maildir back up to a new server.
  • Scanned: every published image passes a Trivy CVE gate (fails on fixable HIGH/CRITICAL); rebuilt weekly for upstream patches.

šŸ“– Full documentation & source: https://github.com/Josiah-OGT/imap-backup⁠

⁠Tags

latest, vX.Y.Z (releases), isync-<version> (e.g. isync-1.5.1), sha-<short>, and a YYYYMMDD date tag on the weekly build. Pin a vX.Y.Z or isync-<version> tag for reproducibility; use latest for the freshest isync.

docker pull unsalted1832/imap-backup:latest

⁠Quick start (Docker Compose)

1. Create docker-compose.yml:

services:
  imap-backup:
    image: unsalted1832/imap-backup:latest
    container_name: imap-backup
    env_file: .env
    restart: unless-stopped
    volumes:
      - ./backups:/backups
      - ./logs:/logs

2. Create .env with your accounts (numbered blocks; gaps are fine):

# Global (all optional — defaults shown)
SYNC_INTERVAL=1h
RETAIN_DELETED=false        # false = exact mirror; true = keep server-deleted mail
LOG_LEVEL=normal            # normal | verbose | debug

# Account 1 (minimum: HOST, USER, PASS)
ACCOUNT_1_HOST=imap.gmail.com
[email protected]
ACCOUNT_1_PASS=app-password-here
# ACCOUNT_1_PORT=993        # optional (default 993)
# ACCOUNT_1_TLS=IMAPS       # IMAPS (default) or STARTTLS (+ PORT=143)

For Gmail/Outlook use an app password, not your login password. To avoid plaintext, use ACCOUNT_N_PASSCMD (any command that prints the password).

3. Start the backup service:

mkdir -p backups logs
docker compose up -d
docker compose logs -f      # live; also written to ./logs/imap-backup.log

⁠One-off commands

The running backup service is left untouched:

docker compose run --rm imap-backup sync-once     # a single backup cycle
docker compose run --rm imap-backup restore       # restore all accounts
docker compose run --rm imap-backup restore 1 3   # restore specific indices

⁠Stop / update

docker compose down                            # stop & remove the container
docker compose pull && docker compose up -d    # update to the latest image

⁠Without Compose (docker run)

docker run -d --name imap-backup --env-file .env \
  -v ./backups:/backups -v ./logs:/logs \
  unsalted1832/imap-backup:latest

Run a single cycle instead of the loop:

docker run --rm --env-file .env \
  -v ./backups:/backups -v ./logs:/logs \
  unsalted1832/imap-backup:latest sync-once

⁠Restore / migrate to a new server

Set the restore target per account in .env (omit any field to fall back to the original source value):

ACCOUNT_1_RESTORE_HOST=imap.newserver.com
[email protected]
ACCOUNT_1_RESTORE_PASS=new-password
# optional: RESTORE_PRESYNC=true   -> pull latest from source before pushing

Then run a one-off restore (restore pushes the local Maildir to the target):

docker compose run --rm imap-backup restore        # all accounts
docker compose run --rm imap-backup restore 1      # specific account(s)

⁠Health check

The service container reports healthy while backup cycles keep completing, and unhealthy when the loop stalls (no cycle activity for SYNC_INTERVAL + HEALTH_GRACE) or HEALTH_MAX_FAILURES consecutive cycles fail. A cycle still in progress counts as healthy, and one-off sync-once / restore runs always report healthy.

docker ps                          # STATUS shows (healthy) / (unhealthy)
docker inspect --format '{{.State.Health.Status}}' imap-backup

The failure reason (stale loop vs. failed cycles) is recorded in the docker inspect health log.

⁠Configuration reference

VariableDefaultMeaning
SYNC_INTERVAL1hTime between cycles (30, 30m, 1h, 1d).
RETAIN_DELETEDfalsefalse exact mirror (propagate server deletions); true archival (keep server-deleted mail).
RESTORE_PRESYNCfalsePull latest from source before a restore.
HEALTH_GRACE5mSlack beyond SYNC_INTERVAL before the loop counts as stale (unhealthy).
HEALTH_MAX_FAILURES3Consecutive failed cycles before reporting unhealthy.
LOG_LEVELnormalmbsync verbosity: normal (summary), verbose (-V), debug (-V -D).
LOG_DIR/logsLogfile directory (mount it).
LOG_MAX_SIZE10MRotate after this size.
LOG_KEEP7Rotated logs retained.
LOG_TIMESTAMPStrueISO timestamps on log lines.
LOG_COMPRESSfalsegzip rotated logs.
BACKUP_DIR/backupsMaildir root (mount it).
ACCOUNT_N_HOST / _USER / _PASS—Required per account.
ACCOUNT_N_PORT / _TLS / _NAME / _PASSCMD993 / IMAPS / address / —Optional.
ACCOUNT_N_RESTORE_HOST/_PORT/_USER/_PASS/_TLS/_PASSCMDsource valuesRestore target.

⁠Notes

  • File ownership: under rootful Docker the container runs as root, so backup files are owned by root. PUID/PGID are not app settings — to own the files as yourself, add a user: line to the service and feed it your host IDs:
        user: "${PUID:-0}:${PGID:-0}"   # add under the imap-backup service
    
    then set PUID/PGID (from id -u / id -g) in .env, or inline: PUID=$(id -u) PGID=$(id -g) docker compose up -d. Pre-create backups/ and logs/ as that user first. (With docker run, use --user "$(id -u):$(id -g)".)
  • SELinux (Fedora/RHEL): append :Z to the volume mounts, e.g. ./backups:/backups:Z.
  • Stopping: docker stop sends SIGTERM; the container finishes the in-progress account, then exits.
  • TLS: defaults to IMAPS (993) with the system CA bundle. Use ACCOUNT_N_TLS=STARTTLS (+ ACCOUNT_N_PORT=143) for STARTTLS servers.

Licensed under the terms in the repository⁠.

Tag summary

Content type

Image

Digest

sha256:d607f59d7…

Size

5 MB

Last updated

4 days ago

docker pull unsalted1832/imap-backup