Containerized tool that helps sync two inboxes (e.g. Gmail) via IMAP, using
imapsync. Defaults to Gmail on both ends
(--gmail1 --gmail2 baked into the entrypoint — no need to set
--host1/--host2 yourself).
Published as ghcr.io/welworx/sync-gmail:latest (amd64 + arm64). GHCR
packages are private on first push — set it public in the package's GitHub
settings, or docker login ghcr.io first.
Full example, German→English Gmail sync, with the flags worth using by default:
docker run --rm -v sync-gmail-cache:/cache -v sync-gmail-logs:/logs ghcr.io/welworx/sync-gmail \
--user1 source@gmail.com --password1 'app-password-1' \
--user2 dest@gmail.com --password2 'app-password-2' \
--useuid --usecache --tmpdir /cache --maxsleep 30 --errorsmax 200 \
--log --logdir /logs \
--folderlast "[Gmail]/Gesendet" --folderlast "[Gmail]/Papierkorb" \
--folderlast "[Gmail]/Wichtig" --folderlast "[Gmail]/Markiert" \
--folderlast "[Gmail]/Entwürfe" --folderlast "[Gmail]/Spam" \
--folderlast "[Gmail]/Alle Nachrichten" \
--f1f2 "[Gmail]/Markiert=[Gmail]/Starred" \
--f1f2 "[Gmail]/Wichtig=[Gmail]/Important"- Gmail requires an app password (2FA) or OAuth2 — a plain account password won't authenticate.
-v sync-gmail-cache:/cache+--tmpdir /cache --usecache: persists imapsync's UID cache across runs, so re-runs skip already-synced messages instead of re-checking every message on both servers.-v sync-gmail-logs:/logs+--log --logdir /logs: keeps a log file per run (/logs/<timestamp>_user1_user2.txt) — off by default under Docker otherwise, and you'll want it the first time a message errors out.--useuid --maxsleep 30: UID-based dedup (more reliable than the header-based default) and a higher sleep ceiling (default is 2s) so the Gmail preset's bandwidth throttling can actually back off.--errorsmax 200: raises the abort threshold from the default 50 — a handful of transient per-message fetch errors (timeouts, dropped connections) over a large mailbox is normal and shouldn't abort the whole sync; imapsync just skips that message and keeps going.--folderlast/--f1f2: fixes German folder names the image's Gmail preset doesn't recognize — see Known limitations. Swap the German strings to whichever side (host1/host2) is actually the German-locale account; drop both flags entirely if neither account is German-locale.
Any other imapsync flag can be appended the same way — the entrypoint is exec-form, docker run args are simply appended to it.
- Gmail bandwidth limits: 2500MB/day IMAP download, 500MB/day upload, per account — exceeding it suspends the account for 1-24h. See Google's docs. The Gmail preset already sets
--maxbytespersecond 300_000; lower it further if you still hit the limit. - "All Mail" contains everything: Gmail exposes labels as IMAP folders; a message with no label only shows up in
[Gmail]/All Mail. - Non-English folder names: the baked-in Gmail preset's folder-ordering list is hardcoded English, and imapsync's folder auto-mapping only covers RFC 6154's categories (Sent/Trash/Drafts/Junk/Flagged/Archive/All) — with built-in strings for German Sent/Trash/Drafts, but not Starred. Important isn't RFC 6154 at all — it's Gmail-only, so automap never handles it, in any language. Both need an explicit
--f1f2(shown above); a same-named folder on both sides (e.g. English↔English) doesn't need mapping at all, since it already matches by name. --skipcrossduplicatesis deliberately not used above: per imapsync's own docs it's meant for Gmail→non-Gmail migrations and defaults off for Gmail→Gmail (label sync needs to visit each label-folder). Only add it if the destination isn't Gmail.- Vulnerability scanning is amd64-only: the CI build is multi-arch, but the scan step can't load a multi-platform image locally; arm64 uses identical package versions.
- Dependabot doesn't track the imapsync version: it's installed via
apk add imapsync, resolved from Alpine's own community repo at build time — Dependabot'sdockerecosystem only watches theFROM alpine:...base image tag/digest, not packages installed insideRUN. The weekly scheduled CI build rebuilds withno-cachespecifically soapk addactually re-fetches instead of reusing a cached layer (a cached rebuild would otherwise never pick up a newer imapsync even though the schedule fires), but there's still no active check — watch for imapsync's own self-reported staleness message on every run ("This imapsync is not up to date...", unless--noreleasecheckis passed).
Everything else — credentials from a file or env var instead of
--password1/--password2 (--passfile1/--passfile2,
IMAPSYNC_PASSWORD1/IMAPSYNC_PASSWORD2), and the full flag reference —
see the imapsync manual and
Gmail FAQ.