InfrastructureSelf-Hosting

How to Receive Email at Home Through a VPS Gateway

A complete Postfix, Dovecot, Rspamd, HAProxy, and reverse-SSH setup for receiving Internet email on a home server without router access.

Terracotta line illustration showing Internet email entering a public gateway, crossing a blue encrypted tunnel, and reaching mailboxes inside a home, while the outgoing route climbs a mountain.
Lead image Terracotta line illustration showing Internet email entering a public gateway, crossing a blue encrypted tunnel, and reaching mailboxes inside a home, while the outgoing route climbs a mountain.
On this page

I wanted a real mailbox on my home server: mail accepted from the Internet, filtered, stored locally, and available over IMAP. The awkward part was that the server sits behind a router I do not control.

That rules out the usual advice to forward ports 25, 587, and 993. It does not rule out self-hosting mail.

I already had a small VPS and a persistent reverse SSH tunnel from home to it. That was enough to split the system at a useful boundary:

  • the VPS owns the public IP address, SMTP ports, TLS certificates, and outbound reputation;
  • the home server owns accounts, Maildir storage, and Dovecot;
  • an encrypted SSH connection, initiated from home, joins them.

The result receives ordinary Internet email at user@example.com even though no inbound connection can reach the home network. I verified the full path with a message from Gmail: Google connected to the VPS over TLS, Rspamd accepted it, Postfix moved it through the tunnel, Dovecot saved it to the home Maildir, and the message appeared over public IMAPS.

This tutorial rebuilds that design with placeholders. Replace:

example.com       your mail domain
mail.example.com  your public mail hostname
203.0.113.10      your VPS IPv4 address
alice             your first mailbox

The configuration targets Dovecot 2.4 syntax. Run dovecot --version before copying it; Dovecot 2.3 uses the older mail_location and authentication-block syntax.

Understand the two paths

Receiving and reading mail are separate network flows.

Incoming delivery

sender's mail server
        |
        | SMTP :25
        v
public VPS
Postfix -> Rspamd
        |
        | reverse SSH forward, VPS :10025 -> home :25
        v
home Postfix -> Dovecot LMTP -> Maildir


Reading mail

phone or laptop
        |
        | IMAPS :993
        v
VPS HAProxy terminates TLS
        |
        | reverse SSH forward, VPS :1143 -> home :1143
        v
home Dovecot IMAP

Normal Internet delivery between mail servers uses TCP port 25. A tunnel product that exposes only HTTP does not solve that problem. Neither does an outbound SMTP relay: a relay can help send mail, but other mail servers still need a public MX endpoint for delivery.

The VPS is that endpoint. If the home server is offline, VPS Postfix keeps the message in its queue and retries later. The mailbox itself remains at home.

Authenticated sending uses a third path:

mail client -> VPS Postfix :587 -> Dovecot auth through SSH -> Internet

Rspamd runs on the VPS so it can reject spam before transferring it home and sign outgoing messages before they leave the public IP.

Check the prerequisites

You need:

  • a domain whose DNS records you can edit;
  • a VPS with a stable public IPv4 address;
  • permission to receive TCP 25 on the VPS;
  • outbound TCP 25 from the VPS, or an SMTP relay;
  • control of the VPS reverse-DNS/PTR record;
  • root access to the VPS and home server; and
  • a home server that can make outbound SSH connections to the VPS.

Check outbound SMTP from the VPS:

sudo apt install netcat-openbsd
timeout 8 nc -vz gmail-smtp-in.l.google.com 25

A successful TCP connection does not guarantee good delivery, but a timeout or provider rejection is an immediate blocker for direct sending.

Also check whether the three public ports are free:

sudo ss -ltnp | grep -E ':(25|587|993)\b' || true

Port 25 will be public SMTP, 587 authenticated submission with STARTTLS, and 993 IMAP over implicit TLS.

Create the DNS records first

Add these records at the authoritative DNS provider:

TypeNameValue
Amail203.0.113.10
MX@priority 10, mail.example.com
TXT@v=spf1 ip4:203.0.113.10 -all
TXT_dmarcv=DMARC1; p=none; rua=mailto:dmarc@example.com; adkim=s; aspf=s

The DKIM TXT record comes later, after generating its key.

