Mail System
CorePanel includes a complete email stack designed for reliability, security, and high deliverability. This document provides technical details about the mail system architecture and configuration.
Mail Stack Components
Section titled “Mail Stack Components”CorePanel’s mail system is built on industry-standard components:
| Component | Role |
|---|---|
| Postfix | SMTP server (sending/receiving mail) |
| Dovecot | IMAP/POP3 server and LMTP delivery |
| Rspamd | Spam filtering and DKIM signing |
| corepanel-auth | Authentication broker |
| Roundcube | Webmail, served at webmail.<domain> |
Architecture Overview
Section titled “Architecture Overview” ┌─────────────────┐ Incoming Mail │ │ ───────────────►│ Postfix │ │ (SMTP MTA) │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ Rspamd │ │ (filtering) │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ Dovecot LMTP │◄──── User Access (IMAP/POP3) │ (delivery) │ └────────┬────────┘ │ ┌────────▼────────┐ │ corepanel-auth │◄──── Authentication │ (broker) │ └────────┬────────┘ │ ┌────────▼────────┐ │ corepanel-core │◄──── User/Mailbox Database │ (SQLite) │ └─────────────────┘Email Account Management
Section titled “Email Account Management”Account Creation
Section titled “Account Creation”When a mailbox is created through the CorePanel interface or API, the following process occurs:
- corepanel-api receives the creation request
- corepanel-core validates the domain ownership and quota limits
- corepanel-core stores the mailbox metadata in SQLite
- corepanel-core syncs the mailbox to Postfix lookup database
- Dovecot creates the maildir structure on first delivery
# Mailbox directory structure/home/{account}/mail/{domain}/{localpart}/├── cur/ # Read messages├── new/ # Unread messages└── tmp/ # Temporary storage during deliveryMailbox Data Model
Section titled “Mailbox Data Model”Each mailbox stores the following information:
| Field | Description |
|---|---|
Localpart | Email local part (e.g., “user”) |
Username | Full email address (e.g., “user@example.com”) |
Password | bcrypt hash |
QuotaMB | Storage quota in megabytes |
LinuxUID/GID | OS-level user/group for mail delivery |
HomeDir | Account home directory |
Active | Enable/disable flag |
Quota Management
Section titled “Quota Management”Each mailbox has configurable storage quotas stored in the CorePanel database. Dovecot queries these quotas through the corepanel-auth service in real-time, ensuring:
- Accurate quota enforcement during delivery
- Instant quota updates without service restarts
- Per-mailbox and per-domain quota limits
The corepanel-auth Service
Section titled “The corepanel-auth Service”The corepanel-auth service is a critical component that acts as an authentication broker between mail services and the CorePanel database.
Why corepanel-auth?
Section titled “Why corepanel-auth?”Traditional mail setups require direct database queries from Postfix and Dovecot. This approach has several drawbacks:
- Database credentials exposed in multiple config files
- No centralized authentication logic
- Difficult to implement custom authentication rules
- Complex query configuration in mail services
corepanel-auth solves these problems by providing a unified authentication endpoint.
How It Works
Section titled “How It Works”┌──────────────┐ checkpassword protocol ┌─────────────────┐│ Dovecot │ ───────────────────────────►│ │└──────────────┘ │ corepanel-auth │ │ │┌──────────────┐ socketmap lookups │ (JSON-RPC) ││ Postfix │ ───────────────────────────►│ │└──────────────┘ └────────┬────────┘ │ ┌────────▼────────┐ │ corepanel-core │ │ (database) │ └─────────────────┘Authentication Flow
Section titled “Authentication Flow”- User attempts IMAP/SMTP login
- Dovecot calls the checkpassword helper binary
- Helper connects to
corepanel-authvia Unix socket corepanel-authqueriescorepanel-corevia JSON-RPC (core.AuthenticateMailUser)- Response returns UID, GID, home directory, maildir path, and quota
- Helper translates response into Dovecot environment variables
Postfix Integration
Section titled “Postfix Integration”Postfix uses socketmap lookups to query corepanel-auth for:
| Lookup | Purpose |
|---|---|
domains | Check if domain is configured for mail |
alias | Get destination for email aliases |
Socket Locations
Section titled “Socket Locations”/var/run/corepanel/corepanel-mail-auth.sockAuthentication Audit Trail
Section titled “Authentication Audit Trail”corepanel-auth is the only process on the server that sees every credential
check — IMAP, POP3, SMTP submission, FTP and FTPS — with the client’s address
attached. Every one of those checks is recorded in a dedicated SQLite database:
/var/lib/corepanel-auth/events.sqliteEach row holds the time, the service, the action, the username, the client and
server addresses, the outcome (ok, fail or error) and a reason such as
wrong_password or unknown_user. Mutations of the mail map arriving from
corepanel-core — a mailbox added, an alias deleted — are recorded too, so a
mailbox that suddenly stops receiving mail is a lookup rather than a mystery.
No password material is ever written. Not the value, not a hash, not a length. That is a rule of the design, not a default that could be changed.
A country column is filled in from the GeoLite2 database that corehttpd
downloads and keeps fresh at /var/lib/corehttpd/geoip/. corepanel-auth only
reads that file — it never downloads anything itself, which is why the broker
that sits in front of every credential on the box makes no outbound connections
at all. If the database is missing the country is simply left empty and
everything else keeps working.
Two limits keep the store bounded, and both are reported rather than applied silently:
| Limit | Default | What happens |
|---|---|---|
| Retention | 15 days | Older events are deleted every 5 minutes |
| Row cap | 500 000 | The oldest events are dropped first, so a sustained attack cannot fill the disk |
Auditing never delays or blocks a login. Writes are batched onto a background goroutine, and if that queue is ever full the event is dropped and counted — losing visibility is an acceptable failure, delaying every mail login on the server is not.
Reading it
Section titled “Reading it”In the panel it is Security → Login activity, with a per-mailbox Recent access block in each mailbox’s Security section and a failed-logins card on the dashboard — see Access Protection.
The same store is queryable from the command line on every edition:
# Everything that failed in the last hourcorepanel auth events --result fail --since 1h
# The addresses with the most failed logins this weekcorepanel auth attackers --since 168h
# When and from where did this mailbox last log in?corepanel auth user ana@example.comcorepanel auth attackers is the one to reach for during an attack. Its
ACCOUNTS column separates the two cases that look identical in a log file:
one account failing repeatedly from a single address is almost always a stale
password on somebody’s phone, while one address failing against many accounts is
a dictionary run. corepanel auth stats adds the health of the audit trail
itself — how many events are stored, how far back they go, and how many were
dropped because the writer fell behind.
The full command reference is in CLI Reference.
SPF (Sender Policy Framework)
Section titled “SPF (Sender Policy Framework)”SPF is a DNS-based email authentication method that specifies which mail servers are authorized to send email on behalf of your domain.
How SPF Works
Section titled “How SPF Works”- Sending server connects to receiving server
- Receiving server checks the sender’s domain SPF record
- SPF record lists authorized IP addresses/hostnames
- If sender’s IP matches, email passes SPF check
SPF Record Format
Section titled “SPF Record Format”A typical SPF record looks like:
v=spf1 a mx ip4:SERVER_IP ~all| Mechanism | Description |
|---|---|
v=spf1 | SPF version identifier |
a | Allow the domain’s A record IP |
mx | Allow the domain’s MX server IPs |
ip4: | Allow specific IPv4 addresses |
~all | Soft fail for unauthorized senders |
SPF in CorePanel
Section titled “SPF in CorePanel”CorePanel automatically creates SPF records when a domain is added. The default record is:
example.com. IN TXT "v=spf1 a mx ~all"This default configuration:
- Allows the domain’s A record IP to send mail
- Allows the domain’s MX servers to send mail
- Uses soft fail (
~all) for other sources
The system verifies SPF validity by:
- Performing DNS TXT lookup on the domain
- Searching for records starting with
v=spf1 - Displaying the status in the domain’s mail configuration
SPF Best Practices
Section titled “SPF Best Practices”- Use
~all(soft fail) during initial setup, switch to-all(hard fail) once verified - Keep records under 10 DNS lookups to avoid SPF permerror
- Include third-party senders (e.g.,
include:_spf.google.comfor Google Workspace)
DKIM (DomainKeys Identified Mail)
Section titled “DKIM (DomainKeys Identified Mail)”DKIM adds a cryptographic signature to outgoing emails, allowing receiving servers to verify that the message was sent by an authorized server and wasn’t modified in transit.
How DKIM Works
Section titled “How DKIM Works”- Key Generation: CorePanel generates a 2048-bit RSA key pair per domain
- DNS Publication: Public key is published as a TXT record
- Message Signing: Rspamd signs outgoing emails with the private key
- Verification: Receiving servers verify signatures using the public key
DKIM Key Management
Section titled “DKIM Key Management”CorePanel uses Rspamd for DKIM key generation and signing:
| Setting | Value |
|---|---|
| Selector | cp1 |
| Key size | 2048-bit RSA |
| Key location | /var/lib/rspamd/dkim/ |
DKIM Signing Process
Section titled “DKIM Signing Process”┌─────────────────┐│ Outgoing Email │└────────┬────────┘ │ ▼┌─────────────────┐ ┌──────────────────┐│ Postfix │────►│ Rspamd ││ │ │ (milter) │└─────────────────┘ └────────┬─────────┘ │ ┌────────▼─────────┐ │ Private Key │ │ (per domain) │ └────────┬─────────┘ │ ┌────────▼─────────┐ │ DKIM-Signature │ │ header added │ └──────────────────┘DKIM DNS Record
Section titled “DKIM DNS Record”CorePanel generates DKIM public keys with the selector cp1:
cp1._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqh..."| Tag | Description |
|---|---|
v=DKIM1 | DKIM version |
k=rsa | Key type (RSA) |
p= | Base64-encoded public key |
Setting Up DKIM
Section titled “Setting Up DKIM”CorePanel handles DKIM setup automatically:
- Key Generation: When a domain is added, CorePanel generates the DKIM key pair
- DNS Publication: The public key is automatically published to the DNS zone at
cp1._domainkey.yourdomain.com - Signing Activation: Rspamd begins signing outgoing mail immediately
No manual DNS configuration is required since CorePanel manages the DNS zones.
Verifying DKIM Setup
Section titled “Verifying DKIM Setup”Send a test email to a Gmail address and check the headers:
Authentication-Results: mx.google.com; dkim=pass header.d=example.com header.s=cp1; spf=pass smtp.mailfrom=user@example.comDKIM Key Rotation
Section titled “DKIM Key Rotation”CorePanel supports DKIM key rotation with incremental selectors to maintain email deliverability during the transition period.
Why Rotate DKIM Keys?
Section titled “Why Rotate DKIM Keys?”- Security: Periodic key rotation limits the impact of potential key compromise
- Compliance: Some security policies require regular cryptographic key rotation
- Key Size Updates: Upgrade from older 1024-bit keys to 2048-bit keys
How Key Rotation Works
Section titled “How Key Rotation Works”When you rotate a DKIM key through the CorePanel interface:
- New Selector Generated: A new selector is created incrementally (cp1 → cp2 → cp3…)
- Key Pair Created: A new 2048-bit RSA key pair is generated
- Selector Maps Updated: Rspamd is configured to use the new selector for signing
- DNS Record Added: If DNS is managed by CorePanel, the new DKIM record is automatically added
- Grace Period: The old key remains valid for 7 days
Selector Mapping
Section titled “Selector Mapping”CorePanel uses rspamd’s selector_map and path_map configuration to support different selectors per domain:
/var/lib/rspamd/dkim/selectors.map # Maps domains to selectors/var/lib/rspamd/dkim/paths.map # Maps domains to key file pathsExample content:
example.com cp2other.com cp1
# paths.mapexample.com /var/lib/rspamd/dkim/example.com.cp2.keyother.com /var/lib/rspamd/dkim/other.com.cp1.keyKey File Naming
Section titled “Key File Naming”DKIM keys are stored with the selector in the filename:
/var/lib/rspamd/dkim/├── example.com.cp1.key # Old key (expired)├── example.com.cp1.txt # Old public key record├── example.com.cp2.key # Current active key└── example.com.cp2.txt # Current public key recordGrace Period Cleanup
Section titled “Grace Period Cleanup”A daily job (4:00 AM server time) automatically cleans up expired selectors:
- Removes key files from
/var/lib/rspamd/dkim/ - Deletes the DNS record (if DNS is managed)
- Removes the selector from the database
Rotating keys
Section titled “Rotating keys”DKIM key rotation is triggered from the panel (the domain’s mail
configuration). There is no corepanel CLI subcommand for DKIM. Programmatically,
rotation is the core.RotateDKIMKey JSON-RPC method on corepanel-core, and the
active selectors for a domain are available through sys.ListDKIMSelectors on
corepanel-sys — the same methods the panel uses. From a shell you can still
inspect the result directly with the selector maps and dig (see below).
Troubleshooting Key Rotation
Section titled “Troubleshooting Key Rotation”Check active selector:
cat /var/lib/rspamd/dkim/selectors.map | grep example.comVerify new DKIM record:
dig +short TXT cp2._domainkey.example.comCheck rspamd configuration:
cat /etc/rspamd/local.d/dkim_signing.confView rspamd logs for signing issues:
journalctl -u rspamd | grep -i dkimMail Mode Detection
Section titled “Mail Mode Detection”CorePanel automatically detects the mail capabilities for each domain:
| Mode | Description | Requirements |
|---|---|---|
| None | No mail capability | - |
| Inbound | Can receive mail | MX points to server |
| Outbound | Can send mail | DKIM valid or DNS managed |
| Full | Send and receive | MX + DKIM configured |
The system performs the following DNS checks:
- MX Records: Verifies MX points to server hostname or IP
- DKIM Record: Validates
cp1._domainkeyTXT record matches expected key - SPF Record: Detects presence of
v=spf1record
Server Identity (HELO)
Section titled “Server Identity (HELO)”Postfix announces a name in every SMTP conversation it opens — the HELO/EHLO
greeting, also stamped into Received headers. Receiving mail servers weigh it
heavily: a greeting that is not a fully qualified name, or that resolves nowhere,
is one of the cheapest reasons to reject a message or score it as spam.
CorePanel sets it to the panel domain, and keeps it there:
- At install time it is seeded from the machine’s own hostname.
- When you set or change the panel domain,
myhostnameis updated to match and Postfix is reloaded. The panel domain is used because it is, by construction, a name that resolves to this server.
This matters most on a server provisioned from a cloud or marketplace image, where
the machine boots as something like almalinux-2gb-nyc1-01 — not a fully qualified
name, resolving nowhere. Until you set the panel domain, that is what the server
announces.
Check the current value with:
postconf -h myhostnameTLS Certificate Management
Section titled “TLS Certificate Management”The default (host) certificate
Section titled “The default (host) certificate”Both Postfix and Dovecot fall back to the same certificate when a connection
arrives with no SNI name, or with a name no domain on this server holds: a fixed
host-certificate path (/var/lib/corehttpd/hostcert/). CoreHttpd obtains the certificate, because
issuance belongs to whatever terminates TLS on port 443; corepanel-sys owns that
path and keeps it up to date, because the mail services are its to reload. Its
lifecycle:
- Self-signed, written as soon as the panel has a domain, so TLS works immediately.
- Real certificate, promoted automatically onto the same path once the panel domain resolves to the server and the certificate is issued. Postfix and Dovecot are reloaded on the change.
The promotion happens within seconds of issuance: corepanel-sys watches the
directory CoreHttpd files certificates in, so the certificate obtained during your
first HTTPS visit to the panel is the one mail is already serving by the time you
open a mail client. A periodic check every five minutes covers anything the watch
misses, so the worst case is a few minutes rather than a stall.
While the panel has no domain at all, no host certificate is issued and mail falls back to the system’s self-signed certificate. Mail still flows — TLS is opportunistic — but clients will warn about the certificate until you set the panel domain.
The host certificate is also filled in from the certificates the server already
holds. If nobody has ever reached the panel over HTTPS under its own hostname —
an admin who works from the server’s IP, or from an old URL after a migration —
CoreHttpd never had a handshake to issue on, and the path would keep the
self-signed bootstrap indefinitely. When one of the domain certificates in
/var/lib/cp-sys/ssl/certs/ covers the hostname (a *.example.com wildcard
covers server.example.com), corepanel-sys promotes that one instead. A
certificate obtained for the hostname itself always wins over one that merely
covers it.
Per-domain certificates over SNI
Section titled “Per-domain certificates over SNI”Both daemons present the right certificate per domain, selected by the SNI name
the client sends, so a mail client configured for mail.example.com is handed
example.com’s certificate and not the server’s own.
- Certificates are scanned from
/var/lib/cp-sys/ssl/certs/. - The names come from the certificate, not from the directory it sits in. A
wildcard
*.example.comis expanded into the names clients actually ask for —mail.,webmail.,imap.,smtp.,pop.,pop3.— because neither daemon matches wildcards in an SNI table. A name stated literally in a certificate wins over the same name derived from a wildcard. - Dovecot reads a
local_nameblock per name from/etc/dovecot/conf.d/99-corepanel-ssl.conf. - Postfix reads
tls_server_sni_maps, a table at/etc/postfix/corepanel_sni.mapcompiled withpostmap -F. Postfix does not read the PEM files at handshake time — the compiled.dbembeds the certificates and their keys, which is why it is owned by Postfix’smail_ownerand mode0640, and why it is rebuilt on every renewal and not only when a domain is added. - Both files are generated from the same scan, so the two daemons always publish the same set of names. Both are regenerated whenever a certificate is issued, renewed or removed, and the services reloaded.
Troubleshooting
Section titled “Troubleshooting”Common Issues
Section titled “Common Issues”Mail not sending/receiving:
# Check service statussystemctl status postfix dovecot rspamd corepanel-auth
# Check mail logstail -f /var/log/maillogAuthentication failures:
# Check auth socket exists and has correct permissionsls -la /var/run/corepanel/corepanel-mail-auth.sock
# Check corepanel-auth logsjournalctl -u corepanel-auth -fSPF/DKIM failures:
# Test SPF recorddig +short TXT example.com | grep spf
# Test DKIM recorddig +short TXT cp1._domainkey.example.comMail Queue Management
Section titled “Mail Queue Management”# View mail queuepostqueue -p
# Flush the queuepostqueue -f
# Delete all queued mail (use with caution)postsuper -d ALL