Both providers hand out a sync cursor that expires, and the answer to either is the same: throw the cursor away and re-enumerate everything. v0.1.32 made that recovery correct. Nothing recorded that it happened. Once in a while it is housekeeping — a mailbox that sat untouched past the provider's retention window. Repeatedly it means something else: the account is not being synced inside that window at all, or a worker is dying mid-batch and never getting far enough to store a new cursor. The symptom either way is a mailbox that is intermittently behind, which from outside looks like nothing whatsoever. So the account now carries fullResyncCount and lastFullResyncAt, written where the two syncers already handle expiry — Gmail's 404/410 on a stale historyId, Graph's resyncRequired. The health page attaches it as a FACT on the existing "sync failing" card rather than as a card of its own, because on its own it is not a problem and a card would make it read as one. HealthFact gained labelParams for it. The inspector still emits keys and parameters and never a translated string, which is the rule that keeps the health page translatable and testable in one language. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
plMail
Self-hosted mail with a calendar beside it. Connect the mailboxes you already have — IMAP, Gmail, Outlook — and read them side by side in one inbox, on a server you own.
📖 Read the handbook →
Installing on Docker, Linux, WSL2, macOS or a NAS · every feature in detail · connecting Gmail and
Outlook, with the exact permissions to tick · how the internals work.
Also on the wiki and in docs/ — all three
are built from the same files.
What it is
A mail client you host yourself. It connects to the mailboxes you already have — IMAP, Gmail, Outlook — syncs them into your own PostgreSQL database, and gives them one inbox, one search box and one set of labels in the browser.
It is not a mail server: it doesn't receive mail from the internet or host your domain. Because everything is synced locally, search runs against your database, and your mail stays readable when a provider doesn't.
Highlights
- Every account in one place — IMAP, Gmail and Outlook side by side, sending aliases included. Credentials are encrypted before they touch the database.
- New mail arrives on its own — IMAP IDLE, Gmail and Outlook push, browser notifications.
- Search that means it — full text plus
from:,subject:,label:,has:,before:and friends, completed as you type. - Filters as a tree — any/all/none, nested, restated in plain English and counted against real mail before you save. Apply one to mail that arrived before it existed.
- A calendar beside the mail — two-way sync with Google, Microsoft and CalDAV, drag-to-move week grid, invitations answered in place, share and booking links.
- Files where you keep them — attach from and save to Drive, Photos, OneDrive, Dropbox, Nextcloud and Immich.
- Yours — two-factor auth, per-app JMAP passwords, 41 themes, English, German and Pirate.
A look around
Screenshots use demo data — none of the accounts, senders or messages shown are real.
Install
Images are published for linux/amd64 and linux/arm64. Nothing to fill in first: the secrets are
generated on first start, and the public address is asked for on the setup screen.
Docker
git clone https://github.com/karatektus/pl_mail.git
cd pl_mail
docker compose up -d
Open https://localhost and create your account.
TrueNAS SCALE
- Create a dataset for plMail — say
yourPool/yourDataSet/plmail. - Open
truenas.compose.yamland change one line to that path:path_data: &path_data /mnt/yourPool/yourDataSet/plmail - Apps → Discover → ⋮ → Install via YAML, paste the file, install.
Reach it on port 30080 and put a reverse proxy in front of it. Everything else in that file works
as it is; each setting explains itself in place.
Then
Add an IMAP mailbox in Settings → Mail accounts and you're done. Gmail and Outlook need OAuth credentials from Google or Microsoft first — the handbook walks through both, including the optional Pub/Sub topic for instant Gmail delivery.
Back up two things, separately: the PostgreSQL data and
var/secrets/generated.env. The database holds your mail; the key inside that file unlocks the credentials in it. Keeping them apart is the point — and losing the key means the stored mailbox passwords are gone.
Contributing
Bug reports and pull requests welcome — see CONTRIBUTING.md for the dev setup, the test suites and the console commands.
License
AGPL-3.0.






