Skip to content
Go back

Self-Hosted Email Aliasing on Your Own Domain

By SumGuy 12 min read
Self-Hosted Email Aliasing on Your Own Domain

The Dream vs. The Reality

Someone in every self-hosting forum, at least once a week, says it: “Just host your own email server.” And they’re not wrong in spirit. But they are deeply, tragically wrong about how much pain that involves.

Running a full mail server — Postfix, Dovecot, SpamAssassin, OpenDKIM, the works — means spending your weekend configuring PTR records, begging your VPS provider to unblock port 25, convincing Gmail that you’re not a bot farm, and watching your carefully crafted newsletters land directly in spam because your IP used to belong to someone who sold Viagra in 2009.

Here’s the thing: you probably don’t actually need to run your own mail server. What you actually want is to give a unique email address to every website you sign up for, on your own domain, so when DataBreach #4,729 happens you can trace exactly who sold your email and nuke that alias without touching your real inbox.

That’s email aliasing. And it’s a completely different — and significantly saner — problem.

Why Aliasing Beats the Full Server

Full self-hosted email is a two-part problem: receiving mail and sending mail. Receiving is honestly fine. Your server gets an MX record, accepts connections on port 25, done. Not trivial, but doable.

Sending is where everything catches fire.

Deliverability to Gmail, Outlook, and Yahoo is gated behind a wall of reputation signals: IP warmup history, DMARC/DKIM/SPF alignment, reverse PTR records that match your hostname, and the general goodwill of major providers who can reject your mail for vibes alone. Commercial email services like Postmark and AWS SES exist specifically because building and maintaining sender reputation is a full-time job.

Aliasing sidesteps all of this. The aliasing server receives mail on your domain and forwards it to your real account (Gmail, Proton, Fastmail — whatever you already trust). You never need to send cold mail from your own IP. When you reply through an alias, the aliasing layer rewrites headers so your real address stays hidden, then routes the outbound message through a configured SMTP relay with proper reputation.

Your job: set up MX records, configure SPF/DKIM/DMARC, and point a working SMTP relay at your aliasing server. That’s it.

The Two Contenders

Two projects dominate the self-hostable aliasing space.

SimpleLogin — Proton acquired this in 2022, which either reassures you or makes you suspicious depending on your threat model. The good news: it’s still open source and fully self-hostable. It has polished mobile apps, browser extensions for every major browser, and a web UI that doesn’t look like it was designed by a sysadmin on their lunch break. SimpleLogin supports sending through aliases (reply and compose), custom domains, PGP encryption between server and registered users, and multiple inboxes per account. The self-hosted path uses Docker and is reasonably well-documented.

addy.io (formerly AnonAddy) — Pure open source, community-driven, no acquisition story to worry about. The self-hosted setup is similarly Docker-based. The web UI is solid; mobile apps exist but are less polished than SimpleLogin’s. addy.io supports multiple aliases per recipient, custom domains, bandwidth-based limits (configurable when self-hosting), and similar reply-through-alias behavior. If you’re already paying for Proton services and want SimpleLogin’s hosted tier “for free,” you get it. If you want no corporate parent anywhere near your privacy stack, addy.io is your call.

Honestly? Both are good. The architectural difference is mostly vibes and ecosystem preferences.

Architecture in 30 Seconds

Spammy Website → sends to [email protected]
MX record → your server running SimpleLogin or addy.io
Aliasing layer looks up alias → routes to [email protected]
Gmail receives forwarded mail (sender: [email protected])
You reply → aliasing layer rewrites From → Spammy Website sees alias, not Gmail

Your real address never leaves the aliasing server. When a site gets breached, you disable that one alias. The rest of your mail is unaffected.

Self-Hosting SimpleLogin

DNS First

Before you touch Docker, get DNS right. You need:

MX record — points your domain’s mail to your server:

yourdomain.com. MX 10 mail.yourdomain.com.
mail.yourdomain.com. A YOUR_SERVER_IP

SPF — tells receivers who’s allowed to send mail from your domain:

yourdomain.com. TXT "v=spf1 ip4:YOUR_SERVER_IP -all"

If you’re routing outbound through Postmark, add their include:

yourdomain.com. TXT "v=spf1 ip4:YOUR_SERVER_IP include:spf.mtasv.net -all"

DKIM — cryptographic signature proving the mail came from your server. SimpleLogin generates a keypair on first run. You’ll grab the public key from the container and create a TXT record:

dkim._domainkey.yourdomain.com. TXT "v=DKIM1; k=rsa; p=MIGfMA0GCS..."

DMARC — tells receivers what to do with mail that fails SPF/DKIM checks:

_dmarc.yourdomain.com. TXT "v=DMARC1; p=quarantine; rua=mailto:[email protected]"

