How to Self-Host WildDuck with Docker Compose
What Is WildDuck?
WildDuck is a scalable, open-source IMAP/POP3 mail server that stores all email data in MongoDB instead of the filesystem. This architecture eliminates single points of failure and enables horizontal scaling — add more WildDuck instances behind a TCP load balancer without data migration. It includes a REST API for full server management, built-in 2FA (TOTP and U2F), application-specific passwords, and optional GPG encryption at rest. WildDuck handles mailbox access only — pair it with Haraka for inbound SMTP and ZoneMTA for outbound SMTP. Official site.
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 2 GB of free disk space for the application and database
- 2 GB of RAM minimum (4 GB recommended for production)
- A domain name with MX records configured
- Port 25 open for inbound mail (if running the full stack)
Docker Compose Configuration
Create a directory for WildDuck:
mkdir -p ~/wildduck && cd ~/wildduck
Create docker-compose.yml:
services:
wildduck:
image: ghcr.io/zone-eu/wildduck:1.46.25
container_name: wildduck
ports:
- "993:993"
- "143:143"
- "995:995"
- "110:110"
- "8080:8080"
depends_on:
mongo:
condition: service_healthy
redis:
condition: service_healthy
environment:
- APPCONF_dbs_mongo=mongodb://mongo:27017/wildduck
- APPCONF_dbs_redis=redis://redis:6379/3
- APPCONF_api_host=0.0.0.0
- APPCONF_api_accessToken=change-this-to-a-strong-random-token
restart: unless-stopped
mongo:
image: mongo:7
container_name: wildduck-mongo
volumes:
- mongo_data:/data/db
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
redis:
image: redis:7-alpine
container_name: wildduck-redis
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
mongo_data:
redis_data:
Important: Change APPCONF_api_accessToken to a strong random string (use openssl rand -hex 32). This token authenticates all API requests.
Start the stack:
docker compose up -d
Initial Setup
- Verify WildDuck is running:
docker compose logs wildduck - Create your first user via the REST API:
curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-H "X-Access-Token: your-api-token" \
-d '{
"username": "user@yourdomain.com",
"password": "initial-password",
"name": "Your Name",
"quota": 1073741824
}'
- Connect your email client (Thunderbird, etc.) using IMAP:
- Server: your-server-ip
- IMAP Port: 993 (SSL) or 143 (STARTTLS)
- POP3 Port: 995 (SSL) or 110 (STARTTLS)
- Username: user@yourdomain.com
- Password: the password you set
Configuration
Environment Variables
All WildDuck settings can be overridden via APPCONF_* environment variables that map to TOML config paths:
| Variable | Default | Description |
|---|---|---|
APPCONF_dbs_mongo | mongodb://127.0.0.1:27017/wildduck | MongoDB connection string |
APPCONF_dbs_redis | redis://127.0.0.1:6379/3 | Redis connection string |
APPCONF_api_host | 127.0.0.1 | API bind address |
APPCONF_api_accessToken | (none) | API authentication token |
REST API
WildDuck’s API provides full server management without editing config files:
| Endpoint | Method | Purpose |
|---|---|---|
/users | POST | Create user |
/users/{id} | GET/PUT/DELETE | Manage user |
/users/{id}/mailboxes | GET | List mailboxes |
/users/{id}/messages | GET | List messages |
/users/{id}/filters | GET/POST | Manage mail filters |
/users/{id}/autoreply | PUT | Set auto-reply |
/users/{id}/asps | POST | Create application-specific password |
/users/{id}/2fa/totp/setup | POST | Enable TOTP 2FA |
Full API documentation: docs.wildduck.email
Ports
| Port | Protocol | Purpose |
|---|---|---|
| 993 | IMAPS | IMAP over TLS |
| 143 | IMAP | IMAP with STARTTLS |
| 995 | POP3S | POP3 over TLS |
| 110 | POP3 | POP3 with STARTTLS |
| 8080 | HTTP | REST API |
Complete Email Stack
WildDuck handles IMAP/POP3 mailbox access only. For a complete email system, add inbound and outbound SMTP:
| Component | Service | Purpose |
|---|---|---|
| Mailbox access | WildDuck | IMAP/POP3 + REST API |
| Inbound SMTP | Haraka + haraka-plugin-wildduck | Receive mail on port 25 |
| Outbound SMTP | ZoneMTA + zonemta-wildduck | Send mail on port 587 |
| Webmail | Roundcube or Snappymail | Web-based email access |
The haraka-plugin-wildduck handles recipient validation, quota enforcement, and message delivery directly to MongoDB — no LMTP or filesystem delivery needed.
Reverse Proxy
The REST API (port 8080) can be placed behind an HTTP reverse proxy. IMAP/POP3 ports should be exposed directly or proxied via HAProxy in TCP mode.
See Reverse Proxy Setup for HTTP proxy configuration.
Backup
Back up these volumes:
mongo_data— critical — contains all emails, users, attachments (GridFS), and settings. This is your entire mail store.redis_data— session and cache data. Less critical but speeds up recovery.
Use mongodump for consistent MongoDB backups:
docker compose exec mongo mongodump --out /data/backup
docker cp wildduck-mongo:/data/backup ./mongo-backup-$(date +%Y%m%d)
See Backup Strategy for automated approaches.
Troubleshooting
IMAP Client Cannot Connect
Symptom: Email client shows connection refused or timeout on port 993.
Fix: Verify WildDuck is running: docker compose logs wildduck. Check firewall rules for ports 993 and 143. Without TLS certificates configured, use port 143 with STARTTLS or connect without encryption for testing.
API Returns 401 Unauthorized
Symptom: All API requests return 401.
Fix: Include the access token in the X-Access-Token header. Verify the token matches APPCONF_api_accessToken in your Docker Compose environment.
MongoDB Connection Errors on Startup
Symptom: WildDuck exits with MongoDB connection errors.
Fix: Ensure MongoDB is fully started before WildDuck. The depends_on with service_healthy condition handles this. If using an external MongoDB, verify the connection string and network connectivity.
Emails Not Arriving (When Using Full Stack)
Symptom: Sent emails to your domain don’t appear in mailboxes.
Fix: Check Haraka logs for delivery errors. Verify haraka-plugin-wildduck is configured with the correct MongoDB connection string. Confirm the recipient address exists in WildDuck (check via API).
High MongoDB Disk Usage
Symptom: MongoDB data directory grows rapidly.
Fix: WildDuck stores attachments in MongoDB via GridFS with deduplication. Large mailboxes with many unique attachments will consume significant disk space. Monitor with db.stats() in mongosh. Set user quotas via the API to limit storage per user.
Resource Requirements
- RAM: ~300 MB idle (WildDuck + MongoDB + Redis), 1-2 GB under load
- CPU: Low-Medium — MongoDB handles most of the heavy lifting
- Disk: 500 MB for application, plus email storage in MongoDB (plan for 1-10 GB per active user)
Verdict
WildDuck takes a fundamentally different approach to email storage by putting everything in MongoDB. This makes horizontal scaling straightforward — something that’s nearly impossible with traditional filesystem-based mail servers like Mailcow or Mailu. The trade-off is complexity: you need separate components for SMTP (Haraka + ZoneMTA), and MongoDB administration adds operational overhead. Choose WildDuck if you’re building email infrastructure for hundreds or thousands of users and need horizontal scalability. For a personal or small-team email server, Mailcow or Stalwart are simpler choices that bundle everything together.
Frequently Asked Questions
How does WildDuck compare to Mailcow or Mailu?
WildDuck is fundamentally different — it stores all email in MongoDB instead of the filesystem, enabling horizontal scaling. Mailcow and Mailu are all-in-one solutions that bundle SMTP, IMAP, webmail, and admin UI together. WildDuck handles only IMAP/POP3 and requires separate components for SMTP (Haraka, ZoneMTA). Choose WildDuck for large-scale deployments needing horizontal scaling. Choose Mailcow or Mailu for simpler personal or small-team email servers.
Does WildDuck include a webmail interface?
No. WildDuck provides IMAP/POP3 access and a REST API only. For webmail, add Roundcube or Snappymail as a separate service pointing at WildDuck’s IMAP port. The REST API also allows building custom web frontends if needed.
Can WildDuck handle sending email?
No. WildDuck is a mailbox access server (IMAP/POP3) only. For inbound SMTP, pair it with Haraka using the haraka-plugin-wildduck plugin. For outbound SMTP, use ZoneMTA with zonemta-wildduck. This modular architecture is intentional — it allows scaling each component independently.
Does WildDuck support two-factor authentication?
Yes. WildDuck has built-in support for TOTP (time-based one-time passwords) and U2F/FIDO hardware security keys. Enable 2FA per user through the REST API (/users/{id}/2fa/totp/setup). Application-specific passwords are also supported, allowing apps that don’t support 2FA to authenticate with separate credentials.
How does WildDuck store attachments?
WildDuck stores all email data, including attachments, in MongoDB using GridFS. It automatically deduplicates identical attachments across mailboxes, saving significant disk space when the same file is sent to multiple users. This approach trades filesystem simplicity for scalability and built-in deduplication.
Is WildDuck suitable for a personal email server?
Not really. WildDuck’s architecture — separate IMAP, SMTP inbound, SMTP outbound, MongoDB, and Redis — is designed for large-scale deployments with hundreds or thousands of users. For a personal or small-team email server, Mailcow, Stalwart, or Mailu are much simpler choices that bundle everything together.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments