Quickstart: bash <(curl -fsSL https://raw.githubusercontent.com/brightcolor/mailprobe/main/scripts/quickstart.sh)
MailProbe is a self-hosted email deliverability test service. It accepts test emails on temporary addresses, stores the raw message, runs transparent checks, and shows a report with score + findings.
This project is intentionally built for small VPS setups (including ~1 GB RAM environments):
- Single Go binary (HTTP + SMTP + analysis + cleanup)
- SQLite (no external database service)
- Server-rendered UI (no heavy frontend framework)
- Docker Compose deployment (no Kubernetes)
- Open the web UI
- Generate a random temporary mailbox
- Send your campaign/test email to that address
- MailProbe receives and stores the message
- MailProbe analyzes the message and creates a report
- Open the report with score, checks, warnings, and suggestions
- Random temporary mailbox addresses (
<token>@<request-host>by default, or<token>@SMTP_DOMAINwhen configured) - Score-first web UI with a guided send-and-check workflow
- New test addresses can be generated in-place without a full page reload
- Multiple active mailboxes in parallel
- Multiple emails per mailbox
- Mailbox TTL and automatic expiration
- Raw source and raw headers view
- JSON report endpoint for automation/integrations
- Vvveb Admin Template based shell and visual system (Apache-2.0 vendor assets are shipped locally)
- Score-first dashboard with a large result hero, status counters, message metadata, and grouped diagnostics
- Check groups for authentication, DNS/infrastructure, spam filters, content/format, and raw/header details
- Check accordions show only values relevant to the current check instead of repeating global message metadata everywhere
- Collapsible technical sections for long diagnostics, headers, plaintext, HTML preview, HTML source, and full raw source
- Copy-to-clipboard actions for DNS records, recommendations, headers, source, and technical values
- Remediation blocks are shown only for warning/failing checks as
Wie wird's gemacht? - Detected URLs are grouped by domain with total and per-domain counts
- Responsive layout for desktop and mobile
- Light, dark, and auto theme mode with a UI toggle;
autofollowsprefers-color-scheme
- Score from
0.0to10.0 - Non-black-box scoring model (rule deltas are visible in report)
- Report-first layout with status counters, prioritized checks, recommendations, raw views, and JSON export
- Sandboxed rendered HTML preview plus raw HTML source for received messages
- Check categories include:
- SPF (header result + DNS context)
- DKIM (signature/auth-result heuristics)
- DMARC (record + alignment heuristics)
- PTR/rDNS
- HELO/EHLO plausibility
- Envelope-From vs Header-From alignment
- Return-Path presence
- Received chain presence
- ARC presence info
- MIME structure and multipart sanity
- Plaintext/HTML presence and ratio heuristics
- Attachments detection
- Link extraction
- URL shortener and tracking marker heuristics
- Basic HTML sanity / hidden content heuristics
- Subject spam-style heuristics (caps / punctuation)
- Date header plausibility
- Message-ID presence
- Unicode obfuscation heuristics
- Newsletter hints: List-Unsubscribe / preheader heuristics
- Optional RBL checks (disabled by default)
- RBL findings include checked DNSBL zones, lookup responses, TXT evidence, impact text, pre-delisting checklist, and provider-specific delisting URLs where known
- Optional SpamAssassin integration (disabled by default)
- Optional Rspamd integration (disabled by default)
- Rspamd findings include top rejecting symbols and actionable recommendations in report output
Each check result is intentionally explainable. In addition to the legacy fields (id, name, status, score_delta, summary, suggestion), reports may include:
category: UI/report grouping, for exampleAuthentifizierungorDNS und Infrastrukturseverity:low,medium,high, orinfotechnical_details: structured key/value data used for DNS records, IPs, HELO/EHLO, headers, body metrics, RBL providers, and external filter outputexplanation: why this check matters for deliverabilityrecommendation: concrete remediation text with example DNS, MTA, or template changes where possible
This is stored in the report JSON and visible in the web UI. Existing API consumers can keep reading the old fields; the new fields are additive.
- Not a full production MTA
- Not an outbound mail relay
- Not a replacement for enterprise mailbox-provider proprietary filtering engines
cmd/mailprobe/main.go: bootstrap and service wiringinternal/smtp: lightweight SMTP receiverinternal/analyzer: parsing + checks + scoringinternal/store,internal/db: SQLite persistence layerinternal/web: SSR pages + API endpointsinternal/cleanup: periodic TTL/retention cleanup
Bundled UI vendor assets:
internal/web/static/vendor/vvveb/admin.cssinternal/web/static/vendor/vvveb/fonts/inter/*internal/web/static/vendor/vvveb/LICENSE
The Vvveb Admin Template assets are Apache-2.0 licensed. MailProbe keeps them local to avoid third-party CDN calls.
Data path:
- Web creates mailbox in SQLite
- SMTP receives message and validates recipient
- Message is stored in SQLite
- Analyzer builds report
- Report is stored and shown in UI
- Docker + Docker Compose
- Public IP VPS
- Domain/subdomain you control
- SMTP traffic routed to this host (
25 -> SMTP_PORTor direct bind)
Fully automatic (installs Docker + Docker Compose if missing, no SSL/reverse-proxy setup):
bash <(curl -fsSL https://raw.githubusercontent.com/brightcolor/mailprobe/main/scripts/quickstart.sh)The installer asks whether optional rspamd and redis services should be enabled.
Based on your choice it generates docker-compose.override.yml (instead of editing comments in-place), and updates .env flags.
Optional environment overrides for the script:
INSTALL_DIR=/opt/mailprobe \
HTTP_PORT=8080 \
SMTP_PORT=2525 \
SMTP_DOMAIN= \
PUBLIC_BASE_URL= \
ENABLE_TLS=false \
FORCE_HTTPS=false \
ENABLE_RSPAMD=false \
ENABLE_REDIS=false \
bash <(curl -fsSL https://raw.githubusercontent.com/brightcolor/mailprobe/main/scripts/quickstart.sh)Manual setup:
cp .env.example .env
# edit: MAILPROBE_IMAGE, ports, optional TLS/proxy settings
docker compose pull
docker compose up -dOpen UI:
http://<host>:8080(or your reverse-proxy URL)
Example records:
A mailprobe.example.org -> <server-ip>MX mx-test.example.org 10 mailprobe.example.org
Recommended runtime setup:
- Keep web behind reverse proxy/TLS (
443 -> 8080) - Route SMTP
25to container SMTP port (2525by default) - If
SMTP_DOMAINis empty, open the UI through the same DNS name you want in generated test addresses.
See examples:
deploy/examples/nginx.confdeploy/examples/Caddyfiledeploy/examples/docker-compose.rspamd.ymldeploy/examples/docker-compose.spamassassin.yml
Copy .env.example and adjust.
Important variables:
MAILPROBE_IMAGE(default:ghcr.io/brightcolor/mailprobe:latest; pin a version tag for production)PUBLIC_BASE_URL(optional override; leave empty to derive scheme + host from the request)SMTP_DOMAIN(optional override; leave empty to use the request host for generated mailbox domains)ENABLE_TLS,TLS_CERT_FILE,TLS_KEY_FILE(optional direct TLS for the built-in web server)FORCE_HTTPS(redirect HTTP requests to HTTPS when the app receives plain HTTP traffic)HTTP_PORT,SMTP_PORTHEALTHCHECK_URLMAX_MESSAGE_BYTESMAILBOX_TTLDATA_RETENTION_TTLCLEANUP_INTERVALMAX_ACTIVE_MAILBOXES_PER_IPMAX_ACTIVE_MAILBOXES_GLOBALWEB_RATE_LIMIT_PER_MINWEB_BURST_PER_10_SECTRUSTED_PROXY_CIDRS(only these proxy CIDRs may supplyX-Forwarded-For)SMTP_RATE_LIMIT_PER_HOURSMTP_BURST_PER_MINENABLE_RBL_CHECKS,RBL_PROVIDERS(default:zen.spamhaus.org,bl.spamcop.net,b.barracudacentral.org,psbl.surriel.com,dnsbl.dronebl.org,bl.blocklist.de)ENABLE_SPAMASSASSIN,SPAMASSASSIN_HOSTPORTENABLE_RSPAMD,RSPAMD_URL,RSPAMD_PASSWORDALERT_WEBHOOK_URL(optional outbound webhook for operational alerts)
By default, MailProbe no longer needs PUBLIC_BASE_URL or SMTP_DOMAIN in .env.
The web server derives its public URL from the incoming request:
- direct access:
Hostand whether the request is TLS - trusted proxy access:
X-Forwarded-Proto,X-Forwarded-Host, or RFCForwardedheaders, but only when the direct proxy IP matchesTRUSTED_PROXY_CIDRS
Generated addresses use the detected request host, with the port removed.
Example: opening https://probe.example.org generates addresses like <token>@probe.example.org.
Set SMTP_DOMAIN when your SMTP receiving domain differs from the web host, for example:
SMTP_DOMAIN=mx-test.example.orgSMTP is still not an open relay: the receiver only accepts addresses that already exist as active temporary mailboxes.
The web server can terminate TLS directly when you do not want a reverse proxy:
ENABLE_TLS=true
TLS_CERT_FILE=/certs/fullchain.pem
TLS_KEY_FILE=/certs/privkey.pem
FORCE_HTTPS=false
HEALTHCHECK_URL=https://127.0.0.1:8080/healthzMount the certificate directory in docker-compose.yml, for example:
volumes:
- mailprobe_data:/data
- ./certs:/certs:roNotes:
- Certificate files are only required when
ENABLE_TLS=true. - With
ENABLE_TLS=true, the configured HTTP listener speaks HTTPS directly. FORCE_HTTPS=trueis mainly useful when MailProbe receives plain HTTP behind a trusted reverse proxy or when running HTTP-only and redirecting users to the public HTTPS URL.- For reverse proxy deployments, keep
ENABLE_TLS=false, terminate TLS at the proxy, and setTRUSTED_PROXY_CIDRSso forwarded scheme/host headers are trusted.
Implemented safeguards:
- No open relay behavior
- SMTP recipient validation against active temporary mailboxes
- Request and SMTP rate limits
- Maximum accepted message size
- Max active mailboxes per client IP
- TTL-based data lifecycle
Operational recommendations:
- Restrict host firewall to required ports
- Use reverse proxy with TLS for web access
- Keep
.envprivate and backed up securely - Run regular image updates
Data is persisted in Docker volume mailprobe_data:
- SQLite database (
/data/mailprobe.db+ WAL/SHM)
Backup options:
- Volume snapshot
- Periodic DB copy/export during low activity windows
Health endpoints:
GET /healthzGET /readyzGET /metrics(Prometheus text format)
Report API:
GET /api/reports/<mailbox-token>/<message-ref>returns mailbox metadata, message metadata, and the full analysis report as JSON.
Useful commands:
docker compose ps
docker compose logs -f mailprobeGitHub Actions workflows are included:
.github/workflows/ci.yml- runs
go test ./... - builds multi-arch image (
linux/amd64,linux/arm64) - publishes to GHCR on
mainand tags (v*)
- runs
.github/workflows/release.yml- optional manual tag creation
Published image target:
ghcr.io/brightcolor/mailprobe:<tag>
Image tag strategy:
latest: newest image frommainmain: newest image frommain, same moving channel aslatestsha-<shortsha>: immutable image for every pushed commitvX.Y.Z: immutable release tag, created by.github/workflows/release.ymlX.Y.Z,X.Y,X: SemVer aliases created fromvX.Y.Ztags
Recommended production pin:
MAILPROBE_IMAGE=ghcr.io/brightcolor/mailprobe:v0.1.1
docker compose pull
docker compose up -dRollback to an older image:
MAILPROBE_IMAGE=ghcr.io/brightcolor/mailprobe:v0.1.0
docker compose pull
docker compose up -dUse a sha-<shortsha> tag when you need an exact commit build instead of a named release.
For standard usage, this is intended to run on small servers. Current compose limits are conservative:
mem_limit: 512mcpus: 0.50
Optional checks (RBL, SpamAssassin, Rspamd) increase resource usage and latency.
- DKIM verification is heuristic-oriented (not full cryptographic verifier depth)
- SPF/DMARC outcomes rely on available headers + DNS lookups, not full receiver-grade policy pipeline
- Single-node design (no built-in clustering/HA)
- Stronger DKIM verification path
- More rule configurability via external config
- Auth-protected/private deployment mode
- Report export formats
MIT (see LICENSE).