# 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. ![PHP](https://img.shields.io/badge/PHP-8.4+-777BB4?logo=php&logoColor=white) ![Symfony](https://img.shields.io/badge/Symfony-8-000000?logo=symfony&logoColor=white) ![PostgreSQL](https://img.shields.io/badge/PostgreSQL-18-4169E1?logo=postgresql&logoColor=white) ![License](https://img.shields.io/badge/license-AGPL--3.0-green) ### ๐Ÿ“– [Read the handbook โ†’](https://karatektus.github.io/pl_mail/docs/) 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](https://github.com/karatektus/pl_mail/wiki) and in [`docs/`](docs/) โ€” all three are built from the same files. ### ๐Ÿ“ฑ Apps for your phone - **Android** โ€” [karatektus/pl_mail_android](https://github.com/karatektus/pl_mail_android) - **iOS** โ€” [karatektus/pl_mail_ios](https://github.com/karatektus/pl_mail_ios) Both connect to your own plMail server. --- ## 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. ![The plMail inbox](docs/screenshots/inbox.png) ## 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. Every hit shows the words it matched on, marked, in a window of the message around them. What the words found comes first; what the model merely thought looked similar follows, labelled, and never pushes a real match off the page. - **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. - **Filed where you drop it** โ€” drag a conversation onto a folder, a label or one of the inbox tabs, several at once if they are ticked. A folder moves it and a label just adds itself, so dropping on "Receipts" does not take the mail out of your inbox; **Starred** stars it and **Unread** โ€” an optional list of every unread conversation, whatever folder it is in โ€” marks it unread, both where it lies. Only the targets that will accept it light up, and a tab you choose by hand stays chosen when the next message in the thread arrives. - **A calendar beside the mail** โ€” two-way sync with Google, Microsoft and CalDAV, a drag-to-move week grid that pans rather than squeezes on a phone, invitations answered in place and landing in the calendar as you answer them, share and booking links. The time is always in view โ€” on the Happening Soon button, under the name, or at the foot of the sidebar with the next thing coming up. - **Files where you keep them** โ€” attach from and save to Drive, Photos, OneDrive, Dropbox, Nextcloud, Immich and Paperless-ngx. - **A language model, if you want one** โ€” point plMail at an Ollama box on your own network, or at any OpenAI-compatible endpoint, and it can search by meaning, draft replies, summarise a thread and sort mail into tabs. A long thread is summarised from as much of it as fits, and says so; ask for the whole conversation and it runs as a job with a context window to match. What it has been told about how you write is editable in the composer, for one attempt at a time. Off by default, each of the four switched on separately. Mail goes to the model host you name and nowhere else, so with a model on your own machine nothing leaves your network. - **You decide what sorts your mail** โ€” header rules that answer the same way every time, or the model reading each message; and on Gmail, whether either of them is allowed to disagree with Google. Change your mind and the mailbox re-sorts itself in the background. Every message's **Details** panel shows what all three said and which one won, and a **Report** button tells your administrator when it got it wrong. - **It tells you when something breaks** โ€” a sign-in that has expired, a calendar that stopped syncing, push that lapsed, work the queue gave up on. Each with what it means for your mail and the smallest thing that fixes it. A Google sign-in dying every week gets named for what it is โ€” Google expires tokens for an app still in Testing โ€” and warned about before the next one. - **And when there is something newer** โ€” follow releases or the main branch, and the administrators hear about each new build once: on their devices and at the top of the admin area, with what changed since the one they run. - **Yours** โ€” two-factor auth, sign-in through Authentik, Keycloak or any other OpenID Connect provider, per-app JMAP passwords, 41 themes, ten icons for the tab and the phone's home screen, English, German, French and Pirate. ## A look around | | | |---|---| | ![Reading a thread](docs/screenshots/thread.png) | ![Writing a message](docs/screenshots/compose.png) | | **Threads** โ€” replies collapse into one conversation. | **Compose** โ€” rich text, autocomplete, undo send. | | ![Dark mode](docs/screenshots/inbox-dark.png) | ![Calendar](docs/screenshots/calendar.png) | | **Themes** โ€” 41 of them, light and dark. | **Calendar** โ€” docked beside the mail, or its own page. | | ![Filters](docs/screenshots/filters.png) | ![Admin](docs/screenshots/admin.png) | | **Filters** โ€” counted before you save. | **Admin** โ€” workers, push health, logs, queue. | | ![Choosing an icon](docs/screenshots/icons.png) | ![Choosing its colours](docs/screenshots/colourways.png) | | **Icons** โ€” ten of them, a blue horn among them. | **Colourways** โ€” each icon in its own colours, or any of 32. | 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 ```bash git clone https://github.com/karatektus/pl_mail.git cd pl_mail docker compose up -d ``` Open [https://localhost](https://localhost) and create your account. From another machine, open `https://` and the server's address instead; the browser warns about the self-signed certificate once and lets you continue. ### TrueNAS plMail is in the TrueNAS app catalogue. **Apps โ†’ Discover Apps**, search for **plMail**, and install it. The form asks for one thing, a database password; everything else has a default that works. Reach it on port `30519`, and put a reverse proxy in front of it if it should be reachable from outside. Updates arrive through the catalogue like any other app's. ### 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](CONTRIBUTING.md) for the dev setup, the test suites and the console commands. ## License AGPL-3.0.