If the domain uses Cloudflare, the mail A record must be DNS only, not orange-cloud proxied. Cloudflare states that MX records cannot be proxied and a hostname used for email should also be DNS only because its normal proxy does not carry SMTP port 25 (Cloudflare DNS documentation ).

Do not publish an AAAA record unless the VPS is genuinely configured to receive and send mail over that IPv6 address. A broken IPv6 mail path is worse than having no mail IPv6 record.

At the VPS provider, set:

203.0.113.10 PTR mail.example.com

Forward and reverse DNS must agree:

dig +short A mail.example.com
dig +short -x 203.0.113.10

The first command should return 203.0.113.10; the second should return mail.example.com.

Reverse DNS is mostly an outgoing-mail concern, but it is worth fixing before the hostname becomes operational. Google currently requires valid forward and reverse DNS for senders, along with TLS and authentication (Gmail sender guidelines ).

Obtain one TLS certificate on the VPS

TLS terminates on the VPS for both SMTP submission and IMAPS. The home-side IMAP connection is plaintext only on loopback and then travels inside SSH.

Using a DNS challenge avoids fighting an existing web server on ports 80 and 443. For Cloudflare:

sudo apt update
sudo apt install certbot python3-certbot-dns-cloudflare haproxy
sudo install -d -m 700 /root/.secrets/certbot
sudoedit /root/.secrets/certbot/cloudflare.ini

Put a narrowly scoped Cloudflare token in that file:

dns_cloudflare_api_token = REPLACE_WITH_A_DNS_EDIT_TOKEN

Then protect it and request the certificate:

sudo chmod 600 /root/.secrets/certbot/cloudflare.ini
sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/certbot/cloudflare.ini \
  -d mail.example.com

The DNS plugin creates and removes the ACME TXT challenge automatically. Its documentation recommends an API token rather than Cloudflare’s account-wide global key (Certbot DNS Cloudflare ).

The resulting files are:

/etc/letsencrypt/live/mail.example.com/fullchain.pem
/etc/letsencrypt/live/mail.example.com/privkey.pem

HAProxy wants the certificate and private key in one PEM file:

sudo install -d -m 750 -o root -g haproxy /etc/haproxy/certs
sudo sh -c 'cat \
  /etc/letsencrypt/live/mail.example.com/fullchain.pem \
  /etc/letsencrypt/live/mail.example.com/privkey.pem \
  > /etc/haproxy/certs/mail.example.com.pem'
sudo chown root:haproxy /etc/haproxy/certs/mail.example.com.pem
sudo chmod 640 /etc/haproxy/certs/mail.example.com.pem

Put those commands in an executable Certbot deploy hook that reloads Postfix and HAProxy after renewal:

sudoedit /etc/letsencrypt/renewal-hooks/deploy/mail-services
sudo chmod 750 /etc/letsencrypt/renewal-hooks/deploy/mail-services

The hook should rebuild the HAProxy PEM, validate both services, then reload them:

#!/bin/sh
set -eu

cat /etc/letsencrypt/live/mail.example.com/fullchain.pem \
    /etc/letsencrypt/live/mail.example.com/privkey.pem \
    > /etc/haproxy/certs/mail.example.com.pem
chown root:haproxy /etc/haproxy/certs/mail.example.com.pem
chmod 640 /etc/haproxy/certs/mail.example.com.pem

postfix check
haproxy -c -f /etc/haproxy/haproxy.cfg
systemctl reload postfix haproxy

Configure the home mailbox server

Install the small home-side stack:

sudo apt install postfix dovecot-core dovecot-imapd dovecot-lmtpd autossh

Choose “Internet Site” if the Postfix package asks, then replace its generated configuration below.

Create a dedicated virtual-mail owner:

sudo groupadd -g 5000 vmail
sudo useradd -r -u 5000 -g vmail \
  -d /var/vmail -s /usr/sbin/nologin vmail
sudo install -d -m 750 -o vmail -g vmail \
  /var/vmail/example.com/alice

Generate a password hash:

sudo doveadm pw -s ARGON2ID

Save the output—not the cleartext password—in /etc/dovecot/users:

alice@example.com:{ARGON2ID}REPLACE_WITH_DOVEADM_OUTPUT

Protect the file:

sudo chown root:dovecot /etc/dovecot/users
sudo chmod 640 /etc/dovecot/users

