Skip to content

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.

CorePanel’s mail system is built on industry-standard components:

ComponentRole
PostfixSMTP server (sending/receiving mail)
DovecotIMAP/POP3 server and LMTP delivery
RspamdSpam filtering and DKIM signing
corepanel-authAuthentication broker
RoundcubeWebmail, served at webmail.<domain>
┌─────────────────┐
Incoming Mail │ │
───────────────►│ Postfix │
│ (SMTP MTA) │
└────────┬────────┘
┌─────────────────┐
│ Rspamd │
│ (filtering) │
└────────┬────────┘
┌─────────────────┐
│ Dovecot LMTP │◄──── User Access (IMAP/POP3)
│ (delivery) │
└────────┬────────┘
┌────────▼────────┐
│ corepanel-auth │◄──── Authentication
│ (broker) │
└────────┬────────┘
┌────────▼────────┐
│ corepanel-core │◄──── User/Mailbox Database
│ (SQLite) │
└─────────────────┘

When a mailbox is created through the CorePanel interface or API, the following process occurs:

  1. corepanel-api receives the creation request
  2. corepanel-core validates the domain ownership and quota limits
  3. corepanel-core stores the mailbox metadata in SQLite
  4. corepanel-core syncs the mailbox to Postfix lookup database
  5. Dovecot creates the maildir structure on first delivery
Terminal window
# Mailbox directory structure
/home/{account}/mail/{domain}/{localpart}/
├── cur/ # Read messages
├── new/ # Unread messages
└── tmp/ # Temporary storage during delivery

Each mailbox stores the following information:

FieldDescription
LocalpartEmail local part (e.g., “user”)
UsernameFull email address (e.g., “user@example.com”)
Passwordbcrypt hash
QuotaMBStorage quota in megabytes
LinuxUID/GIDOS-level user/group for mail delivery
HomeDirAccount home directory
ActiveEnable/disable flag

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 is a critical component that acts as an authentication broker between mail services and the CorePanel database.

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.

┌──────────────┐ checkpassword protocol ┌─────────────────┐
│ Dovecot │ ───────────────────────────►│ │
└──────────────┘ │ corepanel-auth │
│ │
┌──────────────┐ socketmap lookups │ (JSON-RPC) │
│ Postfix │ ───────────────────────────►│ │
└──────────────┘ └────────┬────────┘
┌────────▼────────┐
│ corepanel-core │
│ (database) │
└─────────────────┘
  1. User attempts IMAP/SMTP login
  2. Dovecot calls the checkpassword helper binary
  3. Helper connects to corepanel-auth via Unix socket
  4. corepanel-auth queries corepanel-core via JSON-RPC (core.AuthenticateMailUser)
  5. Response returns UID, GID, home directory, maildir path, and quota
  6. Helper translates response into Dovecot environment variables

Postfix uses socketmap lookups to query corepanel-auth for:

LookupPurpose
domainsCheck if domain is configured for mail
aliasGet destination for email aliases
/var/run/corepanel/corepanel-mail-auth.sock

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.sqlite

Each 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:

LimitDefaultWhat happens
Retention15 daysOlder events are deleted every 5 minutes
Row cap500 000The 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.

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:

Terminal window
# Everything that failed in the last hour
corepanel auth events --result fail --since 1h
# The addresses with the most failed logins this week
corepanel auth attackers --since 168h
# When and from where did this mailbox last log in?
corepanel auth user ana@example.com

corepanel 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 is a DNS-based email authentication method that specifies which mail servers are authorized to send email on behalf of your domain.

  1. Sending server connects to receiving server
  2. Receiving server checks the sender’s domain SPF record
  3. SPF record lists authorized IP addresses/hostnames
  4. If sender’s IP matches, email passes SPF check

A typical SPF record looks like:

v=spf1 a mx ip4:SERVER_IP ~all
MechanismDescription
v=spf1SPF version identifier
aAllow the domain’s A record IP
mxAllow the domain’s MX server IPs
ip4:Allow specific IPv4 addresses
~allSoft fail for unauthorized senders

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:

  1. Performing DNS TXT lookup on the domain
  2. Searching for records starting with v=spf1
  3. Displaying the status in the domain’s mail configuration
  • 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.com for Google Workspace)

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.

  1. Key Generation: CorePanel generates a 2048-bit RSA key pair per domain
  2. DNS Publication: Public key is published as a TXT record
  3. Message Signing: Rspamd signs outgoing emails with the private key
  4. Verification: Receiving servers verify signatures using the public key

