InkPigeon: a self-hosted, multi-tenant email service provider
A SendGrid-class platform that owns its whole mail pipeline: SMTP relay and REST sending, DKIM, direct delivery to recipient MX servers, bounces, tracking, hosted mailboxes and metered Stripe billing, on one VPS.
What it is
Customers add a domain, verify SPF, DKIM and DMARC, then send transactional mail through an SMTP relay or a REST API and run campaigns and automations. The platform delivers directly to recipient MX servers, processes bounces and complaints, tracks opens and clicks, fires webhooks, offers optional hosted mailboxes and bills through Stripe subscriptions with metered overage.
My role
Everything: an eleven-document product spec covering infrastructure, conventions, a 34-table schema, API, SMTP behaviour, billing and UI; the architecture; the implementation; CI/CD; server operations; DNS and sending reputation.
Key decisions
- A modular monolith, not microservices. One Python package with strict module boundaries, deployed as about eight containers that scale independently. On a single VPS, microservices would add failure points without adding capacity.
- PostgreSQL as the mail queue. Delivery workers claim messages with FOR UPDATE SKIP LOCKED: transactional claims, durable retry state and one fewer system to operate.
- Build versus adopt. Mail-in-a-Box, Mailu, Zimbra and Postal are mailbox or outbound-only products without a multi-tenant SaaS layer. I reuse only the two commodity components, Dovecot and Rspamd, and build everything customer-facing.
- Hosting where port 25 works. Major clouds rarely unblock outbound SMTP, so the platform runs on a VPS with Caddy for TLS, a multi-IP sending pool and reverse DNS.
- Tenant isolation and secrets as first-class rules. Every tenant table carries an organisation id and every service function takes it explicitly. Argon2id for passwords and SMTP credentials, SHA-256 for API keys, AES-GCM for DKIM private keys.
How it is run
- 55 backend test modules. CI runs Ruff, mypy and pytest against real PostgreSQL and Redis plus a Mailpit end-to-end send; merges to main deploy over SSH only if the suite passes.
- After an adversarial review found two privilege-escalation paths in the custom-roles feature, I wrote the rule set that now governs every authorisation change: map the full bypass surface, no self-service on your own access, every router's gate is an explicit decision.
- Prometheus metrics, Grafana dashboards, backup and restore scripts, a server bootstrap script and a runbook.
- Spec-first development: implementation was carried out largely in AI-assisted coding sessions governed by a written standards file, one build-plan task per commit, and acceptance criteria that must show real test output before a task counts as done.