Skip to content

Latest commit

ย 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

RayMail logo

Self-hosted mail server, webmail client and delivery telemetry โ€” in one docker compose up

CI License: MIT Version Stalwart Next.js

Stars Forks Issues Docker TypeScript


Own your mail server end to end



Quick start ยท Features ยท Architecture ยท Configuration ยท Deliverability ยท Contributing ยท Discussions


What is RayMail?

RayMail is a complete, open-source, self-hosted email stack you run on your own VPS. It replaces the usual pile of Postfix + Dovecot + Roundcube + a tracking SaaS with one Docker Compose file:

  • A real mail server โ€” RFC-compliant SMTP, IMAP and JMAP from Stalwart, with automatic DKIM key generation.
  • A modern webmail client โ€” a fast, Gmail-style three-pane workspace built in Next.js 15 and TypeScript.
  • Delivery telemetry that is actually yours โ€” open and click tracking, a full queued โ†’ sent โ†’ delivered โ†’ opened โ†’ clicked pipeline, and a document vault, all stored locally in SQLite. No third-party tracking pixel, no data leaving your server.
  • An optional AI assistant โ€” draft replies, summarise long threads and brief your inbox, with aggressive token budgeting so it stays cheap.

Desktop and mobile clients (Outlook, Apple Mail, Thunderbird, K-9, Gmail app) connect over standard IMAP/SMTP. The webmail speaks JMAP over the internal Docker network, so mail credentials never reach the browser.

RayMail webmail inbox โ€” Gmail-style three-pane workspace with search, folder rail and reading pane

โšก Quick start

One command

curl -fsSL https://raw.githubusercontent.com/DeveloperSarim/raymail/main/install.sh | bash

The installer audits your ports, warns you if your provider blocks outbound :25, generates secrets, fixes container volume ownership, starts the stack, completes the mail-server setup handshake and prints the exact DNS records you need.

Manual

Step by step (click to expand)
# 1. clone
git clone https://github.com/DeveloperSarim/raymail.git
cd raymail

# 2. configure
cp .env.example .env
$EDITOR .env          # set MAIL_DOMAIN, MAIL_HOSTNAME, APP_URL

# 3. prepare volumes
#    both containers run unprivileged - root-owned bind mounts are the
#    single most common first-boot failure
mkdir -p stalwart/etc stalwart/data/logs data
sudo chown -R 2000:2000 stalwart/etc stalwart/data   # stalwart runs as uid 2000
sudo chown -R 1001:1001 data                         # web runs as uid 1001

# 4. start
docker compose up -d

# 5. finish setup, then print your DNS records
./deploy/dns-records.py

# 6. TLS + reverse proxy
sudo ./deploy/setup-tls.sh

# 7. health check
./deploy/verify.sh

Requirements

Minimum
OS Any Linux with Docker (Ubuntu 22.04+ tested), or macOS for local development
RAM 1 GB (2 GB comfortable)
Disk 5 GB + your mail
Ports 25, 465, 587, 993 free, plus two loopback ports
DNS A domain you control

โœจ Features

๐Ÿ“ฌ Mail engine

  • Inbound SMTP on :25
  • Submission on :587 (STARTTLS) and :465 (implicit TLS)
  • IMAP over TLS on :993
  • JMAP + WebSocket for the webmail
  • ManageSieve on :4190
  • Automatic DKIM (RSA + Ed25519)
  • SPF and DMARC record generation
  • Smarthost relay for hosts that block :25

๐Ÿ“Š Telemetry

  • 1ร—1 zero-cache open pixel
  • Signed click redirector โ€” no open-redirect
  • Full delivery pipeline, reconciled against the MTA
  • Open/click rates, bounce tracking
  • Per-message audit trail with IP and user agent
  • Document vault indexing every attachment
  • All of it in local SQLite

๐Ÿ’Œ Webmail

  • Gmail-style three-pane workspace
  • Search, star, archive, delete
  • Sandboxed message reader โ€” no CSS bleed, no XSS
  • Remote images blocked until you ask
  • Attachment previews (images, PDF, text)
  • Composer with drag-and-drop and CSS inlining
  • โŒ˜/Ctrl + Enter to send
  • Responsive down to a phone

