Publish Alpine musl distroless Postfix as the sole latest image
Build and Push Docker Image / build (push) Successful in 1m25s
Build and Push Docker Image / build (push) Successful in 1m25s
This commit is contained in:
1 parent
2a9a7c6baa
commit
7ead00647b
12 files changed
+776
-1274
No files matched your search
@@ -1,465 +1,132 @@
|
||||
# Docker Postfix SMTP Relay
|
||||
|
||||
A lightweight Postfix SMTP relay container supporting multiple email service providers including Maileroo, Mailtrap, SendGrid, and any generic SMTP server.
|
||||
Source: https://git.k2patel.in/k2patel/docker-postfix
|
||||
|
||||
The image uses Alpine 3.24 (musl libc) and is published as `k2patel/postfix:latest`.
|
||||
Builds run on Gitea for pushes to `main`, monthly, or by manual dispatch.
|
||||
`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.
|
||||
|
||||
## Features
|
||||
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.
|
||||
|
||||
- 🚀 Multi-provider support (Maileroo, Mailtrap, SendGrid, Gmail, Office365, any SMTP)
|
||||
- 🔒 TLS/SSL encryption
|
||||
- 🌐 Auto-detects /16 subnet for relay
|
||||
- 📝 All logs to stderr (`docker logs`)
|
||||
- 🏥 Built-in health checks
|
||||
- 🔧 Simple Makefile-based operations
|
||||
## Quick start
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Clone and Configure
|
||||
|
||||
```bash
|
||||
```sh
|
||||
git clone https://git.k2patel.in/k2patel/docker-postfix.git
|
||||
cd docker-postfix
|
||||
cp env.sample .env
|
||||
nano .env # Edit with your SMTP provider details
|
||||
# Edit .env with your provider credentials and domain.
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### 2. Build and Start
|
||||
To build locally, run `docker compose build` first. Builds include isolated
|
||||
SMTP relay tests; no test messages are sent to external mailboxes.
|
||||
|
||||
```bash
|
||||
cd test
|
||||
make build
|
||||
make up
|
||||
```
|
||||
|
||||
### 3. Test
|
||||
|
||||
```bash
|
||||
cd test
|
||||
make test TO=your@email.com
|
||||
```
|
||||
Configure applications to connect to `postfix-relay:25` on the same Docker
|
||||
network. Only trusted networks should be allowed to relay.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
| 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 |
|
||||
|
||||
| Variable | Required | Default | Description |
|
||||
|----------|----------|---------|-------------|
|
||||
| `SMTP_SERVER` | Yes | - | SMTP server hostname |
|
||||
| `SMTP_PORT` | Yes | 587 | SMTP server port |
|
||||
| `SMTP_USERNAME` | Yes | - | SMTP username |
|
||||
| `SMTP_PASSWORD` | Yes | - | SMTP password |
|
||||
| `DOMAIN` | Yes | - | Domain for outgoing mail |
|
||||
| `SERVER_HOSTNAME` | No | Auto | Server FQDN |
|
||||
| `TIMEZONE` | No | America/New_York | Timezone |
|
||||
| `SMTP_NETWORKS` | No | Auto /16 | Additional networks (comma-separated CIDR) |
|
||||
| `SMTP_LISTEN_PORT` | No | 25 | Port to expose on host |
|
||||
| `LOCAL_NETWORK` | No | Auto-detected | Override local network CIDR (e.g., 192.168.0.0/16) |
|
||||
| `DEBUG` | No | no | Enable debug logging |
|
||||
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.
|
||||
|
||||
### Provider Examples
|
||||
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.
|
||||
|
||||
#### Maileroo
|
||||
```bash
|
||||
SMTP_SERVER=smtp.maileroo.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USERNAME=noreply@example.com
|
||||
SMTP_PASSWORD=your_password
|
||||
DOMAIN=example.com
|
||||
```
|
||||
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.
|
||||
|
||||
#### Mailtrap
|
||||
```bash
|
||||
SMTP_SERVER=live.smtp.mailtrap.io
|
||||
SMTP_PORT=587
|
||||
SMTP_USERNAME=your_username
|
||||
SMTP_PASSWORD=your_password
|
||||
DOMAIN=example.com
|
||||
```
|
||||
## Updating an existing deployment
|
||||
|
||||
#### SendGrid
|
||||
```bash
|
||||
SMTP_SERVER=smtp.sendgrid.net
|
||||
SMTP_PORT=587
|
||||
SMTP_USERNAME=apikey
|
||||
SMTP_PASSWORD=your_api_key
|
||||
DOMAIN=example.com
|
||||
```
|
||||
|
||||
**Note**: For SendGrid, username must be `apikey` (literal string).
|
||||
|
||||
#### Gmail
|
||||
```bash
|
||||
SMTP_SERVER=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USERNAME=your_email@gmail.com
|
||||
SMTP_PASSWORD=your_app_password
|
||||
DOMAIN=gmail.com
|
||||
```
|
||||
|
||||
## Makefile Commands
|
||||
|
||||
All operations are handled through the Makefile in the `test/` directory.
|
||||
|
||||
### Setup Commands
|
||||
|
||||
```bash
|
||||
cd test
|
||||
|
||||
make build # Build the Docker image
|
||||
make up # Start the container
|
||||
make down # Stop the container
|
||||
make restart # Restart the container
|
||||
```
|
||||
|
||||
### Monitoring Commands
|
||||
|
||||
```bash
|
||||
make logs # View logs (follow mode)
|
||||
make logs-tail # View last 100 lines
|
||||
make status # Show container and Postfix status
|
||||
make queue # View mail queue
|
||||
make health # Check container health
|
||||
```
|
||||
|
||||
### Maintenance Commands
|
||||
|
||||
```bash
|
||||
make shell # Open shell in container
|
||||
make config # Show Postfix configuration
|
||||
make test # Send test email (auto-detects container)
|
||||
make flush # Flush mail queue
|
||||
make check # Validate Postfix configuration
|
||||
```
|
||||
|
||||
### Cleanup Commands
|
||||
|
||||
```bash
|
||||
make clean # Remove container and volumes
|
||||
make clean-all # Remove everything including images
|
||||
```
|
||||
|
||||
### Advanced Commands
|
||||
|
||||
```bash
|
||||
make debug # Start with debug mode enabled
|
||||
make validate # Validate .env file
|
||||
make queue-delete # Delete all messages in queue
|
||||
make stats # Show real-time container stats
|
||||
```
|
||||
|
||||
### Full Command List
|
||||
|
||||
Run `make help` or just `make` to see all available commands:
|
||||
|
||||
```bash
|
||||
cd test
|
||||
make
|
||||
```
|
||||
|
||||
## Network Configuration
|
||||
|
||||
The container automatically allows relay from:
|
||||
- Localhost (127.0.0.0/8)
|
||||
- Auto-detected /16 subnet
|
||||
|
||||
To add custom networks:
|
||||
```bash
|
||||
SMTP_NETWORKS=192.168.1.0/24,10.0.0.0/8
|
||||
```
|
||||
|
||||
## Using with Applications
|
||||
|
||||
Configure your application to use the relay:
|
||||
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
|
||||
services:
|
||||
your-app:
|
||||
environment:
|
||||
MAIL_HOST: postfix-relay
|
||||
MAIL_PORT: 25
|
||||
depends_on:
|
||||
- postfix
|
||||
healthcheck:
|
||||
test: ["CMD", "/usr/local/bin/postfix-entrypoint", "--healthcheck"]
|
||||
```
|
||||
|
||||
Connection details:
|
||||
- **Host**: `postfix-relay`
|
||||
- **Port**: `25`
|
||||
- **Authentication**: Not required for local network
|
||||
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.
|
||||
|
||||
## Testing
|
||||
## Operations
|
||||
|
||||
### Quick Test (Recommended)
|
||||
```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
|
||||
```
|
||||
|
||||
The easiest way to test your Postfix relay:
|
||||
`--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.
|
||||
|
||||
```bash
|
||||
Host-side helpers work without a shell inside the container:
|
||||
|
||||
```sh
|
||||
cd test
|
||||
make test TO=recipient@example.com
|
||||
```
|
||||
|
||||
**That's it!** No configuration needed.
|
||||
|
||||
### What Gets Auto-Detected?
|
||||
|
||||
The test script automatically finds and configures:
|
||||
|
||||
| Feature | What Happens | Override Option |
|
||||
|---------|--------------|-----------------|
|
||||
| **Container Name** | Finds containers using `docker-postfix-postfix` image | Specify as first arg |
|
||||
| **FROM Address** | Uses `noreply@DOMAIN` from container env | `FROM=email@domain.com` |
|
||||
| **SMTP Port** | Detects mapped port (e.g., `25` or custom) | N/A (always detected) |
|
||||
| **Host IP** | Converts `0.0.0.0` → `127.0.0.1` for local testing | N/A (always detected) |
|
||||
| **Subject** | Defaults to "Test Email from Postfix Relay" | `SUBJECT="Your Subject"` |
|
||||
|
||||
**No manual configuration required** - just provide the recipient email!
|
||||
|
||||
### Advanced Testing Options
|
||||
|
||||
**With custom FROM address:**
|
||||
```bash
|
||||
make test TO=user@example.com FROM=noreply@mydomain.com
|
||||
```
|
||||
|
||||
**With custom subject:**
|
||||
```bash
|
||||
make test TO=user@example.com FROM=sender@domain.com SUBJECT="My Test Email"
|
||||
```
|
||||
|
||||
**Using the test script directly:**
|
||||
```bash
|
||||
./test-email.sh user@example.com
|
||||
./test-email.sh user@example.com sender@mydomain.com
|
||||
./test-email.sh user@example.com sender@mydomain.com "Custom Subject"
|
||||
```
|
||||
|
||||
**Manual container specification (if auto-detection fails):**
|
||||
```bash
|
||||
./test-email.sh my-container user@example.com sender@domain.com
|
||||
```
|
||||
|
||||
### Test Script Features
|
||||
|
||||
The `test-email.sh` script automatically:
|
||||
- Finds containers using `docker-postfix-postfix` image
|
||||
- Detects port mapping and converts `0.0.0.0` to `127.0.0.1`
|
||||
- Uses proper SMTP protocol via sendmail
|
||||
- Falls back to mail command if needed
|
||||
- Shows queue status after sending
|
||||
- Provides colored output for easy reading
|
||||
|
||||
### Validation Tests
|
||||
|
||||
```bash
|
||||
cd test
|
||||
make check # Validate Postfix configuration
|
||||
make validate # Validate .env file
|
||||
make queue # View mail queue
|
||||
make config # Show current Postfix config
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Email Not Received
|
||||
|
||||
**1. Check container logs:**
|
||||
```bash
|
||||
cd test
|
||||
make logs-tail # Last 100 lines
|
||||
make logs # Follow in real-time
|
||||
```
|
||||
|
||||
**2. Check mail queue:**
|
||||
```bash
|
||||
make queue # View queued messages
|
||||
make flush # Force processing
|
||||
```
|
||||
|
||||
**3. Verify configuration:**
|
||||
```bash
|
||||
make config # Show Postfix config
|
||||
make check # Validate config
|
||||
make validate # Validate .env file
|
||||
```
|
||||
|
||||
### SMTP Protocol Errors
|
||||
|
||||
If you see "improper command pipelining" errors:
|
||||
|
||||
**Use the test script** (handles protocol correctly):
|
||||
```bash
|
||||
./test-email.sh user@example.com sender@domain.com
|
||||
```
|
||||
|
||||
**Verify FROM domain matches DOMAIN setting:**
|
||||
```bash
|
||||
# In .env file
|
||||
DOMAIN=mydomain.com
|
||||
|
||||
# Use matching FROM address
|
||||
make test TO=user@example.com FROM=noreply@mydomain.com
|
||||
```
|
||||
|
||||
**Check mynetworks configuration:**
|
||||
```bash
|
||||
docker exec postfix-relay postconf mynetworks
|
||||
```
|
||||
|
||||
### Container Not Auto-Detected
|
||||
|
||||
**1. Verify container is running:**
|
||||
```bash
|
||||
docker ps | grep postfix
|
||||
```
|
||||
|
||||
**2. Start the container:**
|
||||
```bash
|
||||
cd test
|
||||
make up
|
||||
```
|
||||
|
||||
**3. Manually specify container:**
|
||||
```bash
|
||||
./test-email.sh my-container-name user@example.com
|
||||
```
|
||||
|
||||
### Enable Debug Mode
|
||||
|
||||
For detailed SMTP transaction logs:
|
||||
```bash
|
||||
cd test
|
||||
make debug
|
||||
```
|
||||
|
||||
### Emails Stuck in Queue
|
||||
|
||||
```bash
|
||||
cd test
|
||||
make queue # View queue
|
||||
make flush # Force processing
|
||||
make queue-delete # Delete all (with confirmation)
|
||||
```
|
||||
|
||||
### Network Configuration Issues
|
||||
|
||||
**Override auto-detected network:**
|
||||
```bash
|
||||
# In .env file
|
||||
LOCAL_NETWORK=192.168.1.0/24
|
||||
```
|
||||
|
||||
**Add additional networks:**
|
||||
```bash
|
||||
SMTP_NETWORKS=10.0.0.0/8,172.16.0.0/12
|
||||
```
|
||||
|
||||
### Container Health Issues
|
||||
|
||||
```bash
|
||||
cd test
|
||||
make status # Check status
|
||||
make health # Check health status
|
||||
make restart # Restart container
|
||||
make down # Stop container
|
||||
make up # Start fresh
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Testing Examples
|
||||
|
||||
**Simple test with Gmail:**
|
||||
```bash
|
||||
cd test
|
||||
make test TO=youraddress@gmail.com
|
||||
# FROM will auto-detect from DOMAIN in .env
|
||||
```
|
||||
|
||||
**Test with custom FROM and subject:**
|
||||
```bash
|
||||
make test TO=client@example.com FROM=support@mycompany.com SUBJECT="Production Test"
|
||||
```
|
||||
|
||||
**Test from command line:**
|
||||
```bash
|
||||
./test-email.sh user@example.com noreply@mydomain.com "Hello World"
|
||||
```
|
||||
|
||||
**Verify email was sent:**
|
||||
```bash
|
||||
make queue # Should show empty queue if sent
|
||||
make logs-tail # Check for "status=sent"
|
||||
```
|
||||
|
||||
### Basic Workflow
|
||||
|
||||
```bash
|
||||
# Initial setup
|
||||
cd docker-postfix
|
||||
cp env.sample .env
|
||||
nano .env
|
||||
|
||||
# Build and start
|
||||
cd test
|
||||
make build
|
||||
make up
|
||||
|
||||
# Check status
|
||||
make help
|
||||
make status
|
||||
make logs
|
||||
|
||||
# Test email delivery
|
||||
make test TO=your@email.com
|
||||
|
||||
# Stop
|
||||
make down
|
||||
```
|
||||
|
||||
### Development Workflow
|
||||
|
||||
```bash
|
||||
cd test
|
||||
|
||||
# Start with debug
|
||||
make debug
|
||||
|
||||
# Check logs in another terminal
|
||||
make logs
|
||||
|
||||
# Restart after changes
|
||||
make restart
|
||||
|
||||
# Clean up
|
||||
make clean
|
||||
```
|
||||
|
||||
### Monitoring Workflow
|
||||
|
||||
```bash
|
||||
cd test
|
||||
|
||||
# Check everything
|
||||
make status
|
||||
make health
|
||||
make queue
|
||||
make logs-tail
|
||||
|
||||
# Continuous monitoring
|
||||
make logs
|
||||
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
|
||||
```
|
||||
|
||||
## Security
|
||||
Submission to the queue is not proof of delivery. Check logs for `status=sent`
|
||||
and inspect deferred messages with `postqueue -p`.
|
||||
|
||||
- Store credentials in `.env` (never commit to git)
|
||||
- Limit relay to trusted networks only
|
||||
- TLS automatically enabled for known providers
|
||||
- Use app-specific passwords for Gmail
|
||||
## Build and CI
|
||||
|
||||
## License
|
||||
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.
|
||||
|
||||
MIT License - See [LICENSE](LICENSE) file for details.
|
||||
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.
|
||||
|
||||
## Support
|
||||
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.
|
||||
|
||||
For issues and questions, please open an issue on [Gitea](https://git.k2patel.in/k2patel/docker-postfix/issues).
|
||||
## License and support
|
||||
|
||||
MIT License; see [LICENSE](LICENSE).
|
||||
Issues: https://git.k2patel.in/k2patel/docker-postfix/issues
|
||||
Reference in new issue
Block a user