Start with p=quarantine instead of p=reject until you’re confident everything is aligned.

PTR record — the reverse DNS entry mapping your server IP back to your hostname. This one’s on your VPS provider’s control panel, not your DNS host. Set YOUR_SERVER_IP → mail.yourdomain.com. Most providers let you do this from the dashboard. Some budget providers don’t. If your provider won’t give you PTR control, your deliverability will be limited — use a proper SMTP relay for outbound.

DKIM Key Generation

Before writing your compose file, generate the keypair SimpleLogin expects:

Terminal window
mkdir -p /opt/simplelogin/dkim
openssl genrsa -out /opt/simplelogin/dkim/dkim.key 2048
openssl rsa -in /opt/simplelogin/dkim/dkim.key -pubout -out /opt/simplelogin/dkim/dkim.pub.key

Grab the public key content (strip the header/footer lines, join into one line) — that’s what goes in your DKIM TXT record.

Docker Compose

docker-compose.yml
services:
postgres:
image: postgres:15-alpine
restart: unless-stopped
environment:
POSTGRES_USER: simplelogin
POSTGRES_PASSWORD: changeme_strong_password
POSTGRES_DB: simplelogin
volumes:
- ./postgres-data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- ./redis-data:/data
app:
image: simplelogin/app:latest
restart: unless-stopped
depends_on:
- postgres
- redis
env_file:
- .env
ports:
- "7777:7777"
volumes:
- ./dkim:/dkim
- ./upload:/code/static/upload
email-handler:
image: simplelogin/app:latest
restart: unless-stopped
command: ["python", "email_handler.py"]
depends_on:
- postgres
- redis
env_file:
- .env
ports:
- "20381:20381"
volumes:
- ./dkim:/dkim

Environment Config

.env
# Domain config
URL=https://app.yourdomain.com
EMAIL_DOMAIN=yourdomain.com
ALIAS_DOMAINS=yourdomain.com
SUPPORT_EMAIL=[email protected]
# Database
DB_URI=postgresql://simplelogin:changeme_strong_password@postgres/simplelogin
# Redis
REDIS_URL=redis://redis:6379/0
# Secret key — generate with: python3 -c "import secrets; print(secrets.token_hex(32))"
SECRET_KEY=your_generated_secret_key_here
# DKIM
DKIM_PRIVATE_KEY_PATH=/dkim/dkim.key
DKIM_DNS_VALUE=dkim._domainkey.yourdomain.com
# Email handler
EMAIL_HANDLER_HOST=email-handler
EMAIL_HANDLER_PORT=20381
# Outbound SMTP — use Postmark, Mailgun, or similar
POSTFIX_SERVER=smtp.postmarkapp.com
POSTFIX_PORT=587
POSTFIX_USERNAME=your-postmark-api-token
POSTFIX_PASSWORD=your-postmark-api-token
# App settings
DISABLE_REGISTRATION=false
DISABLE_ONBOARDING=false

One reality check: POSTFIX_SERVER here isn’t actually Postfix. SimpleLogin’s naming is a bit loose — it’s just an SMTP relay endpoint. Point it at Postmark, Mailgun, AWS SES, or whatever you’re using for legitimate outbound. Plan for about $1-2/month for light usage on most providers.

Initialize and Run

Terminal window
docker compose up -d postgres redis
# Wait a few seconds for postgres to initialize
docker compose run --rm app flask db upgrade
docker compose up -d

Point your reverse proxy (Caddy, Nginx, Traefik) at port 7777 for the web UI. Make sure your MX record’s port 25 reaches the email-handler container on port 20381 — most reverse proxies don’t handle SMTP, so you’ll need to forward port 25 → 20381 directly at the host level or via your firewall.

Terminal window
# On the host, if using ufw or iptables:
iptables -t nat -A PREROUTING -p tcp --dport 25 -j REDIRECT --to-port 20381

Or just expose port 25 in Docker and map it to 20381 in the compose file if SimpleLogin is the only mail service on the host.

Self-Hosting addy.io

Compose Setup

docker-compose.yml
services:
addy:
image: anonaddy/anonaddy:latest
restart: unless-stopped
depends_on:
- postgres
env_file:
- .env
ports:
- "8080:8080"
volumes:
- ./storage:/var/www/html/storage
addy-worker:
image: anonaddy/anonaddy:latest
restart: unless-stopped
command: ["php", "artisan", "queue:work", "--tries=3"]
depends_on:
- postgres
env_file:
- .env
postgres:
image: postgres:15-alpine
restart: unless-stopped
environment:
POSTGRES_USER: addy
POSTGRES_PASSWORD: changeme
POSTGRES_DB: addy
volumes:
- ./postgres-data:/var/lib/postgresql/data