CorePanel uses Rspamd for DKIM key generation and signing:

SettingValue
Selectorcp1
Key size2048-bit RSA
Key location/var/lib/rspamd/dkim/
┌─────────────────┐
│ Outgoing Email │
└────────┬────────┘
┌─────────────────┐ ┌──────────────────┐
│ Postfix │────►│ Rspamd │
│ │ │ (milter) │
└─────────────────┘ └────────┬─────────┘
┌────────▼─────────┐
│ Private Key │
│ (per domain) │
└────────┬─────────┘
┌────────▼─────────┐
│ DKIM-Signature │
│ header added │
└──────────────────┘

CorePanel generates DKIM public keys with the selector cp1:

cp1._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqh..."
TagDescription
v=DKIM1DKIM version
k=rsaKey type (RSA)
p=Base64-encoded public key

CorePanel handles DKIM setup automatically:

  1. Key Generation: When a domain is added, CorePanel generates the DKIM key pair
  2. DNS Publication: The public key is automatically published to the DNS zone at cp1._domainkey.yourdomain.com
  3. Signing Activation: Rspamd begins signing outgoing mail immediately

No manual DNS configuration is required since CorePanel manages the DNS zones.

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.com

CorePanel supports DKIM key rotation with incremental selectors to maintain email deliverability during the transition period.

  • 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

When you rotate a DKIM key through the CorePanel interface:

  1. New Selector Generated: A new selector is created incrementally (cp1 → cp2 → cp3…)
  2. Key Pair Created: A new 2048-bit RSA key pair is generated
  3. Selector Maps Updated: Rspamd is configured to use the new selector for signing
  4. DNS Record Added: If DNS is managed by CorePanel, the new DKIM record is automatically added
  5. Grace Period: The old key remains valid for 7 days

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 paths

Example content:

selectors.map
example.com cp2
other.com cp1
# paths.map
example.com /var/lib/rspamd/dkim/example.com.cp2.key
other.com /var/lib/rspamd/dkim/other.com.cp1.key

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 record

A daily job (4:00 AM server time) automatically cleans up expired selectors:

  1. Removes key files from /var/lib/rspamd/dkim/
  2. Deletes the DNS record (if DNS is managed)
  3. Removes the selector from the database

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).

Check active selector:

Terminal window
cat /var/lib/rspamd/dkim/selectors.map | grep example.com

Verify new DKIM record:

Terminal window
dig +short TXT cp2._domainkey.example.com

Check rspamd configuration:

Terminal window
cat /etc/rspamd/local.d/dkim_signing.conf

View rspamd logs for signing issues:

Terminal window
journalctl -u rspamd | grep -i dkim

CorePanel automatically detects the mail capabilities for each domain:

ModeDescriptionRequirements
NoneNo mail capability-
InboundCan receive mailMX points to server
OutboundCan send mailDKIM valid or DNS managed
FullSend and receiveMX + DKIM configured

The system performs the following DNS checks:

  • MX Records: Verifies MX points to server hostname or IP
  • DKIM Record: Validates cp1._domainkey TXT record matches expected key
  • SPF Record: Detects presence of v=spf1 record

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, myhostname is 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:

Terminal window
postconf -h myhostname

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:

  1. Self-signed, written as soon as the panel has a domain, so TLS works immediately.
  2. 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.

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.com is 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_name block per name from /etc/dovecot/conf.d/99-corepanel-ssl.conf.
  • Postfix reads tls_server_sni_maps, a table at /etc/postfix/corepanel_sni.map compiled with postmap -F. Postfix does not read the PEM files at handshake time — the compiled .db embeds the certificates and their keys, which is why it is owned by Postfix’s mail_owner and mode 0640, 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.

Mail not sending/receiving:

Terminal window
# Check service status
systemctl status postfix dovecot rspamd corepanel-auth
# Check mail logs
tail -f /var/log/maillog

Authentication failures:

Terminal window
# Check auth socket exists and has correct permissions
ls -la /var/run/corepanel/corepanel-mail-auth.sock
# Check corepanel-auth logs
journalctl -u corepanel-auth -f

SPF/DKIM failures:

Terminal window
# Test SPF record
dig +short TXT example.com | grep spf
# Test DKIM record
dig +short TXT cp1._domainkey.example.com
Terminal window
# View mail queue
postqueue -p
# Flush the queue
postqueue -f
# Delete all queued mail (use with caution)
postsuper -d ALL