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

  1. Verify WildDuck is running: docker compose logs wildduck
  2. 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
  }'
  1. 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:

VariableDefaultDescription
APPCONF_dbs_mongomongodb://127.0.0.1:27017/wildduckMongoDB connection string
APPCONF_dbs_redisredis://127.0.0.1:6379/3Redis connection string
APPCONF_api_host127.0.0.1API bind address
APPCONF_api_accessToken(none)API authentication token

REST API

WildDuck’s API provides full server management without editing config files:

EndpointMethodPurpose
/usersPOSTCreate user
/users/{id}GET/PUT/DELETEManage user
/users/{id}/mailboxesGETList mailboxes
/users/{id}/messagesGETList messages
/users/{id}/filtersGET/POSTManage mail filters
/users/{id}/autoreplyPUTSet auto-reply
/users/{id}/aspsPOSTCreate application-specific password
/users/{id}/2fa/totp/setupPOSTEnable TOTP 2FA

Full API documentation: docs.wildduck.email

Ports

PortProtocolPurpose
993IMAPSIMAP over TLS
143IMAPIMAP with STARTTLS
995POP3SPOP3 over TLS
110POP3POP3 with STARTTLS
8080HTTPREST API

Complete Email Stack

WildDuck handles IMAP/POP3 mailbox access only. For a complete email system, add inbound and outbound SMTP:

ComponentServicePurpose
Mailbox accessWildDuckIMAP/POP3 + REST API
Inbound SMTPHaraka + haraka-plugin-wildduckReceive mail on port 25
Outbound SMTPZoneMTA + zonemta-wildduckSend mail on port 587
WebmailRoundcube or SnappymailWeb-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_datacritical — 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.

Comments