Deployment

Running as a systemd Service

On Linux, the simplest way to run Mailgator in production is as a systemd service. Create a service file:

# /etc/systemd/system/mailgator.service

[Unit]
Description=Mailgator Email Proxy
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=mailgator
Group=mailgator
WorkingDirectory=/opt/mailgator
ExecStart=/usr/local/bin/mailgator serve -c /opt/mailgator/config.toml
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/opt/mailgator

[Install]
WantedBy=multi-user.target

Then enable and start the service:

# Create a dedicated user
sudo useradd --system --no-create-home --shell /usr/sbin/nologin mailgator

# Set up the directory
sudo mkdir -p /opt/mailgator
sudo cp mailgator-config.toml /opt/mailgator/config.toml
sudo cp mailgator-config-secrets.toml /opt/mailgator/config-secrets.toml
sudo chown -R mailgator:mailgator /opt/mailgator
sudo chmod 600 /opt/mailgator/config-secrets.toml

# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable mailgator
sudo systemctl start mailgator

# Check status
sudo systemctl status mailgator
sudo journalctl -u mailgator -f

Docker Deployment

Mailgator works well in Docker. Note that Docker deployments require a Max license (see Licensing).

# Dockerfile
FROM alpine:latest

COPY mailgator /usr/local/bin/mailgator
RUN chmod +x /usr/local/bin/mailgator

EXPOSE 1993 1587 8080

ENTRYPOINT ["mailgator"]
CMD ["serve", "-c", "/etc/mailgator/config.toml"]

Build and run:

# Build
docker build -t mailgator .

# Run with mounted config
docker run -d \
  --name mailgator \
  -p 1993:1993 \
  -p 1587:1587 \
  -p 8080:8080 \
  -v /path/to/config.toml:/etc/mailgator/config.toml:ro \
  -v /path/to/config-secrets.toml:/etc/mailgator/config-secrets.toml \
  -v /path/to/data:/data \
  mailgator

Docker Compose

For a complete setup with Docker Compose:

# docker-compose.yml

services:
  mailgator:
    image: mailgator:latest
    restart: unless-stopped
    ports:
      - "1993:1993"
      - "1587:1587"
      - "8080:8080"
    volumes:
      - ./config.toml:/etc/mailgator/config.toml:ro
      - ./config-secrets.toml:/etc/mailgator/config-secrets.toml
      - mailgator-data:/data

volumes:
  mailgator-data:

Make sure your config file uses paths relative to the container, for example:

[database]
path = "/data/mailgator.db"

Kubernetes Basics

For Kubernetes deployments, store your config in a ConfigMap and your secrets in a Secret:

# Create ConfigMap from config file
kubectl create configmap mailgator-config \
  --from-file=config.toml=mailgator-config.toml

# Create Secret from secrets file
kubectl create secret generic mailgator-secrets \
  --from-file=config-secrets.toml=mailgator-config-secrets.toml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mailgator
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mailgator
  template:
    metadata:
      labels:
        app: mailgator
    spec:
      containers:
        - name: mailgator
          image: mailgator:latest
          args: ["serve", "-c", "/etc/mailgator/config.toml"]
          ports:
            - containerPort: 1993
            - containerPort: 1587
            - containerPort: 8080
          volumeMounts:
            - name: config
              mountPath: /etc/mailgator/config.toml
              subPath: config.toml
              readOnly: true
            - name: secrets
              mountPath: /etc/mailgator/config-secrets.toml
              subPath: config-secrets.toml
            - name: data
              mountPath: /data
      volumes:
        - name: config
          configMap:
            name: mailgator-config
        - name: secrets
          secret:
            secretName: mailgator-secrets
        - name: data
          persistentVolumeClaim:
            claimName: mailgator-data

Important: Mailgator uses a SQLite database (for ask rules), so it should run as a single replica. If you need high availability, consider running multiple instances with separate configs and a load balancer.

Config Management

Best practices for managing your Mailgator configuration:

  • Version control your config. The main config file (mailgator-config.toml) does not contain secrets and is safe to commit to Git.
  • Never commit the secrets file. Add *-secrets.toml to your .gitignore. The secrets file contains your license token and encryption key.
  • Use mailgator config validate in your CI pipeline to catch config errors before deploying.
  • Keep a backup of the secrets file. If you use the ask flow, losing the encryption key means pending emails cannot be replayed.
# .gitignore
*-secrets.toml
*.db

TLS in Production

Mailgator's listener ports accept plain-text connections from your email client. In production, you will typically want to put a reverse proxy in front of Mailgator to handle TLS termination.

For the web approval UI (ask flow), use a standard HTTPS reverse proxy. Here are examples for common reverse proxies:

Caddy

# Caddyfile
mailgator.company.com {
    reverse_proxy localhost:8080
}

Nginx

server {
    listen 443 ssl http2;
    server_name mailgator.company.com;

    ssl_certificate /etc/letsencrypt/live/mailgator.company.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mailgator.company.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

For IMAP and SMTP TLS (ports 993 and 465), you would need a TCP-level TLS proxy (e.g., nginx stream module or HAProxy) rather than an HTTP reverse proxy. In many cases, it is simpler to run Mailgator on a private network and let your email clients connect locally or over a VPN.

Monitoring and Logging

Mailgator logs structured output to stdout. Each proxied connection and rule evaluation is logged with relevant details (username, operation, rule name, action).

If running with systemd, logs are available through journalctl:

# Follow logs
sudo journalctl -u mailgator -f

# Show recent logs
sudo journalctl -u mailgator --since "1 hour ago"

With Docker, use docker logs:

docker logs -f mailgator

Backup Considerations

The following files should be included in your backup strategy:

File Priority Notes
config.toml High Your rules and server configuration. Should be in version control.
config-secrets.toml Critical License token and encryption key. Without it, pending emails are lost. Store securely (not in Git).
mailgator.db Medium SQLite database with pending and historical approval records. Only exists if you use ask rules.

For SQLite backups, you can use the sqlite3 backup command or simply copy the file while Mailgator is stopped. For live backups, use the SQLite .backup command to avoid corruption.