๐Ÿค– AI assistant (optional)

  • Draft a new email from a one-line prompt
  • Draft a reply from the thread
  • Per-message summary with extracted actions
  • Whole-inbox overview
  • Four tones: professional, friendly, direct, apologetic
  • Token-budgeted โ€” see the table below
  • Disabled cleanly when no API key is set
RayMail admin console โ€” mailbox management, DKIM records, listeners and outbound routes The built-in mail server console โ€” create mailboxes, rotate passwords, copy DNS records, inspect listeners and the outbound queue.

๐Ÿ— Architecture

flowchart LR
    MX[Sending MX]
    C[Outlook / Apple Mail / Thunderbird]
    B[Browser]
    P[Reverse proxy 443]
    W[RayMail web - Next.js]
    SW[Stalwart - 25 465 587 993]
    DB[(SQLite telemetry)]
    R[Smarthost relay 587]

    MX -->|inbound SMTP| SW
    C -->|IMAP and SMTP| SW
    B --> P
    P --> W
    W <-->|JMAP| SW
    W --> DB
    SW -->|outbound| R
    R --> MX
Loading

Delivery pipeline โ€” a message only moves forward; bounced is terminal from anywhere.

flowchart LR
    Q[queued] --> S[sent]
    S --> D[delivered]
    D --> O[opened]
    O --> C[clicked]
    Q --> X[bounced]
    S --> X
    D --> X
Loading

Port map

Port Bind Purpose Exposed
25 0.0.0.0 Inbound MX ๐ŸŒ Public
465 0.0.0.0 SMTP submission, implicit TLS ๐ŸŒ Public
587 0.0.0.0 SMTP submission, STARTTLS ๐ŸŒ Public
993 0.0.0.0 IMAP, implicit TLS ๐ŸŒ Public
3880 127.0.0.1 Webmail + telemetry API ๐Ÿ”’ Proxy only
3881 127.0.0.1 Mail server admin + JMAP ๐Ÿ”’ Loopback only

RayMail never binds :80 or :443 โ€” those stay with whatever web server you already run.

Project layout

raymail/
โ”œโ”€โ”€ docker-compose.yml        # stalwart + web
โ”œโ”€โ”€ install.sh                # one-command installer
โ”œโ”€โ”€ deploy/
โ”‚   โ”œโ”€โ”€ setup-tls.sh          # certbot + reverse-proxy vhost
โ”‚   โ”œโ”€โ”€ configure-relay.py    # smarthost for blocked :25
โ”‚   โ”œโ”€โ”€ dns-records.py        # prints records incl. live DKIM
โ”‚   โ””โ”€โ”€ verify.sh             # read-only health check
โ””โ”€โ”€ web/src/
    โ”œโ”€โ”€ app/                  # routes: pages at /, API under /api
    โ”œโ”€โ”€ components/           # presentational UI
    โ”œโ”€โ”€ hooks/                # TanStack Query bindings
    โ”œโ”€โ”€ lib/                  # db, telemetry tokens, session crypto
    โ”œโ”€โ”€ services/             # JMAP + Stalwart admin + DeepSeek clients
    โ””โ”€โ”€ types/                # domain models

โš™๏ธ Configuration

Everything lives in .env.

Variable Required Purpose
MAIL_DOMAIN โœ… Domain RayMail handles mail for
MAIL_HOSTNAME โœ… Public hostname, used in SMTP greetings
APP_URL โœ… Public URL, used for tracking links
TELEMETRY_SECRET โœ… Signs tracking tokens and encrypts sessions
STALWART_ADMIN_USER / _PASSWORD โœ… Administrator mailbox
RELAY_HOST / _PORT / _USERNAME / _PASSWORD โš ๏ธ Smarthost โ€” required when :25 is blocked
DEEPSEEK_API_KEY โž– Enables the AI assistant
DEEPSEEK_MODEL โž– Defaults to deepseek-chat
Desktop client settings
Incoming   IMAP    mail.example.com   993   SSL/TLS
Outgoing   SMTP    mail.example.com   587   STARTTLS
Username   the full address, you@mail.example.com
Password   your mailbox password
Auth       normal password, required for outgoing

Works with Microsoft Outlook, Apple Mail, Thunderbird, K-9 Mail and the Gmail app.

AI token budgeting