On Debian-family Dovecot packages, comment out the default !include auth-system.conf.ext line in /etc/dovecot/conf.d/10-auth.conf. Otherwise normal operating-system users may remain eligible for mail authentication.

Create /etc/dovecot/conf.d/99-home-mail.conf:

protocols = imap lmtp
listen = 127.0.0.1

mail_driver = maildir
mail_home = /var/vmail/%{user | domain}/%{user | username}
mail_path = %{home}/Maildir
mail_uid = vmail
mail_gid = vmail
first_valid_uid = 5000
last_valid_uid = 5000

ssl = no
auth_allow_cleartext = yes
auth_mechanisms = plain login
auth_failure_delay = 3 secs

passdb passwd-file {
  default_password_scheme = ARGON2ID
  auth_username_format = %{user | lower}
  passwd_file_path = /etc/dovecot/users
}

userdb static {
  fields {
    uid = vmail
    gid = vmail
    home = /var/vmail/%{user | domain}/%{user | username}
  }
}

service imap-login {
  inet_listener imap {
    listen = 127.0.0.1
    port = 1143
  }
  inet_listener imaps {
    port = 0
  }
}

service auth {
  inet_listener postfix-auth {
    listen = 127.0.0.1
    port = 12345
  }
}

service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode = 0600
    user = postfix
    group = postfix
  }
}

protocol lmtp {
  auth_username_format = %{user | lower}
  postmaster_address = postmaster@example.com
}

ssl = no and cleartext authentication are safe here only because Dovecot listens exclusively on 127.0.0.1; HAProxy handles public TLS and the intermediate leg is SSH. Do not bind this listener to a LAN or public address.

Dovecot documents mail_driver and mail_path as the Dovecot 2.4 way to define Maildir storage (mail location settings ). Its Postfix LMTP guide uses the same Unix-socket handoff for final delivery (Postfix and Dovecot LMTP ).

Configure home Postfix in /etc/postfix/main.cf:

compatibility_level = 3.6
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain

inet_interfaces = loopback-only
inet_protocols = ipv4
mydestination = localhost
mynetworks = 127.0.0.0/8

virtual_mailbox_domains = example.com
virtual_mailbox_maps = hash:/etc/postfix/vmailbox
virtual_alias_maps = hash:/etc/postfix/virtual
virtual_transport = lmtp:unix:private/dovecot-lmtp

relayhost = [127.0.0.1]:1025
smtp_tls_security_level = may

smtpd_relay_restrictions = permit_mynetworks, reject_unauth_destination
smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_destination
disable_vrfy_command = yes
message_size_limit = 26214400
mailbox_size_limit = 0
recipient_delimiter = +

Create the mailbox and aliases:

sudo tee /etc/postfix/vmailbox >/dev/null <<'EOF'
alice@example.com example.com/alice/
EOF

sudo tee /etc/postfix/virtual >/dev/null <<'EOF'
postmaster@example.com alice@example.com
abuse@example.com alice@example.com
dmarc@example.com alice@example.com
EOF

sudo postmap /etc/postfix/vmailbox
sudo postmap /etc/postfix/virtual
sudo doveconf -n
sudo postfix check
sudo systemctl restart dovecot postfix

At this point the home server should listen only on loopback:

sudo ss -ltnp | grep -E '127.0.0.1:(25|1143|12345)\b'

Create a restricted SSH tunnel account

Generate a dedicated key on the home server:

sudo -u YOUR_HOME_USER ssh-keygen \
  -t ed25519 \
  -f /home/YOUR_HOME_USER/.ssh/mail_vps_tunnel_ed25519 \
  -N ''

Create a tunnel-only account on the VPS:

sudo adduser --system --group \
  --home /home/mail-tunnel \
  --shell /usr/sbin/nologin \
  mail-tunnel
sudo install -d -m 700 -o mail-tunnel -g mail-tunnel \
  /home/mail-tunnel/.ssh

Put the public key in /home/mail-tunnel/.ssh/authorized_keys as one line with these restrictions:

restrict,port-forwarding,permitlisten="127.0.0.1:10025",permitlisten="127.0.0.1:1143",permitlisten="127.0.0.1:12345",permitopen="127.0.0.1:25" ssh-ed25519 REPLACE_WITH_PUBLIC_KEY

