133 lines
5.4 KiB
Markdown
133 lines
5.4 KiB
Markdown
# Docker Postfix SMTP Relay
|
|
|
|
Source: https://git.k2patel.in/k2patel/docker-postfix
|
|
|
|
`k2patel/postfix:latest` is the single maintained image: a distroless SMTP relay
|
|
built from Alpine 3.24/musl into a `scratch` runtime. It supports Maileroo,
|
|
Mailtrap, SendGrid, Gmail, Office 365, and generic upstream SMTP servers.
|
|
There is no shell, BusyBox, package manager, Supervisor, or compiler in the image.
|
|
|
|
A native launcher configures Postfix, creates queue directories on fresh volumes,
|
|
builds the SASL credential map, and starts Postfix in the foreground. Logs go to
|
|
container stdout. Native Postfix tools remain available for queue administration.
|
|
|
|
## Quick start
|
|
|
|
```sh
|
|
git clone https://git.k2patel.in/k2patel/docker-postfix.git
|
|
cd docker-postfix
|
|
cp env.sample .env
|
|
# Edit .env with your provider credentials and domain.
|
|
docker compose pull
|
|
docker compose up -d
|
|
```
|
|
|
|
To build locally, run `docker compose build` first. Builds include isolated
|
|
SMTP relay tests; no test messages are sent to external mailboxes.
|
|
|
|
Configure applications to connect to `postfix-relay:25` on the same Docker
|
|
network. Only trusted networks should be allowed to relay.
|
|
|
|
## Configuration
|
|
|
|
| Variable | Default | Purpose |
|
|
|----------|---------|---------|
|
|
| `SMTP_SERVER` | Required | Upstream SMTP hostname |
|
|
| `SMTP_PORT` | `587` | Upstream SMTP port |
|
|
| `SMTP_USERNAME` | Required | Upstream SMTP username |
|
|
| `SMTP_PASSWORD` | Required | Upstream SMTP password or token |
|
|
| `DOMAIN` | Hostname suffix, otherwise `localdomain` | Domain for outgoing mail; set explicitly |
|
|
| `SERVER_HOSTNAME` | Container hostname | Relay hostname; set to a valid FQDN |
|
|
| `TIMEZONE` | `America/New_York` | IANA timezone |
|
|
| `LOCAL_NETWORK` | Detected IPv4 interface subnet | Trusted local CIDR |
|
|
| `SMTP_NETWORKS` | Empty | Additional trusted IPv4 CIDRs, comma-separated |
|
|
| `SMTP_HEADER_TAG` | Empty | Optional `RelayTag` header value |
|
|
| `SMTP_LISTEN_PORT` | `25` | Host port in Compose |
|
|
| `DATA_FOLDER` | Current directory | Compose bind-mount base for logs/mail/spool |
|
|
| `DEBUG` | `no` | Enable Postfix debug level 2 |
|
|
|
|
Localhost and the local subnet are trusted. Invalid CIDRs and multiline
|
|
configuration values are rejected. Credentials are stored in root-only files;
|
|
the launcher does not print their contents.
|
|
|
|
Maileroo, Mailtrap, and SendGrid require STARTTLS. Generic providers retain
|
|
opportunistic STARTTLS (`smtp_tls_security_level=may`). Use the provider's
|
|
STARTTLS submission port, typically 587; implicit TLS on port 465 is not configured.
|
|
|
|
Provider examples are included in `env.sample`. For SendGrid, use the literal
|
|
username `apikey`. Gmail requires an app password. Keep credentials in `.env`,
|
|
which is excluded from Git and the Docker build context.
|
|
|
|
## Updating an existing deployment
|
|
|
|
Use `k2patel/postfix:latest`, pull, and recreate the container. Existing environment
|
|
settings and `/var/spool/postfix` persistence are retained. The default Compose
|
|
file supplies the native health check. Replace an old custom `postfix status`
|
|
or shell-based health check with:
|
|
|
|
```yaml
|
|
healthcheck:
|
|
test: ["CMD", "/usr/local/bin/postfix-entrypoint", "--healthcheck"]
|
|
```
|
|
|
|
The check connects to localhost SMTP and requires a `220` greeting. The old
|
|
`postfix status`, `postfix check`, shell access, and `mail`/`mailx` commands are
|
|
not part of this image. Use native commands below. Custom pipe/alias delivery
|
|
commands that need a shell or external utilities are unsupported.
|
|
|
|
## Operations
|
|
|
|
```sh
|
|
docker logs -f postfix-relay
|
|
docker exec postfix-relay /usr/local/bin/postfix-entrypoint --healthcheck
|
|
docker exec postfix-relay /usr/local/bin/postfix-entrypoint --check
|
|
docker exec postfix-relay postconf -n
|
|
docker exec postfix-relay postqueue -p
|
|
docker exec postfix-relay postqueue -f
|
|
docker compose restart postfix
|
|
```
|
|
|
|
`--check` parses and displays the active main and master configuration; startup
|
|
also checks queue structure using `postsuper`. It does not validate upstream
|
|
credentials or external delivery.
|
|
|
|
Host-side helpers work without a shell inside the container:
|
|
|
|
```sh
|
|
cd test
|
|
make help
|
|
make status
|
|
make queue
|
|
make check
|
|
# Sends a real email only when you explicitly run this command:
|
|
make test TO=recipient@example.com
|
|
# Optional sender, subject, or container:
|
|
make test TO=recipient@example.com FROM=sender@example.com SUBJECT='Relay test' CONTAINER=postfix-relay
|
|
```
|
|
|
|
Submission to the queue is not proof of delivery. Check logs for `status=sent`
|
|
and inspect deferred messages with `postqueue -p`.
|
|
|
|
## Build and CI
|
|
|
|
Gitea Actions builds `linux/amd64` on the Kubernetes runner for pushes to `main`,
|
|
monthly on the first day, and manual dispatch. Configure repository secrets
|
|
`DOCKER_USER` and `DOCKER_TOKEN` for Docker Hub publishing. Each build uses a
|
|
temporary Buildx builder and removes its cache afterward.
|
|
|
|
The final filesystem contains Postfix and its musl/shared-library dependencies,
|
|
SASL and database plugins, ICU data, CA certificates, timezone data, and the
|
|
native launcher. Go, Python, and packaging tools are confined to the build stage.
|
|
|
|
Before publication, a separate stage tests the exact shell-free filesystem:
|
|
startup with an empty queue and stale PID file, native health checks, SMTP
|
|
submission, STARTTLS and authenticated relay to a local mock server, header
|
|
insertion, LMDB/PCRE plugins, queue draining, restart, and SIGTERM shutdown.
|
|
The final image is copied from the pristine runtime, excluding test credentials
|
|
and queue contents.
|
|
|
|
## License and support
|
|
|
|
MIT License; see [LICENSE](LICENSE).
|
|
Issues: https://git.k2patel.in/k2patel/docker-postfix/issues
|