elektromail
A minimal IMAP/SMTP mail server written in Rust, designed for development and testing purposes.
Features
- IMAP4rev2 server on port 1143 (default)
- SMTP server on port 2525 (default)
- Auto-creates mailboxes when emails arrive
- In-memory storage (default) or SQLite persistence
- Plain text authentication (no TLS required for local dev)
- Optional STARTTLS with auto-generated certificates
- HTTP control plane for test automation
- Multi-architecture Docker images (amd64/arm64)
- Zero configuration required
- RFC 9051 compliance test harness
Installation
Docker (Recommended)
Pull from GitLab Container Registry:
# Pull latest image (multi-arch: amd64/arm64)
# Or pull a specific version
Run the container:
With custom credentials:
Cargo (crates.io)
Install as a CLI tool:
Or add as a dev-dependency for testing:
[]
= "0.1"
From Source
Quick Start
# Run the server
# Run with custom credentials
ELEKTROMAIL_USERS=demo:demo
# Run all tests
# Run RFC 9051 compliance tests only
Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
ELEKTROMAIL_USERS |
user:pass |
Comma-separated user credentials |
ELEKTROMAIL_AUTH_DISABLED |
- | Set to 1, true, or yes to accept any credentials |
ELEKTROMAIL_DB |
- | SQLite database path for persistent storage |
ELEKTROMAIL_HTTP_TOKEN |
- | Bearer token required for HTTP control plane access |
ELEKTROMAIL_PRELOAD_DIR |
- | Filesystem directory for preloaded fixtures |
ELEKTROMAIL_DSN_RULES |
- | JSON rules for delivery policy/DSN generation |
SMTP_PORT |
2525 |
SMTP listen port |
IMAP_PORT |
1143 |
IMAP listen port |
HTTP_PORT |
- | HTTP control plane port (disabled if not set) |
BIND_ADDR |
0.0.0.0 |
Bind address |
User Provisioning
Configure users via ELEKTROMAIL_USERS in comma-separated format:
# Single user
ELEKTROMAIL_USERS=admin:adminpass
# Multiple users
ELEKTROMAIL_USERS=admin:admin,demo:demo,test:test
# Extended format with email address
ELEKTROMAIL_USERS=admin:admin@example.com:adminpass,demo:demo@example.com:demopass
If not configured, defaults to user / pass.
Runtime user provisioning is available through the HTTP control plane (POST /users). Users added
there are available immediately for SMTP/IMAP authentication, and POST /reset restores the seed
users from ELEKTROMAIL_USERS.
Storage
In-memory (default): All data is lost when the server stops. Suitable for most test scenarios.
SQLite (persistent): Set ELEKTROMAIL_DB to enable:
ELEKTROMAIL_DB=/path/to/mail.db
Preloading Fixtures
Seed messages from disk by setting ELEKTROMAIL_PRELOAD_DIR to a directory structure like:
fixtures/
user/
INBOX/
welcome.eml
welcome.eml.meta.json
Each .eml file is appended to the specified mailbox. An optional .eml.meta.json sidecar can
include IMAP flags and an internal date:
The preloaded messages become the baseline for POST /reset.
Delivery Policy / DSN Rules
You can simulate bounces and rejections by configuring ELEKTROMAIL_DSN_RULES with JSON rules.
Rules support simple * wildcards on recipient and sender patterns.
Example:
Supported actions:
rejectreturns an SMTP 5xx response duringRCPT TO.bounceacceptsRCPT TO/DATA, drops delivery, and sends a DSN to the sender.acceptexplicitly allows delivery.
DSN messages are delivered to the sender’s mailbox (local part of MAIL FROM).
Authentication
Both IMAP and SMTP support plain text authentication without TLS:
- IMAP:
LOGINcommand andAUTHENTICATE PLAIN - SMTP:
AUTH PLAINmechanism
STARTTLS is available but optional. Connections start unencrypted and authentication works before any TLS upgrade. Certificates are auto-generated.
For tests that don't need auth validation, set ELEKTROMAIL_AUTH_DISABLED=true to accept any credentials.
HTTP Control Plane
Enable by setting HTTP_PORT:
HTTP_PORT=8080
| Endpoint | Method | Description |
|---|---|---|
/reset |
POST | Clear all mailboxes |
/purge |
POST | Clear all messages, keep users/mailboxes |
/inject |
POST | Inject email directly (JSON body) |
/users |
GET | List configured users |
/users |
POST | Create a user |
/config |
GET | Server configuration snapshot |
/messages |
GET | List all messages |
/messages?user=X&mailbox=Y |
GET | List messages for specific mailbox |
/messages/{uid} |
GET | Fetch a single message by UID |
/openapi.json |
GET | OpenAPI specification |
/docs |
GET | Swagger UI for the HTTP API |
/users supports include_email=true to return user entries with optional email fields.
Optional authentication for the HTTP API can be enabled with:
ELEKTROMAIL_HTTP_TOKEN=your-token
When set, requests must include Authorization: Bearer your-token. For browser access,
/docs?token=your-token and /openapi.json?token=your-token are also accepted, and the Swagger UI
will inject the header automatically.
Docker
Pre-built Images
Multi-architecture images (amd64/arm64) are available from GitLab Container Registry:
| Tag | Description |
|---|---|
registry.gitlab.com/elektroladen/elektromail:latest |
Latest release |
registry.gitlab.com/elektroladen/elektromail:v0.1.0 |
Specific version |
Images are based on Alpine Linux 3.21 for minimal footprint. The binary is statically compiled and stripped. Runs as non-root user.
Build Locally
Docker Compose Example
services:
mail:
image: registry.gitlab.com/elektroladen/elektromail:latest
ports:
- "2525:2525" # SMTP
- "1143:1143" # IMAP
environment:
- ELEKTROMAIL_USERS=testuser:testpass
With custom ports and HTTP control plane:
services:
mail:
image: registry.gitlab.com/elektroladen/elektromail:latest
ports:
- "3025:3025" # SMTP on custom port
- "3143:3143" # IMAP on custom port
- "8080:8080" # HTTP control plane
environment:
- SMTP_PORT=3025
- IMAP_PORT=3143
- HTTP_PORT=8080
- ELEKTROMAIL_USERS=admin:admin,demo:demo,test:test
CI/CD Integration
For Docker-in-Docker setups, set ELEKTROMAIL_DOCKER_HOST=docker.
ELEKTROMAIL_DOCKER_TESTS=1
RFC 9051 Compliance Test Harness
This project includes a comprehensive test harness for validating IMAP server implementations against RFC 9051 (IMAP4rev2).
Test Coverage
210 tests covering all major IMAP commands:
| Section | Commands | Tests |
|---|---|---|
| 6.1 Any State | CAPABILITY, NOOP, LOGOUT | 19 |
| 6.2 Not Authenticated | LOGIN, STARTTLS, AUTHENTICATE | 22 |
| 6.3 Authenticated | SELECT, EXAMINE, CREATE, DELETE, RENAME, SUBSCRIBE, LIST, NAMESPACE, STATUS, APPEND, IDLE, ENABLE | 92 |
| 6.4 Selected | CLOSE, UNSELECT, EXPUNGE, SEARCH, FETCH, STORE, COPY, MOVE | 77 |
Testing Against Local Server
# Test the bundled elektromail server
Testing Against External IMAP Servers
The harness can validate any IMAP server:
# Test against an external server
RFC9051_TEST_HOST=imap.example.com \
RFC9051_TEST_PORT=993 \
RFC9051_TEST_USER=testuser \
RFC9051_TEST_PASS=testpass \
RFC9051_TEST_TLS=true \
Test Organization
Tests are organized by RFC section:
tests/rfc9051/
├── mod.rs # Configuration and test client
├── client.rs # IMAP test client with TLS support
├── response.rs # Response parser
├── assertions.rs # RFC-specific assertions
├── fixtures.rs # Test messages
└── section6_commands/
├── any_state/ # CAPABILITY, NOOP, LOGOUT
├── not_authenticated/ # LOGIN, STARTTLS, AUTHENTICATE
├── authenticated/ # SELECT, EXAMINE, CREATE, etc.
└── selected/ # FETCH, STORE, SEARCH, etc.
RFC Documentation
Local reference documentation is available in docs/rfc9051/:
- Section 1: Introduction
- Section 2: Protocol Overview
- Section 3: State Machine
- Section 4: Data Formats
- Section 5: Operational
- Section 6: Commands
- Section 7: Responses
IMAP Commands Reference
Any State
CAPABILITY- List server capabilitiesNOOP- No operation (keepalive)LOGOUT- End session
Not Authenticated
LOGIN user pass- Authenticate with credentialsSTARTTLS- Upgrade to TLSAUTHENTICATE mechanism- SASL authentication
Authenticated
SELECT mailbox- Open mailbox read-writeEXAMINE mailbox- Open mailbox read-onlyCREATE mailbox- Create new mailboxDELETE mailbox- Delete mailboxRENAME old new- Rename mailboxSUBSCRIBE mailbox- Subscribe to mailboxUNSUBSCRIBE mailbox- Unsubscribe from mailboxLIST ref pattern- List mailboxesNAMESPACE- Get namespace prefixesSTATUS mailbox (items)- Get mailbox statusAPPEND mailbox message- Add message to mailboxIDLE- Wait for updatesENABLE capability- Enable extension
Selected
CLOSE- Close mailbox, expunge deletedUNSELECT- Close mailbox, keep deletedEXPUNGE- Remove deleted messagesSEARCH criteria- Find messagesFETCH seq items- Retrieve message dataSTORE seq flags- Modify message flagsCOPY seq mailbox- Copy messagesMOVE seq mailbox- Move messages
Development
Running Specific Tests
# Run a specific command's tests
# Run with output
# Run single-threaded (useful for debugging)
Adding New Tests
- Create a new test file in the appropriate
section6_commands/subdirectory - Add the module to the parent
mod.rs - Use the
TestClientfromcrate::rfc9051 - Use assertions from
crate::rfc9051::assertions
Example:
use crate;
use crate*;
async
Credits
This project was inspired by GreenMail by Marcel May. GreenMail is an excellent Java-based email server for testing purposes. elektromail aims to provide a similar experience for developers who prefer a Rust-based solution.
License
This project is licensed under the European Union Public Licence v1.2 (EUPL-1.2) - see the LICENSE file for details.