Then:

sudo chown -R mail-tunnel:mail-tunnel /home/mail-tunnel/.ssh
sudo chmod 600 /home/mail-tunnel/.ssh/authorized_keys

The restrictions permit only the four mail paths this design needs. They do not grant a shell, agent forwarding, arbitrary listening ports, or arbitrary destinations.

Check the VPS SSH policy. If it contains a Match User mail-tunnel block, it must permit both local and remote TCP forwarding:

Match User mail-tunnel
    PasswordAuthentication no
    PubkeyAuthentication yes
    AllowTcpForwarding yes
    GatewayPorts no
    X11Forwarding no
    PermitTTY no

Validate before reloading:

sudo sshd -t
sudo systemctl reload ssh

Make the tunnel persistent

Create /etc/systemd/system/home-mail-tunnel.service on the home server:

[Unit]
Description=Mail reverse SSH tunnel to public VPS
Wants=network-online.target postfix.service dovecot.service
After=network-online.target postfix.service dovecot.service

[Service]
User=YOUR_HOME_USER
Environment=AUTOSSH_GATETIME=0
ExecStart=/usr/bin/autossh -M 0 -NT \
  -o ExitOnForwardFailure=yes \
  -o ServerAliveInterval=30 \
  -o ServerAliveCountMax=3 \
  -i /home/YOUR_HOME_USER/.ssh/mail_vps_tunnel_ed25519 \
  -R 127.0.0.1:10025:127.0.0.1:25 \
  -R 127.0.0.1:1143:127.0.0.1:1143 \
  -R 127.0.0.1:12345:127.0.0.1:12345 \
  -L 127.0.0.1:1025:127.0.0.1:25 \
  mail-tunnel@203.0.113.10
Restart=always
RestartSec=10
NoNewPrivileges=yes

[Install]
WantedBy=multi-user.target

The three -R forwards create loopback listeners on the VPS that lead home:

  • VPS 10025 to home Postfix 25;
  • VPS 1143 to home Dovecot IMAP 1143;
  • VPS 12345 to home Dovecot authentication 12345.

The -L forward gives home Postfix a path back to VPS Postfix for locally generated outgoing messages.

Enable it:

sudo systemctl daemon-reload
sudo systemctl enable --now home-mail-tunnel.service

On the VPS:

sudo ss -ltnp | grep -E '127.0.0.1:(10025|1143|12345)\b'

All three must remain bound to loopback. The public services will connect to them locally.

Configure the VPS gateway

Install the public-side stack:

sudo apt install postfix haproxy rspamd redis-server swaks

Create /etc/postfix/transport:

example.com smtp:[127.0.0.1]:10025

Create /etc/postfix/relay_recipients:

alice@example.com OK
postmaster@example.com OK
abuse@example.com OK
dmarc@example.com OK

This list matters. Without relay_recipient_maps, a gateway can accept mail for nonexistent users and generate a bounce later—the backscatter pattern spammers exploit.

Create /etc/postfix/sender_login:

alice@example.com alice@example.com

Compile the maps:

sudo postmap /etc/postfix/transport
sudo postmap /etc/postfix/relay_recipients
sudo postmap /etc/postfix/sender_login

Use this /etc/postfix/main.cf:

compatibility_level = 3.6
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain

inet_interfaces = all
inet_protocols = ipv4
mydestination = $myhostname, localhost.$mydomain, localhost
mynetworks = 127.0.0.0/8

relay_domains = example.com
relay_recipient_maps = hash:/etc/postfix/relay_recipients
transport_maps = hash:/etc/postfix/transport

smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
smtpd_helo_required = yes
disable_vrfy_command = yes

smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
smtpd_tls_security_level = may
smtpd_tls_auth_only = yes
smtp_tls_security_level = may

smtpd_sasl_type = dovecot
smtpd_sasl_path = inet:127.0.0.1:12345
smtpd_sasl_auth_enable = no
smtpd_sasl_security_options = noanonymous

smtpd_milters = inet:127.0.0.1:11332
non_smtpd_milters = inet:127.0.0.1:11332
milter_protocol = 6
milter_default_action = tempfail

smtpd_client_connection_count_limit = 20
smtpd_client_connection_rate_limit = 30
smtpd_client_auth_rate_limit = 10
anvil_rate_time_unit = 60s

