Files
ouranos/docs/smtp4dev.md
Robert Helewka b4d60f2f38 docs: rewrite README with structured overview and quick start guide
Replaces the minimal project description with a comprehensive README
including a component overview table, quick start instructions, common
Ansible operations, and links to detailed documentation. Aligns with
Red Panda Approval™ standards.
2026-03-03 12:49:06 +00:00

6.7 KiB

smtp4dev - Development SMTP Server

Overview

smtp4dev is a fake SMTP server for development and testing. It accepts all incoming email without delivering it, capturing messages for inspection via a web UI and IMAP client. All services in the Agathos sandbox that send email (Casdoor, Gitea, etc.) are wired to smtp4dev so email flows can be tested without a real mail server.

Host: Oberon (container_orchestration)
Web UI Port: 22085 → https://smtp4dev.ouranos.helu.ca
SMTP Port: 22025 (used by all services as smtp_host:smtp_port)
IMAP Port: 22045
Syslog Port: 51405 (Alloy)

Architecture

┌─────────────────────────────────────────────────────────┐
│                      Oberon Host                        │
│                                                         │
│  ┌──────────────────────────────────────────────────┐  │
│  │         smtp4dev Container (Docker)              │  │
│  │                                                  │  │
│  │  Port 80  → host 22085  (Web UI)                │  │
│  │  Port 25  → host 22025  (SMTP)                  │  │
│  │  Port 143 → host 22045  (IMAP)                  │  │
│  │                                                  │  │
│  │  Volume: smtp4dev_data → /smtp4dev              │  │
│  │  Logs: syslog → Alloy:51405 → Loki             │  │
│  └──────────────────────────────────────────────────┘  │
│                                                         │
└─────────────────────────────────────────────────────────┘
          ▲                     ▲
          │ SMTP :22025          │ SMTP :22025
   ┌──────┴──────┐        ┌──────┴──────┐
   │   Casdoor   │        │    Gitea    │
   │  (Titania)  │        │  (Rosalind) │
   └─────────────┘        └─────────────┘

External access:
  https://smtp4dev.ouranos.helu.ca  →  HAProxy (Titania)  →  oberon.incus:22085

Shared SMTP Variables

smtp4dev connection details are defined once in ansible/inventory/group_vars/all/vars.yml and consumed by all service templates:

Variable Value Purpose
smtp_host oberon.incus SMTP server hostname
smtp_port 22025 SMTP server port
smtp_from noreply@ouranos.helu.ca Default sender address
smtp_from_name Agathos Default sender display name

Any service that needs to send email references these shared variables rather than defining its own SMTP config. This means switching to a real SMTP server only requires changing group_vars/all/vars.yml.

Ansible Deployment

Playbook

# Deploy smtp4dev on Oberon
ansible-playbook smtp4dev/deploy.yml

# Redeploy HAProxy to activate the smtp4dev.ouranos.helu.ca backend
ansible-playbook haproxy/deploy.yml

Files

File Purpose
ansible/smtp4dev/deploy.yml Main deployment playbook
ansible/smtp4dev/docker-compose.yml.j2 Docker Compose template

Deployment Steps

The deploy.yml playbook:

  1. Filters hosts — only runs on hosts with smtp4dev in their services list (Oberon)
  2. Creates smtp4dev system group and user
  3. Adds ponos user to the smtp4dev group (for docker compose access)
  4. Creates /srv/smtp4dev directory owned by smtp4dev:smtp4dev
  5. Templates docker-compose.yml into /srv/smtp4dev/
  6. Resets SSH connection to apply group membership
  7. Starts the service with community.docker.docker_compose_v2: state: present

Host Variables

Defined in ansible/inventory/host_vars/oberon.incus.yml:

# smtp4dev Configuration
smtp4dev_user: smtp4dev
smtp4dev_group: smtp4dev
smtp4dev_directory: /srv/smtp4dev
smtp4dev_port: 22085        # Web UI (container port 80)
smtp4dev_smtp_port: 22025   # SMTP    (container port 25)
smtp4dev_imap_port: 22045   # IMAP    (container port 143)
smtp4dev_syslog_port: 51405 # Alloy syslog collector

Service Integrations

Casdoor

The Casdoor email provider is declared in ansible/casdoor/init_data.json.j2 and seeded automatically on a fresh Casdoor deployment:

{
  "owner": "admin",
  "name": "provider-email-smtp4dev",
  "displayName": "smtp4dev Email",
  "category": "Email",
  "type": "SMTP",
  "host": "oberon.incus",
  "port": 22025,
  "disableSsl": true,
  "fromAddress": "noreply@ouranos.helu.ca",
  "fromName": "Agathos"
}

⚠️ For existing Casdoor installs, create the provider manually:

  1. Log in to https://id.ouranos.helu.ca as admin
  2. Navigate to Identity → Providers → Add
  3. Set Category: Email, Type: SMTP
  4. Fill host oberon.incus, port 22025, disable SSL, from noreply@ouranos.helu.ca
  5. Save and assign the provider to the heluca organization under Organizations → heluca → Edit → Default email provider

Gitea

Configured directly in ansible/gitea/app.ini.j2:

[mailer]
ENABLED = true
SMTP_ADDR = {{ smtp_host }}
SMTP_PORT = {{ smtp_port }}
FROM = {{ smtp_from }}

Redeploy Gitea to apply:

ansible-playbook gitea/deploy.yml

External Access

smtp4dev's web UI is exposed via HAProxy on Titania at https://smtp4dev.ouranos.helu.ca.

Backend entry in ansible/inventory/host_vars/titania.incus.yml:

- subdomain: "smtp4dev"
  backend_host: "oberon.incus"
  backend_port: 22085
  health_path: "/"

Verification

# Check container is running
ssh oberon.incus "cd /srv/smtp4dev && docker compose ps"

# Check logs
ssh oberon.incus "cd /srv/smtp4dev && docker compose logs --tail=50"

# Test SMTP delivery (sends a test message)
ssh oberon.incus "echo 'Subject: test' | sendmail -S oberon.incus:22025 test@example.com"

# Check web UI is reachable internally
curl -s -o /dev/null -w "%{http_code}" http://oberon.incus:22085

# Check external HTTPS route
curl -sk -o /dev/null -w "%{http_code}" https://smtp4dev.ouranos.helu.ca

site.yml Order

smtp4dev is deployed after Docker (it requires the Docker engine) and before Casdoor (so the SMTP endpoint exists when Casdoor initialises):

- name: Deploy Docker
  import_playbook: docker/deploy.yml

- name: Deploy smtp4dev
  import_playbook: smtp4dev/deploy.yml

- name: Deploy PPLG Stack     # ...continues