SelfPost

# SelfPost Self-hosted outbound SMTP relay with a web control panel, shipped as a single Docker image. Postfix, OpenDKIM, and a small Go panel run together under `supervisord`; you configure domains, DKIM keys, and SASL applications once, then point your apps at the SMTP endpoint. SelfPost sends mail straight to the internet from **your own IP**, with per-domain DKIM signing. It is **outbound only** — no inbound mail, mailboxes, or webmail. **For:** operators who run their own VPS or home server and want a simple relay they control, without a third-party SMTP provider. **Key properties:** one container, multi-domain DKIM, SASL per application, send log and DNS checks in the panel, encrypted backups. ## Features - Outbound SMTP (465/smtps; optional 587 submission) with per-domain DKIM signing - Web panel — domains, applications, deliveries, mail queue, system log, backup - Multi-domain relay — each SASL application is bound to one sending domain - DNS status checks (PTR, SPF, DKIM, DMARC) with in-panel re-check - Two-level rate limiting — IP backstop (Postfix) and per-domain/per-app limits - Full-server backup and single-domain export/import (optional password encryption) - Single Docker image; data in a `./data` bind mount ## Documentation | Document | Contents | |---|---| | [**Operator guide**](docs/guide.md) | Reverse proxy, environment variables, DNS, IP warmup, panel operations, rate limiting, backup/restore, ports, image tag | | [Product boundaries](docs/product.md) | Purpose, deployment assumptions, out-of-scope items, multi-domain model | | [Architecture](docs/architecture.md) | As-built technical design | | [Security](docs/security.md) | Accepted security trade-offs and requirements | | [Development](docs/development.md) | Building, testing, and contributing | | [CHANGELOG](CHANGELOG.md) | Release history | Repository: — source, issues, releases, and the `ghcr.io/mixeme/selfpost` image. ## Requirements Providing these is the operator's job — SelfPost cannot fix a blocked port or a missing PTR record for you. Details: [Operator guide](docs/guide.md). ### Platform - Docker + Compose v2 on the host - A reverse proxy in front of the panel (SelfPost never terminates HTTPS itself) - Rough sizing: **1 vCPU**, **512 MB–1 GB RAM**, **8–10 GB disk** (send log and rotated `mail.log` are the main growth drivers) ### Network and IP - [ ] Static IP address - [ ] Outbound TCP port 25 unblocked (many consumer/cloud hosts block it by default) - [ ] PTR/rDNS for that IP pointing at your mail hostname (`SELFPOST_HOSTNAME`) - [ ] Reasonable starting IP reputation — a fresh IP still needs [warmup](docs/guide.md#ip-warmup) ### Per sending domain For every domain you add in the panel: - [ ] SPF TXT record authorizing this server - [ ] DKIM TXT record (value shown on the domain page) - [ ] DMARC `_dmarc` TXT record See [DNS setup](docs/guide.md#dns-setup) in the operator guide. ## Quick start ```sh mkdir -p selfpost && cd selfpost curl -O https://raw.githubusercontent.com/mixeme/selfpost/main/deploy/docker-compose.yml curl -O https://raw.githubusercontent.com/mixeme/selfpost/main/deploy/.env.example mv .env.example .env # then edit SELFPOST_HOSTNAME etc. docker compose up -d ``` `SELFPOST_HOSTNAME` is required — bare FQDN only (e.g. `mail.example.com`). It is the Postfix HELO name, the SASL realm, and must match the PTR record and certificate CN/SAN. This starts SelfPost only. You still need a reverse proxy with TLS certificates bind-mounted at `./certs` — see [Reference deploy](#reference-deploy) and the [reverse-proxy section](docs/guide.md#reverse-proxy-mandatory) in the operator guide. On first start, `docker compose logs -f` prints a one-time setup link to create the admin account. The same link is also in `./data/setup-token` on the host — see [Operations → First-time setup link](docs/guide.md#operations). ## Reference deploy | Artefact | Path | |---|---| | Compose file (fixed image tag) | [deploy/docker-compose.yml](deploy/docker-compose.yml) | | Environment template | [deploy/.env.example](deploy/.env.example) | | Apache vhost (recommended) | [deploy/apache/selfpost-vhost.conf](deploy/apache/selfpost-vhost.conf) | | nginx | [deploy/nginx/](deploy/nginx/) | | Caddy | [deploy/caddy/](deploy/caddy/) | | Traefik | [deploy/traefik/](deploy/traefik/) | The compose file maps ports **465** (always) and **587** (when `SUBMISSION_ENABLE=true`). TLS certificate paths inside the container are fixed to match the `./certs` bind mount. Bump the pinned image tag deliberately when upgrading — never use `:latest` ([why](docs/guide.md#fixed-image-tag)). ## License [AGPL-3.0](LICENSE). The AGPL closes the "SaaS loophole": if you run a modified version as a network-accessible service, you must make the modified source available to its users — not only when you distribute copies of the code.