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.
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:
- Filters hosts — only runs on hosts with
smtp4devin theirserviceslist (Oberon) - Creates
smtp4devsystem group and user - Adds
ponosuser to thesmtp4devgroup (fordocker composeaccess) - Creates
/srv/smtp4devdirectory owned bysmtp4dev:smtp4dev - Templates
docker-compose.ymlinto/srv/smtp4dev/ - Resets SSH connection to apply group membership
- 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:
- Log in to
https://id.ouranos.helu.caas admin- Navigate to Identity → Providers → Add
- Set Category:
SMTP- Fill host
oberon.incus, port22025, disable SSL, fromnoreply@ouranos.helu.ca- Save and assign the provider to the
helucaorganization 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