message_size_limit = 26214400
mailbox_size_limit = 0
recipient_delimiter = +

Postfix supports Dovecot as its SMTP AUTH backend, including a TCP listener when the two services are on separate machines (Dovecot SASL documentation ). The auth socket here is reachable only on VPS loopback through SSH.

Enable submission in /etc/postfix/master.cf:

smtp      inet  n       -       y       -       -       smtpd
submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o smtpd_sender_login_maps=hash:/etc/postfix/sender_login
  -o smtpd_sender_restrictions=reject_sender_login_mismatch

The normal SMTP service offers opportunistic STARTTLS because other mail servers may fall back to plaintext. Submission is different: port 587 requires encryption and authentication.

Put Rspamd in front of the tunnel

Configure the milter worker in /etc/rspamd/local.d/worker-proxy.inc:

bind_socket = "127.0.0.1:11332";
milter = yes;
timeout = 120s;

upstream "local" {
  default = yes;
  self_scan = yes;
}

Configure Redis in /etc/rspamd/local.d/redis.conf:

servers = "127.0.0.1";

Generate a 2048-bit DKIM key:

sudo install -d -m 750 -o _rspamd -g _rspamd /var/lib/rspamd/dkim
sudo sh -c 'rspamadm dkim_keygen \
  -b 2048 \
  -s mail \
  -d example.com \
  -k /var/lib/rspamd/dkim/example.com.mail.key \
  > /root/example.com.mail.dkim.txt'
sudo chown _rspamd:_rspamd /var/lib/rspamd/dkim/example.com.mail.key
sudo chmod 600 /var/lib/rspamd/dkim/example.com.mail.key

Rspamd’s own documentation calls out 1024-bit RSA as weak and shows -b 2048 for DKIM generation (DKIM signing module ).

Create /etc/rspamd/local.d/dkim_signing.conf:

enabled = true;
selector = "mail";
path = "/var/lib/rspamd/dkim/$domain.$selector.key";
allow_username_mismatch = true;
sign_authenticated = true;
sign_local = true;
sign_inbound = false;
use_domain = "header";

Publish the TXT value from /root/example.com.mail.dkim.txt at:

mail._domainkey.example.com

Validate:

sudo rspamadm configtest
sudo postfix check
sudo systemctl restart redis-server rspamd postfix

Keep Rspamd’s controller and Redis on loopback. Neither needs a public port.

Publish IMAP through HAProxy

Use this /etc/haproxy/haproxy.cfg:

global
    log /dev/log local0
    user haproxy
    group haproxy
    daemon
    maxconn 2048

defaults
    log global
    mode tcp
    option tcplog
    timeout connect 10s
    timeout client 1h
    timeout server 1h

frontend imaps_public
    bind *:993 ssl crt /etc/haproxy/certs/mail.example.com.pem
    stick-table type ip size 100k expire 10m store conn_cur,conn_rate(10s)
    tcp-request connection track-sc0 src
    tcp-request connection reject if { sc0_conn_cur gt 10 }
    tcp-request connection reject if { sc0_conn_rate gt 30 }
    default_backend imap_home

backend imap_home
    option tcp-check
    server home 127.0.0.1:1143 check inter 10s fall 3 rise 2

Then:

sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl enable --now haproxy

Open only the required mail ports in the VPS firewall:

25/tcp   public SMTP
587/tcp  authenticated submission
993/tcp  IMAPS

Do not open 10025, 1143, 12345, 11332, or Redis. Those are internal loopback ports.

Verify receiving before sending

First check TLS:

openssl s_client \
  -connect mail.example.com:993 \
  -servername mail.example.com \
  -verify_return_error </dev/null

openssl s_client \
  -starttls smtp \
  -connect mail.example.com:587 \
  -servername mail.example.com \
  -verify_return_error </dev/null

Both should end with:

Verify return code: 0 (ok)

Check recipient handling:

swaks \
  --server mail.example.com \
  --from probe@outside.example \
  --to alice@example.com \
  --quit-after RCPT

The valid recipient should receive 250. Repeat with not-a-user@example.com; it should receive 550 User unknown in relay recipient table.

Check that the server is not an open relay:

swaks \
  --server mail.example.com \
  --from probe@outside.example \
  --to someone@gmail.com \
  --quit-after RCPT

That must fail with Relay access denied.

Now send a real message from Gmail to alice@example.com. Watch the public gateway:

sudo tail -f /var/log/mail.log /var/log/rspamd/rspamd.log

On the home server:

sudo journalctl -f -u postfix -u dovecot

A complete delivery ends with two distinct successes:

VPS:  status=sent (250 2.0.0 Ok: queued as ...)
Home: saved mail to INBOX

The first means the gateway passed the message through the tunnel. The second means Dovecot committed it to the mailbox.

Configure a mail client with:

Email/username: alice@example.com

Incoming IMAP:
  host: mail.example.com
  port: 993
  security: SSL/TLS

Outgoing SMTP:
  host: mail.example.com
  port: 587
  security: STARTTLS
  authentication: password

On iOS, enter the full email address as the username for both incoming and outgoing servers. The SMTP username and password are required even when the form labels them optional.

Why outgoing mail is harder

Receiving mail is mostly an availability problem. If the MX record points to a reachable port 25, the recipient exists, and the server behaves correctly, another mail server can hand over a message.

Sending is a reputation problem.

SPF, DKIM, and DMARC prove that the VPS is authorized to send for the domain. TLS protects the connection. PTR makes the IP and hostname agree. None of those facts forces Gmail, Outlook, or Yahoo to place the message in an inbox.

A new VPS IP has little history. A recycled VPS IP may have bad history. A provider may block outbound port 25. A residential address may appear on policy blocklists even when no spam originated from it. Shared-IP behavior, user complaints, volume spikes, malformed messages, and missing unsubscribe controls can all affect delivery.

Google’s current baseline for all senders includes SPF or DKIM, forward and reverse DNS, TLS, RFC 5322 formatting, and a low spam rate. Bulk senders have additional SPF, DKIM, DMARC, alignment, and unsubscribe requirements (Gmail sender guidelines ).

That creates three reasonable outbound choices.

Send directly from the VPS

Use this when:

  • the provider allows outbound TCP 25;
  • the IP is stable;
  • PTR is configurable;
  • blocklist checks are clean;
  • volume is small and predictable; and
  • you are willing to monitor bounces and reputation.

This is the setup above. Start slowly. Send wanted mail, not test blasts.

Relay outbound mail through a provider

This is often the practical answer. Keep all incoming mail, storage, IMAP, and account control in this design, but point VPS Postfix at a reputable SMTP relay for outbound delivery.

Conceptually:

relayhost = [smtp.relay-provider.example]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = encrypt

The relay credentials belong in /etc/postfix/sasl_passwd with mode 600, followed by postmap. Update SPF to authorize the provider and confirm that it DKIM-signs with—or preserves alignment for—your domain.

This does not weaken the incoming architecture. It changes only the last outbound hop.

Run the entire mailbox on the VPS

This removes the tunnel and is operationally simpler, but it also moves the stored mail off the home server. That may be the right trade if availability matters more than local custody.

The split design is useful precisely when those concerns differ: public network reliability at the VPS, private storage at home.

Operate it as a mail system

Once the first message arrives, the work changes from installation to operations.

Back up at least:

/var/vmail
/etc/dovecot
/etc/postfix
/etc/rspamd
/var/lib/rspamd/dkim
/etc/letsencrypt

The DKIM private key and mailbox-password database are secrets. Back them up encrypted.

Monitor:

systemctl is-active postfix dovecot home-mail-tunnel
ssh root@203.0.113.10 \
  'systemctl is-active postfix rspamd redis-server haproxy'

mailq
ssh root@203.0.113.10 postqueue -p

Test certificate renewal:

sudo certbot renew --dry-run

Keep the VPS and home packages patched. Watch disk space on /var/vmail and both Postfix queues. Preserve postmaster@ and abuse@ aliases. Add every real mailbox or alias to both the VPS recipient map and the home delivery maps; updating only one side creates confusing rejections or late bounces.

The key architectural result is narrow but useful: lack of router access does not prevent a home server from receiving standard Internet email. It requires moving the public SMTP edge to a machine that can be reached, then making the home server initiate the private path back to itself.

That solves incoming mail cleanly. Outgoing mail remains a separate decision about IP reputation, provider policy, and how much deliverability work you want to own.

Continue reading

Complete index →