A server that does not host mail can still send it. Configure one relay, and mail(), WordPress, Laravel and anything else that calls sendmail works on every site.
What this is, and what it is not
A KLYRN server can host mail: mailboxes, forwarders, catch-all, DKIM, Rspamd spam
filtering into Junk, and webmail at /webmail on every site. A site's
mail() then goes through that local mail server and is DKIM-signed on the
way out.
On a server that does not host mail, every site can send through one relay you own.
This page is about the relay. With neither, a contact form that calls
mail() has nothing to hand its message to.
- Outbound
- Through a relay you own: your Google Workspace, Amazon SES, Postmark, or any SMTP server with a username and a password.
- Per site
- Each site sends as its own domain, is rate limited on its own, and keeps its own log.
Turning it on
You need a relay to send through, and the switch. Both are in the panel under Settings. The relay is configured once for the server. Presets for eleven providers fill in the host, port and encryption:
- 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. Running it twice on a correct server changes nothing, writes nothing and reloads nothing.
What switching it on costs. Changing a site's PHP pool restarts its PHP-FPM master, as any pool change does. That is one sub-second restart per account. Two hundred sites across forty accounts cost forty restarts. A second run costs none: main PIDs and reload counts were measured identical 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 always
comes from the site itself, and the domain decides whether SPF passes.
The default is site because a contact form's reply goes to this address,
and nobody reads noreply.
Each PHP pool gets a sendmail_path that points at KLYRN. That is what
gives each site its own identity, rate limit and log:
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 has no sendmail_path line at all and
behaves as it did before. No state exists where some messages leave and some do not.
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
A contact form sends single figures an hour, and a busy shop's order confirmations send tens. At this limit a compromised site can emit 2,400 messages a day. That is enough to be noticed and too few to get your server's address blocklisted first.
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
Receivers decide whether to believe your mail from three records. KLYRN checks them for each sending domain and reports what it found:
- SPF
- Checked: that exactly one record exists, and that it authorises your relay. Two SPF records is a failure, and a common one.
- DKIM
- Checked, never generated. With a relay, the relay signs, so DKIM is set up at your provider.
- DMARC
- Checked, and KLYRN reports what the record asks receivers to do with a failure.
Mail that is accepted by your relay has not been delivered. KLYRN says so. Until something has 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 and waiting to be 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.
Why nothing can reach this from outside
The Postfix KLYRN installs is a null client. KLYRN writes the configuration, and the reasons are in the file:
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. With no listener to reach, there is no open relay to find.- No local delivery
mydestinationis empty, so no address on this machine is a destination.- No direct-to-MX
- A relay is required. Mail sent straight to recipients from a hosting IP 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 and is never returned by the API. The panel is told whether a password is set, never what it is. It is redacted from support bundles in every export shape KLYRN produces.
Lists, filters and delivery
A mailing list can live on any mail domain hosted here. It has members, a rule for who may post (members, anyone or moderators), a subject prefix and an optional reply to the list.
Leaving takes one step with a confirmation. Each post carries List-Id and
List-Unsubscribe headers and is DKIM-signed as the list.
Each mailbox can have its own filters. Spam settings apply per domain and per mailbox. Track delivery shows whether mail to or from your domains was delivered, deferred or bounced.
Turning it off
Disabling removes the sendmail_path from every pool and stops Postfix.
KLYRN owns the files it wrote:
/etc/postfix/main.cf
/etc/postfix/master.cf
/etc/postfix/sasl_passwd
/etc/postfix/sender_canonical
After disabling, mail() returns false again, as it does on a
server that never had this switched on.