- 将数据库中的 expires_at 设为必填,并在认证时仅允许未过期的令牌通过 - 增加空过期时间的更新校验,避免写入非法时间值 - 补充过期令牌与空过期时间更新的测试覆盖 - 调整前端日期输入与提交逻辑,避免创建 API Token 时表单异常中断
LanQin Email
LanQin Email is a self-hosted full-stack webmail solution. The frontend is built with React + TypeScript + shadcn/ui, the backend uses Go + SQLite, and deployment can run as a single all-in-one container with API, Web, Nginx, Postfix, Dovecot, and Rspamd integrated.
Community: Telegram group
Features
- Webmail client: multiple mailbox switching, folders, reading and composing messages, drafts, scheduled sending, attachments, search, labels, stars, move/delete, read/unread status.
- Mailbox enhancements: contacts, signatures, inbox rules, sender blacklist, mail statistics, archive read messages, empty Trash/Spam.
- Multi-domain / multi-mailbox: domain management, DKIM key generation, DNS record display and checks, mailbox accounts, alias forwarding, catch-all toggle.
- Accounts and permissions: login/registration, session management, TOTP two-factor authentication, Cloudflare Turnstile, user self-service mailbox requests, permission groups/RBAC.
- Admin panel: overview checklist, user/permission group/domain/mailbox/alias/all-message management, system settings, mail templates, SMTP testing.
- Mail service stack: Postfix delivery, Dovecot IMAP/POP3, Rspamd anti-spam and DKIM signing, Maildir-to-SQLite sync.
- Deployment friendly: default all-in-one single container, plus a multi-container stack for debugging Postfix/Dovecot/Rspamd.
UI Preview
Repository Structure
.
├── apps/api # Go API, SQLite schema, mail sync, and business logic
├── apps/web # React/Vite Webmail and admin panel
├── deploy # Docker Compose, image build, Postfix/Dovecot/Rspamd config
└── .github/workflows # Docker image release workflows
Requirements
Development
- Go 1.25+
- Node.js 20+
- pnpm 10.28.2 (can be enabled through corepack)
Deployment
- Docker Engine
- Docker Compose v2
- A resolvable mail domain, plus available ports such as 25 / 465 / 587 / 993 / 995
Public email sending and receiving also requires correct MX, SPF, DKIM, and DMARC records, and you should confirm that your cloud provider does not block SMTP ports.
Quick Start
Local Development
Backend:
cd apps/api
go mod download
go test ./...
go run ./cmd/server
Frontend (new terminal):
cd apps/web
corepack enable
corepack prepare pnpm@10.28.2 --activate
pnpm install
pnpm run dev
Open:
- Web:
http://localhost:5173 - API:
http://localhost:8080
The default admin email is admin@lanqin.local. For development, explicitly set LANQIN_ADMIN_PASSWORD; if it is not set, the backend generates a random password on first startup and prints it to the logs.
Docker Deployment (single container)
A server only needs the Compose files and configuration under deploy/; building from source is not required:
cd deploy
cp .env.example .env
# Edit .env: domain, public URL, admin email, admin password, etc.
docker compose pull
docker compose up -d
Common commands:
# View logs
docker compose logs -f lanqin-email
# Pull the latest image and restart
docker compose pull
docker compose up -d
# Stop services
docker compose down
To build the image locally from the full source repository:
cd deploy
cp .env.example .env
docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build
See deploy/README.md for more deployment details.
First Deployment Checklist
- Edit
deploy/.env: at minimum, changeLANQIN_PUBLIC_HOSTNAME,LANQIN_PUBLIC_BASE_URL,LANQIN_ADMIN_EMAIL, andLANQIN_ADMIN_PASSWORD. - In production, mount real TLS certificates and set
LANQIN_TLS_CERT_FILE/LANQIN_TLS_KEY_FILE. - Log in to the admin panel and add your mail domain.
- Copy and configure MX, SPF, DKIM, and DMARC records from domain management, then run the DNS check.
- Create mailbox accounts, alias forwarding, or permission groups; enable registration, 2FA, Turnstile, and self-service mailbox requests as needed.
- Use the admin SMTP test and Webmail send/receive tests to confirm the full path works.
Key Environment Variables
See deploy/.env.example for the full configuration. Common variables:
| Variable | Description | Default / Example |
|---|---|---|
LANQIN_IMAGE |
All-in-one image | ghcr.io/lanqin996/lanqin-email:latest |
LANQIN_PUBLIC_HOSTNAME |
Mail server hostname; affects Postfix/DNS display/links | mail.example.com |
LANQIN_PUBLIC_BASE_URL |
Public Webmail URL | https://mail.example.com |
LANQIN_ADMIN_EMAIL |
Initial admin email | admin@example.com |
LANQIN_ADMIN_PASSWORD |
Initial admin password; must be changed in production | ChangeMe123! |
LANQIN_DB_PATH |
SQLite database path | /data/lanqin.db |
LANQIN_ALLOW_INSECURE_HTTP |
Allow non-HTTPS cookies; useful for local debugging | false |
LANQIN_OPEN_REGISTRATION |
Enable public registration | false |
LANQIN_TWO_FACTOR_ENABLED |
Global 2FA feature toggle | false |
LANQIN_TURNSTILE_ENABLED |
Enable Turnstile | false |
LANQIN_SMTP_HOST / LANQIN_SMTP_PORT |
Webmail outbound SMTP | 127.0.0.1 / 25 |
LANQIN_MAILDIR_ROOT |
Maildir root directory | /var/mail/vhosts |
LANQIN_CATCH_ALL_ENABLED |
Whether unregistered recipient addresses go into all messages | false |
LANQIN_USER_MAILBOX_APPLY_ENABLED |
Allow users to request mailboxes by themselves | false |
LANQIN_EXTERNAL_IMAP_ENABLED |
Enable external IMAP access; also configurable in Admin > System Settings > External IMAP | false |
LANQIN_EXTERNAL_IMAP_SECRET_KEY |
Encryption key for external IMAP passwords; required before enabling access; also configurable in admin | Random long string |
LANQIN_EXTERNAL_IMAP_SYNC_SECONDS |
Sync interval for external IMAP local-storage mode; also configurable in admin | 300 |
LANQIN_EXTERNAL_IMAP_ALLOW_PRIVATE_HOSTS |
Allow external IMAP to connect to private/localhost hosts; also configurable in admin | false |
LANQIN_EXTERNAL_IMAP_GMAIL_CLIENT_ID / LANQIN_EXTERNAL_IMAP_GMAIL_CLIENT_SECRET |
Gmail external IMAP OAuth2; callback is /api/external-imap-oauth/gmail/callback |
Empty |
LANQIN_EXTERNAL_IMAP_OUTLOOK_CLIENT_ID / LANQIN_EXTERNAL_IMAP_OUTLOOK_CLIENT_SECRET |
Microsoft 365 / Outlook external IMAP OAuth2; callback is /api/external-imap-oauth/outlook/callback |
Empty |
Architecture
┌────────────────────────────────────────────────────────────┐
│ lanqin-email single container │
│ │
│ ┌─────────┐ ┌────────────┐ ┌──────────────┐ │
│ │ Nginx │ ───▶ │ Go API │ ───▶ │ SQLite /data │ │
│ │ Web │ │ Webmail API│ └──────┬───────┘ │
│ │ static │ └─────┬──────┘ │ │
│ └─────────┘ │ Maildir sync │ maps │
│ ┌─────────┐ ┌─────▼──────┐ ┌──────▼───────┐ │
│ │ Rspamd │ ◀───▶ │ Postfix │ ───▶ │ Dovecot/LMTP │ │
│ │ DKIM/AS │ │ SMTP/MTA │ │ IMAP/POP3 │ │
│ └─────────┘ └────────────┘ └──────────────┘ │
└────────────────────────────────────────────────────────────┘
Mail flow:
- Receiving: Postfix receives mail → Rspamd scores/marks it → Dovecot writes to Maildir → API worker syncs it into SQLite → Webmail displays it.
- Sending: Webmail calls the API → API builds MIME → SMTP submits to Postfix or an external SMTP server → mail is delivered to the destination.
- Local delivery: In development, internal mailboxes can send directly into the recipient Inbox; if
LANQIN_SMTP_HOSTis not configured, external recipients are not actually delivered. - Third-party clients: Connect with SMTP 465/587, IMAP 993, or POP3 995; in production, configure certificates that match
LANQIN_PUBLIC_HOSTNAME. - External mailbox access: Users can add external IMAP accounts in personal mailbox management. Local-storage mode syncs mail into the database; remote-direct mode reads from the remote server each time and does not write into local mail tables.
Development and Verification
# API tests
cd apps/api
go test ./...
# Web checks and build
cd apps/web
pnpm run check
# Single-container source build verification
cd deploy
docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build
Production Notes
- In production, always change the default admin password and protect
.env, the SQLite database, Maildir, and DKIM private keys. - The Web UI can sit behind host Nginx / aaPanel / an edge gateway, but SMTP/IMAP/POP3 certificates must be mounted separately for Postfix/Dovecot inside the container.
- Cloud providers often block port 25 by default; if public email does not send or receive, first check ports, security groups, firewalls, and reverse DNS.
- SQLite is suitable for single-node deployments; before multi-node deployment, migrate the database and adjust Postfix/Dovecot query configuration accordingly.
SMTP Submission
- Third-party client SMTP submission on
465/587is handled by the LanQin API process. - Before enabling SMTP submission, configure
LANQIN_TLS_CERT_FILE/LANQIN_TLS_KEY_FILE; the API will not expose 465/587 externally with a localhost self-signed certificate. - Postfix only keeps port
25for public inbound mail and internal/external relay. - Webmail/API and third-party client sends are first written into Sent, then enter the send queue.
- The send queue is relayed by a LanQin API background worker to
LANQIN_SMTP_HOST:LANQIN_SMTP_PORT; failures are audited and retried with backoff. - v1 supports sending from the user's own mailbox. For send-as, use an enabled alias forwarding source that points to the user's mailbox, or configure
send_as_grantsin the database. - If the client later writes its own Sent copy through IMAP APPEND, Maildir sync deduplicates by
Message-IDwithin the Sent folder.
License
Star History
Friends: LINUX DO — a new ideal community



