# Postfix PostgreSQL Relay Image A Postfix SMTP relay/MTA whose relay and transport decisions are driven by PostgreSQL lookup maps instead of static config. It receives inbound mail and forwards it to an internal SMTP target after checking the recipient's domain against a database — it has no knowledge of, and makes no assumptions about, what consumes the mail downstream. This mirrors the Postfix setup described in the SimpleLogin self-hosting documentation (`postfix` + `postfix-pgsql`, a `relay_domains` PostgreSQL map, and a `transport_maps` PostgreSQL map): https://github.com/simple-login/app No domain, hostname, or database credential is hardcoded in the image. Everything is templated from environment variables by `docker-entrypoint.sh` at container startup, then Postfix is started in the foreground (`postfix start-fg`) so it runs correctly under Docker's process supervision. ## How it works On startup, the entrypoint renders three files from templates under `/etc/postfix/templates/`: - `/etc/postfix/main.cf` — core Postfix configuration. - `/etc/postfix/pgsql-relay-domains.cf` — PostgreSQL lookup that tells Postfix which domains it should relay mail for. - `/etc/postfix/pgsql-transport-maps.cf` — PostgreSQL lookup that tells Postfix where to forward accepted mail (the internal SMTP relay target). ## Environment variables ### Required | Variable | Description | Example | |----------|--------------|---------| | `MAIL_DOMAIN` | Mail domain this relay handles (`mydomain`/`myorigin` in `main.cf`). | `example.com` | | `MYHOSTNAME` | Hostname of this relay, used as Postfix's `myhostname` and as the MX target for `MAIL_DOMAIN`. | `mx1.example.com` | | `POSTGRES_HOST` | Hostname of the PostgreSQL server holding the lookup tables. | `postgres` | | `POSTGRES_PORT` | PostgreSQL port. | `5432` | | `POSTGRES_USER` | PostgreSQL user used for the lookup queries. | `postfix` | | `POSTGRES_PASSWORD` | PostgreSQL password for `POSTGRES_USER`. | `change-me` | | `POSTGRES_DB` | PostgreSQL database name. | `relaydb` | | `SMTP_RELAY_HOST` | Internal SMTP host that accepted mail is forwarded to after the relay-domains lookup matches. | `app-internal` | | `SMTP_RELAY_PORT` | Port of the internal SMTP relay target. | `2525` | ### Optional | Variable | Description | |----------|--------------| | `TLS_CERT_FILE` | Absolute path (inside the container) to a TLS certificate to use for `smtpd_tls_cert_file`. If unset, a self-signed certificate is generated on first start. | | `TLS_KEY_FILE` | Absolute path (inside the container) to the private key matching `TLS_CERT_FILE`. Must be set together with `TLS_CERT_FILE`. | The PostgreSQL lookup tables are expected to expose at least a `relay_domain(domain)` table; both lookups query it by recipient domain. ## TLS certificates By default, if `TLS_CERT_FILE`/`TLS_KEY_FILE` are not provided, the entrypoint generates a self-signed certificate at `/etc/postfix/ssl/snakeoil.{pem,key}` on first start (matching the self-signed fallback used in the upstream docs) and reuses it on subsequent restarts if that path is kept on a persistent volume. To use your own certificate instead, mount it into the container and point `TLS_CERT_FILE`/`TLS_KEY_FILE` at the mounted paths, e.g.: ```bash docker run \ -v /path/to/fullchain.pem:/certs/fullchain.pem:ro \ -v /path/to/privkey.pem:/certs/privkey.pem:ro \ -e TLS_CERT_FILE=/certs/fullchain.pem \ -e TLS_KEY_FILE=/certs/privkey.pem \ ... ``` ## Usage ### `docker run` ```bash docker run -d \ -p 25:25 \ -e MAIL_DOMAIN=example.com \ -e MYHOSTNAME=mx1.example.com \ -e POSTGRES_HOST=postgres \ -e POSTGRES_PORT=5432 \ -e POSTGRES_USER=postfix \ -e POSTGRES_PASSWORD=change-me \ -e POSTGRES_DB=relaydb \ -e SMTP_RELAY_HOST=app-internal \ -e SMTP_RELAY_PORT=2525 \ gitea.arnodo.fr/damien/postfix-pgsql:latest ``` ### Docker Compose ```yaml services: postfix: image: gitea.arnodo.fr/damien/postfix-pgsql:latest ports: - "25:25" environment: MAIL_DOMAIN: example.com MYHOSTNAME: mx1.example.com POSTGRES_HOST: postgres POSTGRES_PORT: "5432" POSTGRES_USER: postfix POSTGRES_PASSWORD: change-me POSTGRES_DB: relaydb SMTP_RELAY_HOST: app-internal SMTP_RELAY_PORT: "2525" ``` ## Version management The Debian base image tag is set in `POSTFIX_VERSION` (e.g. `12-slim`), which also pins the `postfix`/`postfix-pgsql` package versions available via `apt` for that Debian release. To change it: 1. Edit `POSTFIX_VERSION` with the desired Debian tag. 2. Commit and push. 3. The CI builds the image with that base and tags it with both `latest` and the value of `POSTFIX_VERSION`. ## Reference - Upstream Postfix + PostgreSQL setup this image is based on: https://github.com/simple-login/app