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
mydestinationis 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/postfixbelongs 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.