The cost control is in what is not sent to the model:

Lever Effect
HTML stripped to text Drops markup, styles and tracking pixels before the model sees anything
Quoted history removed A reply chain repeats the thread on every message; it is paid for once
Character budget Bodies capped at ~6k characters, biased to the head where the ask lives
Overview uses envelopes only Sender + subject + preview โ€” hundreds of tokens instead of tens of thousands
Results cached on a content hash Re-opening a message costs nothing; only changed mail is re-summarised
max_tokens per task Every task has a natural length and is capped to it

Actual spend is shown in the admin dashboard, split into tokens in, tokens out, and results served from cache.


๐Ÿ“ฎ Deliverability

Self-hosted mail lands in spam for a small number of fixable reasons. In order of impact:

  1. PTR mismatch โ€” forward and reverse DNS must agree. Set the reverse record for your IP to your mail hostname.
  2. Missing or misaligned DKIM/SPF/DMARC โ€” ./deploy/dns-records.py prints the exact records, including your live DKIM public keys.
  3. Blocked outbound :25 โ€” many providers block it. RayMail then relays through a smarthost on :587; inbound :25 is unaffected.
  4. DMARC alignment โ€” if you relay, your Return-Path is usually a subdomain. Use relaxed alignment (adkim=r; aspf=r) or every relayed message fails.
  5. A brand-new domain has no reputation. Start with p=none, send slowly, and tighten to p=quarantine once reports come back clean.

Two questions come up more than any other, both answered in detail in Discussions: mail still goes to spam with DKIM/SPF/DMARC set ยท first boot fails with "Permission denied"

Troubleshooting matrix
Symptom Likely cause Check
Outbound mail stuck in queue :25 egress blocked, no relay set RELAY_HOST in .env
Container exits on first boot Root-owned bind mounts chown -R 2000:2000 stalwart/
certbot fails A record missing or not propagated dig +short A mail.example.com
Outlook rejects the password Using the local part, not the full address Log in as you@mail.example.com
465/993 silent, no handshake No certificate installed yet Run ./deploy/setup-tls.sh
TLS warning in a mail client Server started before the cert existed docker restart raymail-stalwart
Opens never register Recipient blocks remote images Expected โ€” clicks still track
Mail goes to spam See the five points above Gmail โ†’ Show original
Reverse proxy won't reload Vhost syntax apache2ctl configtest

๐Ÿงช Development

cd web
npm install
npm run dev        # http://localhost:3000
npm run typecheck  # tsc --noEmit, strict mode
npm test           # token forgery + open-redirect guards

The test suite runs on Node's built-in runner with no framework. It covers the security boundary that matters most: tracking tokens are HMAC-signed, so opens and clicks cannot be forged and the click redirector cannot be repointed at another host.


๐Ÿค Contributing

Contributions are welcome โ€” issues, features and documentation alike.

  1. Fork the repository and create a branch: git checkout -b feature/my-change
  2. Keep TypeScript strict โ€” npm run typecheck must pass
  3. Add a test when you touch security or money paths
  4. Commit with a clear message and open a pull request
Good first issues
  • Server-side JMAP search (the list currently filters client-side)
  • Bounce ingestion from the Stalwart queue into the telemetry pipeline
  • Multi-account support in the webmail
  • A nginx and a Caddy variant of deploy/setup-tls.sh
  • Thread grouping in the message list

Contributors Pull requests welcome Last commit

๐Ÿ“„ License

Released under the MIT License. Use it, fork it, ship it.

๐Ÿ™ Built on

Stalwart Mail Server ยท Next.js ยท Tailwind CSS ยท TanStack Query ยท Zustand ยท Lucide


Built and maintained by DeveloperSarim

Keywords โ€” self-hosted email server ยท open source webmail ยท docker mail server ยท email tracking ยท open and click tracking ยท SMTP IMAP JMAP server ยท DKIM SPF DMARC ยท Stalwart mail ยท Next.js webmail client ยท privacy-first email ยท self-hosted Gmail alternative

About

Self-hosted mail server, webmail client and email tracking in one Docker Compose stack. SMTP, IMAP, JMAP, DKIM, SPF, DMARC, open and click telemetry, and an optional AI assistant. An open-source, privacy-first Gmail alternative you own end to end.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages