2. Getting started on a cheap VPS¶
This chapter is a single, end-to-end walkthrough: starting from a freshly rented virtual private server and a domain name you have just registered, it takes you all the way to a first e-mail received and delivered into a local mailbox — and a proper bounce sent back for anything that cannot be delivered. Every other chapter explains one piece in depth; this one strings the pieces into one concrete story you can follow top to bottom.
2.1. What we will build¶
A small mail host that accepts mail for example.org on the standard SMTP port
and delivers each message into the recipient’s local Maildir on the same
server. Anything it cannot deliver locally — an unknown recipient, a mailbox
that is full or unwritable — is turned into a real bounce (a delivery-status
notification) and sent back to the original sender, rather than silently
disappearing into the queue. The inbound pipeline is short:
inbound SMTP -> [stage-init] -> [stage-local]
(pepsi-ingress) pepsi-stage-arc pepsi-stage-relay-to-maildir
(record the (deliver to the user's
inbound verdict) ~/Maildir/new/)
and whatever it cannot deliver is signed and mailed back to the sender over a short outbound path:
[stage-bounce] -> [stage-sign] -> [stage-internet]
pepsi-stage-bounce pepsi-stage-dkim-sign pepsi-stage-relay-to-internet
(build the DSN) (sign the bounce) (send it to the sender's MX)
Replace example.org with your own domain and alice with a real account
name throughout. Because the host now also sends that bounce — DKIM-signed,
from an SPF-authorised address — this setup publishes the sender-authentication
records that let receivers trust its outgoing mail: DKIM, SPF and
DMARC, which a receive-only host would not need. (MTA-STS and TLS
reporting, which harden inbound TLS and need the HTTPS server running, are
left off here and added as optional hardening — see Next steps.) A
final section reuses exactly the same outbound path to add an authenticated
submission port so your own users can send through the host too.
Note
Scope. This is the simplest realistic deployment — receiving mail, delivering it locally, and bouncing what it cannot. It is not the forwarding showcase Pepsi is built for (ARC + SRS + DKIM re-signing on the way back out); see Introduction and pepsi-stage-relay-to-internet for that, and Next steps for how to grow this host into one.
Pepsi delivers into a Maildir but ships no IMAP/POP server, so once mail
has landed you read it on the box itself (mutt, mail) until you add a
mailbox server such as Dovecot. See Next steps.
2.2. Prerequisites¶
A VPS with a public, static IPv4 address (and ideally an IPv6 address) and root access. We use
203.0.113.7/2001:db8::25as placeholders.A registered domain whose DNS you can edit (
example.org).TCP port 25 must reach the VPS both ways. Inbound port 25 carries the mail you receive; outbound port 25 lets the host send bounces back to senders. Many budget providers block outbound 25 by default — if inbound mail never arrives, or bounces never leave the queue, that is the usual cause; ask the provider to open it. (If outbound 25 cannot be unblocked, send the bounces through a smarthost instead — see pepsi-stage-relay-to-smarthost.)
Reverse DNS (PTR). Set the VPS’s PTR record to
mail.example.org— this is done in the VPS provider’s control panel, not in your domain’s zone. It must name the same host you announce inEHLO: many receivers compare the two and refuse the session when they differ (HELO host does not match rDNS), which is easy to trip on a machine that answers to several names.pepsi-setup runwarns about every sending address whosePTRis missing, unconfirmed or names a different host, andpepsi-setup --wizardproposes thePTRname as the hostname for exactly this reason.The build prerequisites from Installation: a Rust toolchain, PostgreSQL, and (for automatic TLS)
certbot. Install them with your platform’s package manager.
2.3. Step 1 — Delegate DNS and place the base records¶
Pepsi contains no DNS server; you publish records wherever your domain’s zone is authoritative. You have two choices, and “delegation” means different things for each:
Host the zone at a DNS provider (your registrar, or a service such as a managed DNS host). Here the registrar already delegates
example.orgto that provider’s name servers; you only add records in their panel. This is the simplest path.Run your own authoritative name server (BIND, NSD, Knot, …) on the VPS or elsewhere. Then at the registrar you set
example.org’sNSrecords to your name servers and, if a name server is itself insideexample.org, add the matching glueA/AAAArecords at the registrar so the delegation can be resolved:; at the registrar (delegation + glue) example.org. NS ns1.example.org. ns1.example.org. A 203.0.113.7
Either way, once the zone is under your control, add the two records the mail host needs immediately:
; the mail host's address
mail.example.org. A 203.0.113.7
mail.example.org. AAAA 2001:db8::25
; mail for the domain goes to that host
example.org. MX 10 mail.example.org.
Note
The remaining records — SPF, DKIM and DMARC — are added in
Step 6 — Publish the remaining records and verify after pepsi-setup has generated the DKIM key and
printed the exact text to publish. (MTA-STS and TLS-reporting records are only
added if you enable those optional features — see Next steps.) Add
the A/AAAA record now, though: the automatic TLS certificate in Step 5
requires mail.example.org to already resolve to this VPS.
2.4. Step 2 — Install Pepsi¶
Build and install from a source checkout (there is no binary package yet). Fetch the vendored submodule first, then install binaries, SQL and the sample config in one step:
git submodule update --init --recursive
make install PREFIX=/usr SYSCONFDIR=/etc
This installs the pepsi-* binaries, the SQL migrations and a sample
/etc/pepsi/pepsi.conf. See Installation for what each step does and for
the database-schema lifecycle; run make install as root so the local-delivery
helper can be installed with its privileges (next step).
2.5. Step 3 — Prepare the system¶
Local delivery writes into other users’ home directories, so Pepsi separates privileges with a dedicated service user and a tightly scoped setuid helper. Create the accounts (names are conventional; use your platform’s user/group tools):
a
pepsiservice user the long-running daemons run as, anda
pepsi-maildirgroup that gates the delivery helper.
The delivery path has two privilege bits, which make install sets
automatically when run as root and the pepsi-maildir group already exists;
otherwise set them by hand:
pepsi-helper-maildir-writer(1) must be setuid-root and group-restricted (
chown root:pepsi-maildir … ; chmod 4750 …), andpepsi-stage-relay-to-maildir must be setgid
pepsi-maildir(chmod 2755 …), which is what lets the unprivilegedpepsiworker exec the helper and nothing else can.
See those two chapters for the rationale.
Create the database. Pepsi connects over the local socket as the connecting
system user (peer authentication), so create a pepsi role and a database it
owns to match CONFIG = postgres:///pepsi below:
createuser pepsi
createdb -O pepsi pepsi
Finally, create at least one local mailbox account to receive mail:
useradd --create-home alice
2.6. Step 4 — Write the configuration¶
All components read one file, /etc/pepsi/pepsi.conf. The sample installed in
Step 2 is a full-featured forwarder; for this walkthrough you want the minimal
local-delivery-plus-bounce configuration instead. There are two equivalent ways
to produce it — let pepsi-setup generate it from a few answers, or write it
by hand — and they build the same pipeline. (The format and every option are
described in Configuration and pepsi.conf(5).)
pepsi-setup --wizard runs a short interview, writes a complete and
already-validated /etc/pepsi/pepsi.conf, and then offers to run the
provisioning of Step 5 for you. Start it:
pepsi-setup --wizard
Answer the prompts as follows. Press Enter to accept a [default]; the
prompts not listed here can all keep their defaults (they are no for this
minimal host):
Mail direction this host serves —
inboundMail server hostname (MX / EHLO name) —
mail.example.org. The default offered here is the name this host’s public address reverse-resolves to; if that is notmail.example.org, fix thePTRrecord before going further rather than overriding it here.Domain(s) we accept mail for —
example.orgPostmaster address — accept the default (
postmaster@example.org)Public sending IP address(es) for SPF —
203.0.113.7 2001:db8::25PostgreSQL connection string — accept the default (
postgres:///pepsi)Deliver mail for local system accounts into their Maildir —
yesAlso relay non-local recipients to an upstream smarthost —
noServe an MTA-STS policy over HTTPS —
no(optional hardening, added later; see Next steps)Send and advertise SMTP TLS Reports (TLSRPT) —
no
The wizard validates the result, writes the file, and asks whether to run
the full setup now: answer yes to fold Step 5 into this step, or no
to run pepsi-setup run yourself afterwards. Your answers are recorded in
a [pepsi-wizard] section, so re-running the wizard pre-fills them.
The file it writes is the same pipeline as the hand-written one in the other
tab, with a few extra production-sensible defaults (DANE = warn, message
lifetimes). It also delivers to any regular login account; add
TARGETS = alice to [stage-local] to restrict delivery to named
accounts.
The same interview without a terminal. pepsi-setup questions prints
it as JSON — every question’s identifier, shape, default and help text —
and pepsi-setup --wizard --answers answers.json answers it from a file
of those identifiers. It is the interactive interview with the prompts
supplied, not a second implementation, so the branches and the per-field
validation are the ones above. That is also the model the browser setup
renders; see the “Setting up in a browser” section of Installation.
Replace /etc/pepsi/pepsi.conf with the following:
[pepsi]
# Where pepsi-setup writes the generated DKIM/ARC keys.
KEY_DIR = /var/pepsi/keys
DKIM_SELECTOR = pepsi
# Identity that signs the ARC seal recording the inbound verdict.
ARC_DOMAIN = example.org
# This minimal host does not serve an MTA-STS policy (that needs the
# HTTPS server), so do not advertise one. See "Next steps" to add it.
MTA_STS_MODE = none
[pepsi-postgres]
CONFIG = postgres:///pepsi
[pepsi-ingress]
HOSTNAME = mail.example.org
ACCEPTED_DOMAINS = example.org
# The MX listener on port 25. MODE = starttls with no TLS_CERT/TLS_KEY
# lets pepsi-setup fill in the certbot certificate paths (Step 5).
[pepsi-ingress-listener-mx]
SERVE = tcp
BIND_TO = 0.0.0.0
PORT = 25
MODE = starttls
# New messages enter here. ARC-seal the SPF/DKIM/DMARC verdict Pepsi
# computed at the boundary, then hand the message to local delivery.
[stage-init]
PROGRAM = pepsi-stage-arc
NEXT_STAGE = local
# Deliver to a local user's Maildir. By default any regular login account
# (the /etc/login.defs UID range) may receive mail, so 'alice' is already
# covered; set TARGETS to restrict delivery to an allow-list (e.g.
# TARGETS = alice). Anything not deliverable here is sent to the bounce
# path: an unknown recipient via NEXT_STAGE, a mailbox that fails to
# accept it via BOUNCE_STAGE.
[stage-local]
PROGRAM = pepsi-stage-relay-to-maildir
SERVER_NAME = mail.example.org
NEXT_STAGE = bounce
BOUNCE_STAGE = bounce
# --- Outbound bounce path: turn an undeliverable message into a DSN and
# mail it back to the original sender. Three short stages: build, sign,
# send.
# Rewrite the message in place into an (unsigned) RFC 3464 bounce
# addressed to the original sender, then hand it to the signer.
[stage-bounce]
PROGRAM = pepsi-stage-bounce
SERVER_NAME = mail.example.org
NEXT_STAGE = sign
# DKIM-sign the bounce so it authenticates at the sender's server.
[stage-sign]
PROGRAM = pepsi-stage-dkim-sign
NEXT_STAGE = internet
# Deliver the signed bounce directly to the sender's mail exchanger.
[stage-internet]
PROGRAM = pepsi-stage-relay-to-internet
SERVER_NAME = mail.example.org
# Read by pepsi-setup to build the SPF record authorising this host to
# send (the bounces above, and any outbound mail you add later).
[pepsi-stage-relay-to-smarthost]
PUBLIC_IP = 203.0.113.7 2001:db8::25
A message for a recipient that is not a permitted local mailbox is turned
into a bounce and mailed back to the sender, rather than silently
failing: an unknown @example.org recipient takes [stage-local]’s
NEXT_STAGE to the bounce path, and a known mailbox that cannot be
written (full disk, permissions) is retried and then bounced via
BOUNCE_STAGE. Both point at the same [stage-bounce].
Note that NEXT_STAGE here goes to the bounce stage, not to
[stage-internet]. This host serves only local mailboxes, so every
recipient it cannot deliver is an unknown user of our own domain, and
relaying those onward would look up example.org’s MX, find this very
host, and post the mail straight back to us — a loop. Turning this host into
a real forwarder — where an alias may legitimately expand to an address
elsewhere — is a separate step (Next steps); there NEXT_STAGE
does reach a relay, and UNKNOWN_MAILBOX_STAGE keeps unknown local users
on the bounce path (see pepsi-stage-relay-to-maildir).
2.7. Step 5 — Provision the schema, keys and certificate¶
If you used the wizard in Step 4 and answered yes to “run setup now”, this is already done — re-run the command below at any time to capture the DNS records again (it is idempotent). Otherwise, run the bootstrap tool once, capturing the DNS records it prints:
pepsi-setup -c /etc/pepsi/pepsi.conf run > pepsi-dns.zone
This validates the configuration, installs the pepsi schema,
generates the per-domain DKIM/ARC keys under KEY_DIR, obtains the TLS
certificate for mail.example.org via certbot (this needs the A record
from Step 1 to be live and inbound port 80 reachable), and prints the
remaining DNS records to pepsi-dns.zone. It is idempotent and safe to re-run.
If you would rather manage the certificate yourself, pass --no-certbot and set
TLS_CERT/TLS_KEY in the listener section. The full provisioning workflow
is in pepsi-setup and Installation.
2.8. Step 6 — Publish the remaining records and verify¶
Open pepsi-dns.zone and publish the records it lists in your zone:
the DKIM public key (
pepsi._domainkey.example.orgTXT),the SPF policy listing this host’s sending addresses, and
a DMARC policy (
_dmarc.example.orgTXT).
(Only these are printed for the minimal host; an MTA-STS or TLS-reporting record appears here too once you enable those features — see Next steps.)
The suggested DMARC policy is v=DMARC1; p=none, which asks receivers to
report on mail that fails authentication without acting on it. That is the
right place to start and the wrong place to stay: add rua=mailto:<address>
so the aggregate reports reach you, and once they show your own mail passing,
tighten p= to quarantine and then reject. Publish it by hand,
carefully — a DMARC record that does not parse is discarded in silence, and a
domain whose policy is one missing ; away from valid looks protected and is
not. That is precisely what the check below is for.
Two more blocks appear once this host does end-to-end cryptography, and both are about letting correspondents find your users’ keys (Key management):
an
openpgpkey.example.orgA/AAAA record pointing at this host, needed by the Web Key Directory’s advanced method — the one every current client tries first.pepsi-setupalso asks certbot to cover that name, so publish the record before the next certificate issuance. The direct method on the apex works without it, so a missing record is a degradation rather than a breakage;an
OPENPGPKEY(RFC 7929) orSMIMEA(RFC 8162) record per published identity. Publish these only if your zone is DNSSEC-signed: an unsigned key record is a key from whoever can answer for the zone, which is no improvement on having no key at all. The Web Key Directory needs no DNSSEC, because HTTPS authenticates the domain itself.
Once DNS has propagated, check what is live against what Pepsi expects:
pepsi-setup -c /etc/pepsi/pepsi.conf check
This is informational (it always exits 0); it flags a missing or mismatched DKIM
or SPF record (and MTA-STS, once enabled) so you can fix the zone before relying
on it. It also validates the DMARC record strictly and reports one that receivers
would ignore as [INVALID], naming the offending tag — the one failure in this
list that produces no bounce, no log line and no complaint from anyone.
2.9. Step 7 — Start the services¶
Two long-lived processes run continuously, both as the pepsi user:
pepsi-ingress serve— the inbound SMTP server, andpepsi-dispatch serve— the stage coordinator (exactly one per host).
Run each under your platform’s service/process manager so they start at boot and restart on failure. The stage programs themselves are spawned by the dispatcher; you never start them by hand. Binding port 25 needs privilege — grant the ingress binary the capability to bind low ports, or use socket activation — see Installation and pepsi-ingress.
2.10. Step 8 — Send the first e-mail¶
From an account on another provider, send a message to alice@example.org (or
use a tool such as swaks --to alice@example.org --server mail.example.org).
Watch it move through the pipeline:
pepsi-status -c /etc/pepsi/pepsi.conf
A delivered message is removed from the queue, so a healthy run shows an empty backlog and an incremented delivery counter. Confirm the mail actually landed:
ls /home/alice/Maildir/new/
mutt -f /home/alice/Maildir # or: mail
If the file is there, you have received your first e-mail.
2.11. Step 9 — Open the console (optional)¶
Everything above is visible in a browser as well. Add an administrative listener bound to loopback:
[pepsi-httpd-listener-admin]
SERVE = tcp
BIND_TO = 127.0.0.1
PORT = 8443
MODE = plain
ADMIN = yes
and an account to sign in with — over the local socket a member of the
pepsi-admin group needs none, but a browser cannot open a UNIX socket, so
create one and reach the listener through an SSH tunnel:
ssh -L 8443:127.0.0.1:8443 root@mail.example.org
Then open http://127.0.0.1:8443/ui. The dashboard shows the same queue
pepsi-status printed, the configuration pages show where each value came
from, and the audit log records every change made from any surface. See
The administration console for the page reference and The administrative API for the API it is a
client of. The console performs no service control: restarting a component is
still systemctl’s job.
2.12. Sending mail too: add a submission port¶
So far the host only receives. To let your own users send mail through it
from a mail client (Thunderbird, Evolution, KMail, or K-9 Mail on a phone), add
an authenticated
submission listener and an outbound delivery path. A client authenticates
with a username and password; pepsi-ingress then records the message with
state.local_origin = true, which both allows it to relay to any domain (not
only example.org) and lets the pipeline tell it apart from inbound mail.
Open inbound TCP ports 465 and/or 587 on the VPS firewall, the same way you opened port 25.
2.12.1. Route inbound and outbound apart¶
You already built an outbound delivery path for the bounces — [stage-sign] →
[stage-internet], with the SPF record covering this host. Authenticated
submissions reuse exactly that path; you only need to route them onto it.
Outbound mail must take a different path from inbound mail, and getting this
wrong causes a mail loop: if you simply pointed [stage-init] at
[stage-sign], inbound mail would be signed and relayed back out instead of
delivered locally. Instead, branch on state.local_origin at the entry point
with a pepsi-stage-if stage — locally-originated (authenticated)
mail goes to the outbound path, everything else to the inbound path. Crucially,
the split must come before pepsi-stage-arc: ARC preserves an upstream
sender’s authentication across your forwarding hop, so it is meaningless for — and
must never touch — mail your own users originate (that mail is authenticated by
[stage-sign] instead; ARC-sealing it would only publish the submission’s own
SPF fail). So make the router the [stage-init] entry stage and move ARC
onto the inbound branch:
# Entry: split by direction FIRST. Authenticated submissions (local_origin =
# true) are outbound and reuse the signing path; everything else is inbound and
# goes through ARC. This only moves the stage; it never changes the message.
[stage-init]
PROGRAM = pepsi-stage-if
STATE_PATH = local_origin
VALUE = true
TRUE_STAGE = sign # outbound: our users' mail (reuses the bounce path)
FALSE_STAGE = arc # inbound: ARC-seal, then deliver locally
# ARC now sits on the inbound branch (received mail only), feeding local
# delivery just as [stage-init] used to.
[stage-arc]
PROGRAM = pepsi-stage-arc
NEXT_STAGE = local
[stage-sign], [stage-internet] and the PUBLIC_IP line are already in
your configuration from the bounce path, so submissions share them unchanged.
[stage-arc] is the former [stage-init] body (unchanged but renamed), and
[stage-local] is unchanged too — its NEXT_STAGE/BOUNCE_STAGE still
bounce mail to unknown local users rather than relaying it anywhere. (The ARC
stage also skips any state.local_origin message on its own, so a misplacement
cannot seal a submission — but keeping it off the outbound branch avoids the
wasted hop.)
Note
The outbound path needs outbound port 25 (see Prerequisites), which budget
providers often block. If yours is blocked, deliver through an upstream
smarthost instead: replace [stage-internet] with a
pepsi-stage-relay-to-smarthost stage and define the smarthost — see
pepsi-stage-relay-to-smarthost. To return non-delivery reports
when an MX refuses your users’ outgoing mail, give [stage-internet] a
BOUNCE_STAGE = bounce as well.
2.12.2. Provide a password backend (Dovecot)¶
A submission listener needs a way to check passwords. Pepsi keeps no password
database of its own; it verifies credentials through a Dovecot SASL socket.
Install Dovecot and configure it to authenticate your accounts (the system users
you created, or virtual users). Pepsi’s inbound server runs as the unprivileged
pepsi-ingress user, which cannot open Dovecot’s shared, root-only
auth-client socket, so give it a dedicated listener it owns — in
/etc/dovecot/conf.d/10-pepsi.conf:
service auth {
unix_listener auth-client-pepsi {
mode = 0600
user = pepsi-ingress
}
}
You do not have to write this by hand: pepsi-setup run detects an unreachable
socket and offers to install exactly this drop-in (validating it with doveconf
and reloading Dovecot), or prints it if it cannot.
Dovecot is also what you would add to let clients read their Maildirs over IMAP (Next steps), so it earns its keep twice. Its full configuration is beyond this guide; see the Dovecot documentation.
2.12.3. Add the submission listeners¶
Add an implicit-TLS listener on 465 (recommended for clients) and/or a STARTTLS
listener on 587. SUBMISSION = yes makes authentication mandatory and applies
the RFC 6409 fixups (a missing Date:/Message-ID: is added);
SASL_TYPE/SASL_PATH wire in Dovecot. Authentication is only ever offered
over TLS. As with the MX listener, leaving TLS_CERT/TLS_KEY unset lets
pepsi-setup install the mail.example.org certificate automatically:
# Port 465: implicit TLS.
[pepsi-ingress-listener-submissions]
SERVE = tcp
BIND_TO = 0.0.0.0
PORT = 465
MODE = tls
SUBMISSION = yes
SASL_TYPE = dovecot
SASL_PATH = /run/dovecot/auth-client-pepsi
# Port 587: cleartext upgraded with STARTTLS.
[pepsi-ingress-listener-submission]
SERVE = tcp
BIND_TO = 0.0.0.0
PORT = 587
MODE = starttls
SUBMISSION = yes
SASL_TYPE = dovecot
SASL_PATH = /run/dovecot/auth-client-pepsi
Re-run the bootstrap so the new listeners’ certificates and the updated SPF record are picked up, then restart the services:
pepsi-setup -c /etc/pepsi/pepsi.conf run > pepsi-dns.zone
Publish the updated records (the SPF record now lists this host) and re-verify with
pepsi-setup -c /etc/pepsi/pepsi.conf check.
2.12.4. Connect a mail client¶
In the client’s account settings, configure the outgoing (SMTP) server:
Server / host:
mail.example.orgPort / security:
465with implicit TLS (SSL/TLS), or587with STARTTLSAuthentication: normal password
Username / password: the account Dovecot expects (e.g.
aliceoralice@example.org) and its password
If you also set up Dovecot for IMAP, point the incoming server at the same host (IMAP, port 993, TLS). Send a test message to an address at another provider, confirm it left the queue:
pepsi-status -c /etc/pepsi/pepsi.conf
and check that it arrived. Outgoing deliverability depends on the same DNS hygiene as receiving — correct PTR/reverse DNS, the SPF record listing this host, and DKIM signing (all configured above); a brand-new IP is sometimes greylisted on first contact, so a short delay on the very first message is normal.
2.13. Troubleshooting¶
- Nothing arrives
Check the
MXrecord resolves tomail.example.organd that record’sA/AAAApoints at the VPS; confirm inbound port 25 is open (test from another host); confirm the PTR/reverse-DNS is set. Watch the ingress log.- A message is stuck or
failed Inspect it with pepsi-status and pepsi-queue. A recipient that is not in
TARGETSor has no account is now bounced back to the sender (watch[stage-bounce]and[stage-internet]), not left in the queue; atimeout/paused row often means a filesystem problem (e.g. a full disk) in the recipient’s home, retried before it bounces. A bounce that itself sticks usually means outbound port 25 is blocked (see below).- The certificate step failed
certbotneedsmail.example.orgto already resolve to this VPS and inbound port 80 reachable. Fix DNS/port 80 and re-runpepsi-setup … run, or use--no-certbotand supplyTLS_CERT/TLS_KEYyourself.- “Permission denied” on delivery
The privilege bits from Step 3 are wrong: the helper must be
4750 root:pepsi-maildirand the relay-to-maildir stage2755setgidpepsi-maildir. Re-runmake installas root with the group present, or set them by hand.- A client cannot authenticate or send
Authentication is offered only over TLS — use port 465 (implicit TLS) or 587 with STARTTLS and “normal password”. A
530reply means the session was unauthenticated; check that the Dovecot auth socket (SASL_PATH) is readable by thepepsiuser and that Dovecot accepts the credentials.- Outgoing mail is stuck
Direct internet delivery needs outbound port 25; if the provider blocks it, switch
[stage-internet]to a smarthost (see the note above). Inspect stuck rows with pepsi-status and pepsi-queue.
2.14. Next steps¶
Read mail remotely. Add an IMAP server (e.g. Dovecot) pointed at the same
~/Maildirso mail clients can fetch it; Pepsi handles only delivery into the Maildir. If you already added Dovecot for submission authentication above, enabling IMAP is a small additional step.Turn this into a forwarder. Insert pepsi-stage-srs on the inbound path so mail re-sent to a third party passes SPF, re-using the outbound relay (
[stage-sign]→[stage-internet]) already in this configuration — Pepsi’s core purpose (Introduction).Customise the bounces. The bounce path is already wired in; you can localise or brand the DSN text with a
BOUNCE_MESSAGEtemplate — see pepsi-stage-bounce.Harden inbound TLS (MTA-STS + TLS reporting). Tell other senders to use validated TLS toward your MX by serving an MTA-STS policy, and collect TLS Reports (RFC 8460). Both need the HTTPS server (pepsi-httpd) running and a certificate for each
mta-sts.<domain>; the easiest way to add them is to re-runpepsi-setup --wizardand answer yes to the two transport-security questions (it pre-fills your earlier answers).Filter and protect. Layer in language blocking, sender whitelisting or the pay-to-send paywall — see Supported Features and the per-stage chapters.
Front an existing Exchange or Microsoft 365 deployment. Pepsi can sit in front of and behind Exchange as a transparent crypto gateway, so users get S/MIME and OpenPGP without touching their clients. That is a different topology from the one above — mail is routed to Exchange rather than delivered locally, which makes loop prevention mandatory rather than advisable — and it has its own chapter: Microsoft Exchange as a gateway.