addy.io Environment

.env
APP_NAME=addy
APP_ENV=production
APP_KEY=base64:your_generated_key_here
APP_URL=https://addy.yourdomain.com
DB_CONNECTION=pgsql
DB_HOST=postgres
DB_PORT=5432
DB_DATABASE=addy
DB_USERNAME=addy
DB_PASSWORD=changeme
# Mail receiving
ANONADDY_DOMAIN=yourdomain.com
ANONADDY_HOSTNAME=mail.yourdomain.com
# Outbound SMTP relay
MAIL_MAILER=smtp
MAIL_HOST=smtp.postmarkapp.com
MAIL_PORT=587
MAIL_USERNAME=your-postmark-token
MAIL_PASSWORD=your-postmark-token
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=[email protected]
# Limits (generous for self-hosted)
ANONADDY_BANDWIDTH_LIMIT=10GB
ANONADDY_ALIAS_LIMIT=0

DNS setup for addy.io is identical to SimpleLogin — same MX, SPF, DKIM, DMARC structure. addy.io uses Laravel’s key generation:

Terminal window
docker compose run --rm addy php artisan key:generate
docker compose run --rm addy php artisan migrate --force
docker compose up -d

Adding Your Custom Domain in addy.io

Once the app is running, log in, go to Domains, add yourdomain.com. addy.io will show you the exact DNS records it needs and verify them. This is where you set up the catch-all behavior: any address *@yourdomain.com routes through addy’s alias table and forwards to your registered inbox.

Replying Through Your Alias

This is the feature that makes the whole thing actually usable. Without it, you could receive mail through an alias but you’d have to reply from your real address, instantly exposing it.

Both SimpleLogin and addy.io handle this with a header-rewriting trick. When a forwarded email arrives in your real inbox, the Reply-To header is set to a special bounce address on your aliasing server (something like [email protected]). When you reply, your real mail client sends to that address. The aliasing server receives it, decodes the original sender, rewrites the From header to your alias, and delivers the reply through your SMTP relay.

From the recipient’s side: they see a reply from [email protected]. Your Gmail address is never in the headers. It’s not magic; it’s just header manipulation. But it works.

Threat Model: When Does Aliasing Actually Help?

Aliasing is good at one specific thing: isolating your real email address from third parties you don’t fully trust.

Aliasing is not a full anonymity solution. Your aliasing server knows your real address. If someone gets access to it, they see everything. And metadata — timing, volume, IP in the received headers — can still leak information even when the address itself is hidden.

If your threat model involves “keep my email private from sophisticated adversaries with legal authority,” you need Tor-accessed Proton with no recovery options, not a self-hosted forwarder. But for “I don’t want Booking.com knowing my real address” — aliasing is exactly right.

The Backup Problem (And Why It Matters More Than You Think)

Here’s the failure mode nobody talks about when they’re setting up a cool self-hosted aliasing server: your server dies.

If you’re using hosted SimpleLogin via Proton ($4/month or free with a Proton plan), your aliases survive because they’re on Proton’s infrastructure. If you’re self-hosting and your VPS gets the boot, your database is unrecoverable, or you forget to renew your domain — every alias you’ve ever given out stops working simultaneously.

You’ve just effectively deleted your email address for every account that uses those aliases.

The mitigation:

Terminal window
# SimpleLogin: backup postgres
docker compose exec postgres pg_dump -U simplelogin simplelogin | gzip > /backup/sl-$(date +%Y%m%d).sql.gz
# addy.io: backup postgres
docker compose exec postgres pg_dump -U addy addy | gzip > /backup/addy-$(date +%Y%m%d).sql.gz

Schedule that. Automate it. Test your restore process at least once. Your 2 AM self who just lost their VPS will appreciate it.

Alternatively: if reliability matters more than control, just pay Proton for hosted SimpleLogin and skip the ops burden. Four dollars a month buys you someone else’s uptime guarantee and a team of people who care about DKIM alignment more than you want to.

Should You Bother?

Yes, if:

Probably not if:

The honest take: aliasing on your own domain is one of the most practical privacy tools available to a home lab person. It’s not glamorous, it doesn’t require explaining threat models to normies, and it just works once it’s set up. SimpleLogin and addy.io are both solid choices. The difference is whether you care more about polish and apps (SimpleLogin) or pure open-source independence (addy.io).

Pick one, set it up on a Saturday afternoon, and next time some website requires your email to download a whitepaper, give them [email protected]. When the spam starts, you’ll know exactly who to blame — and you’ll be one click away from making it stop.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Next Post
mdadm Day-2: Grow, Replace, Scrub

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts