All documentation
  1. Home
  2. Docs
  3. Mail

Mail

KLYRN sends mail and does not receive it. One relay is configured once, and then mail(), WordPress, Laravel and anything else that shells out to sendmail works on every site, each sending as its own domain.

What this is, and what it is not

This is the next release, not the one you can install today. Outbound site mail is not in 0.3.5‑beta.1. On the current build PHP's mail() returns false and logs nothing, because no mail transfer agent is installed at all. Everything on this page is written against the code that implements it and is accurate about what that code does; none of it is reachable until the next release ships. The page is here so you can decide whether to wait for it.

A contact form that silently fails is one of the oldest complaints in shared hosting, and on a KLYRN server today it fails silently for the most literal possible reason: there is nothing installed to send it. Turning outbound mail on fixes that for every site on the server in one action.

Outbound
Yes. Through a relay you own: your Google Workspace, Amazon SES, Postmark, or any SMTP server with a username and a password.
Inbound
No. Nothing listens. Pointing an MX record at a KLYRN server loses mail rather than moving it.
Mailboxes
No. No IMAP, no POP, no webmail, no quotas, no aliases to manage.
Per site
Each site sends as its own domain, is rate limited on its own, and keeps its own log.

This is a deliberate line, not an unfinished feature. A panel that hosts mailboxes owns spam filtering, storage growth, blocklist reputation, IMAP uptime and the support load of all four. Hosting a website and hosting somebody's email are two businesses. KLYRN does the first one properly and hands the second to a provider whose whole job it is. If your customers need mailboxes, point their MX at a mail host and keep their website here.

Turning it on

Two things will be needed: a relay to send through, and the switch. Both live in the panel under Settings. The relay is configured once for the server, and KLYRN carries presets for eleven providers so the host, port and encryption are filled in for you:

Gmail / Google Workspace
smtp.gmail.com, 587 STARTTLS, or 465 TLS
Microsoft 365 / Outlook
smtp.office365.com, 587 STARTTLS
Amazon SES
email-smtp.<region>.amazonaws.com, 587 STARTTLS, or 465 TLS
SendGrid
smtp.sendgrid.net, 587 STARTTLS, or 465 TLS
Mailgun
smtp.mailgun.org, 587 STARTTLS, or 465 TLS
Postmark
smtp.postmarkapp.com, 587 or 2525 STARTTLS
Brevo
smtp-relay.brevo.com, 587 STARTTLS, or 465 TLS
Zoho Mail
smtp.zoho.com, 587 STARTTLS, or 465 TLS
Fastmail
smtp.fastmail.com, 465 TLS, or 587 STARTTLS
Mailjet
in-v3.mailjet.com, 587 STARTTLS, or 465 TLS
SMTP2GO
mail.smtp2go.com, 587 STARTTLS, or 465 TLS
Other relay
Any host, any port, 587 STARTTLS by default

Switching it on installs Postfix, writes the configuration, and points every site's PHP pool at KLYRN's own wrapper. It is idempotent: every step asks what is already true first, so running it twice on a correct server changes nothing, writes nothing and reloads nothing.

What switching it on costs, stated exactly. Changing a site's PHP pool means its PHP-FPM master restarts, and this is true of any pool change rather than something mail introduces. The cost is one sub-second restart per account, not per site: switching mail on for two hundred sites across forty accounts costs forty restarts. Running it a second time costs nothing at all, measured as identical main PIDs and identical reload counts on every master.

What a site sends as

A site on shop.example sends as site@shop.example. The local part is one server-wide setting and defaults to site; the domain half, which is the half that decides whether SPF passes, always comes from the site itself.

site rather than noreply is deliberate. A contact form's reply goes to this address, and noreply is a word for an address nobody reads.

Under the covers, each PHP pool gets a sendmail_path pointing at KLYRN rather than at Postfix directly, which is what makes the per-site identity, the per-site rate limit and the per-site log possible at all:

php_admin_value[sendmail_path] = klyrn sendmail --site shop.example \
    --from site@shop.example --limit 100 --logdir /var/log/klyrn/mail -t -i

A site whose mail is not configured behaves exactly as it did before: the sendmail_path line is absent entirely rather than set to something that half works. There is no state where some messages leave and some do not, because that is harder to diagnose than mail that never sends.

Rate limits and message size

Default rate
100 messages per site per hour
Maximum message
10 MiB including attachments
Scope
Per site, so one busy shop cannot spend another site's budget

100 an hour is chosen as a generous ceiling for a legitimate site and a hard stop for a compromised one. A contact form sends single figures an hour; a busy shop's order confirmations, tens. A site that is taken over can emit 2,400 messages a day at this limit, which is enough to be noticed and not enough to get your server's address blocklisted before anybody looks at it.

The rate is a server-wide setting and can be raised or lowered. Setting it to zero stops that server sending without uninstalling anything.

SPF, DKIM and DMARC

Mail leaving your relay still has to be believed by whoever receives it. KLYRN checks the three records that decide that and tells you what it found, per sending domain:

SPF
Checked: that exactly one record exists, and that it authorises your relay. Two SPF records is itself a failure, and it is a common one.
DKIM
Checked, never generated. With a relay, the relay signs, which is why this is a question about your provider's setup rather than about KLYRN.
DMARC
Checked, and KLYRN reports what the record asks receivers to actually do with a failure.

Mail that is accepted by your relay has not been delivered. KLYRN says so rather than reporting success and leaving you to find out: until something has actually been delivered, check SPF, DKIM and DMARC for each sending domain before trusting the path. A relay refusing its own customer's mail is usually refusing the From domain.

The queue, and what is stuck

The panel shows the outbound queue broken down by what each message is waiting on, with the oldest arrival time and the total size on disk:

Active
Being delivered right now
Incoming
Accepted from a site, not yet picked up
Deferred
Tried, failed, will be retried. Grouped by the reason, because a hundred messages deferred for one reason is one problem
Hold
Held deliberately and will not move until released

A queue record KLYRN cannot parse is still counted in the total and left out of the breakdown, so the numbers never quietly shrink because a future Postfix changed a format.

Why nothing can reach this from outside

The Postfix KLYRN installs is a null client. The configuration is written by KLYRN and the reasons are in the file itself:

inet_interfaces = loopback-only   # no listener on any public interface
mydestination =                   # nothing is delivered on this machine
mynetworks = 127.0.0.0/8 [::1]/128  # only this machine may submit
relayhost = [smtp.example.net]:587  # required; never direct-to-MX
No public listener
inet_interfaces = loopback-only. There is no open relay to find, because there is no listener to reach.
No local delivery
mydestination is empty, so no address on this machine is a destination.
No direct-to-MX
A relay is required. A server that sends straight to recipients from a hosting IP is a server whose mail lands in spam.
Not a tenant's to change
/etc/postfix belongs to KLYRN. A site cannot rewrite its own sender or raise its own limit.

The relay password is stored sealed, is never returned by the API (the panel is told whether a password is set, never what it is), and is redacted from support bundles in every export shape KLYRN knows how to produce.

Turning it off

Disabling removes the sendmail_path from every pool and stops Postfix. The files KLYRN wrote are the files KLYRN owns, listed in one place in the source so that enable, disable and the status check cannot disagree about them:

/etc/postfix/main.cf
/etc/postfix/master.cf
/etc/postfix/sasl_passwd
/etc/postfix/sender_canonical

After disabling, mail() returns false again, which is the behaviour a server that never had this switched on has always had.