mailcow is Docker-native and uses Docker Compose for deployment. This guide provides detailed Docker deployment instructions.
# Clone repository
cd /opt
sudo git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized
# Generate configuration
sudo ./generate_config.sh
# Edit configuration
sudo nano mailcow.conf
# Pull and start
sudo docker compose pull
sudo docker compose up -d
mailcow uses a pre-configured docker-compose.yml with 15+ services:
| Service | Container | Purpose |
|---|---|---|
postfix-mailcow |
postfix-mailcow | MTA (SMTP) |
dovecot-mailcow |
dovecot-mailcow | IMAP/POP3 |
rspamd-mailcow |
rspamd-mailcow | Spam filtering |
clamav-mailcow |
clamav-mailcow | Antivirus |
mysql-mailcow |
mysql-mailcow | Database |
redis-mailcow |
redis-mailcow | Caching |
sogo-mailcow |
sogo-mailcow | Webmail/Groupware |
nginx-mailcow |
nginx-mailcow | Web server |
acme-mailcow |
acme-mailcow | SSL certificates |
| Service | Container | Purpose |
|---|---|---|
unbound-mailcow |
unbound-mailcow | DNS resolver |
watchdog-mailcow |
watchdog-mailcow | Health monitoring |
netfilter-mailcow |
netfilter-mailcow | Firewall rules |
olify-mailcow |
olify-mailcow | Office macro scanning |
memcached-mailcow |
memcached-mailcow | Session cache |
mailcow creates 12 critical volumes that must be backed up:
| Priority | Volume | Purpose | Backup Required |
|---|---|---|---|
| 🔴 CRITICAL | crypt-vol-1 |
Encryption keys | YES |
| 🔴 CRITICAL | mysql-vol-1 |
Database | YES |
| 🔴 CRITICAL | vmail-vol-1 |
Mail storage | YES |
| 🟡 IMPORTANT | rspamd-vol-1 |
Spam learning data | Recommended |
| 🟡 IMPORTANT | redis-vol-1 |
Sessions/cache | No (rebuildable) |
| 🟡 IMPORTANT | clamd-db-vol-1 |
Virus definitions | No (rebuildable) |
| 🟢 OPTIONAL | postfix-vol-1 |
Postfix queue | No |
| 🟢 OPTIONAL | sogo-web-vol-1 |
SOGo files | No |
By default, volumes are stored in:
# Docker managed volumes
/var/lib/docker/volumes/mailcowdockerized_<name>-vol-1/_data
# Or check with:
docker volume ls | grep mailcow
mailcow creates a Docker bridge network:
# View mailcow network
docker network ls | grep mailcow
# Inspect network
docker network inspect mailcowdockerized_mailcow-network
mailcow exposes these ports on the host:
| Port | Protocol | Service | Purpose |
|---|---|---|---|
| 25 | TCP | Postfix | SMTP (mail relay) |
| 465 | TCP | Postfix | SMTPS (legacy SSL) |
| 587 | TCP | Postfix | Submission (TLS) |
| 110 | TCP | Dovecot | POP3 |
| 143 | TCP | Dovecot | IMAP |
| 4190 | TCP | Dovecot | ManageSieve |
| 443 | TCP | Nginx | HTTPS (web UI) |
| 80 | TCP | Nginx | HTTP (redirect) |
| 993 | TCP | Dovecot | IMAPS |
| 995 | TCP | Dovecot | POP3S |
To change port bindings, edit docker-compose.yml:
services:
postfix-mailcow:
ports:
- "127.0.0.1:25:25" # Bind to localhost only
- "587:587"
Key environment variables in mailcow.conf:
# Server hostname
MAILCOW_HOSTNAME=mail.example.com
# Database credentials
DBROOT=secure_root_password
DBPASS=secure_user_password
# Redis password
REDISPASS=secure_redis_password
# Skip IPv6 (if not available)
SKIP_IPV6=true
# Timezone
TZ=Europe/Berlin
# Custom ACME server (optional)
ACME_SERVER=https://acme-v02.api.letsencrypt.org/directory
# Start all services
docker compose up -d
# Stop all services
docker compose down
# Restart specific service
docker compose restart dovecot-mailcow
# View running containers
docker compose ps
# View logs
docker compose logs -f
# View specific service logs
docker compose logs -f postfix-mailcow
# Enter Postfix container
docker compose exec postfix-mailcow bash
# Enter Dovecot container
docker compose exec dovecot-mailcow bash
# Enter MySQL container
docker compose exec mysql-mailcow bash
# Enter Rspamd container
docker compose exec rspamd-mailcow bash
# Check container health
docker inspect --format='{{.State.Health.Status}}' <container-name>
# Run mailcow health check
docker compose exec watchdog-mailcow /usr/local/bin/healthcheck.sh
Create /opt/mailcow-backup.sh:
#!/bin/bash
BACKUP_DIR="/backup/mailcow"
DATE=$(date +%Y%m%d-%H%M%S)
mkdir -p $BACKUP_DIR/$DATE
# Stop mailcow
cd /opt/mailcow-dockerized
docker compose down
# Backup volumes
docker run --rm \
-v mailcowdockerized_crypt-vol-1:/crypt:ro \
-v $BACKUP_DIR/$DATE:/backup \
alpine tar czf /backup/crypt-vol-1-$DATE.tar.gz -C /crypt .
docker run --rm \
-v mailcowdockerized_mysql-vol-1:/mysql:ro \
-v $BACKUP_DIR/$DATE:/backup \
alpine tar czf /backup/mysql-vol-1-$DATE.tar.gz -C /mysql .
docker run --rm \
-v mailcowdockerized_vmail-vol-1:/vmail:ro \
-v $BACKUP_DIR/$DATE:/backup \
alpine tar czf /backup/vmail-vol-1-$DATE.tar.gz -C /vmail .
# Restart mailcow
docker compose up -d
echo "Backup completed: $BACKUP_DIR/$DATE"
Add to crontab:
# Daily backup at 2 AM
0 2 * * * /opt/mailcow-backup.sh
cd /opt/mailcow-dockerized
# Pull latest changes
git pull
# Pull new images
docker compose pull
# Restart with new images
docker compose up -d
mailcow includes an update checker:
# Check available updates
docker compose exec watchdog-mailcow /usr/local/bin/check_updates.sh
# Check logs
docker compose logs <service-name>
# Check resource usage
docker stats
# Inspect container
docker inspect <container-name>
# Test network connectivity
docker compose exec postfix-mailcow ping -c 4 google.com
# Check DNS resolution
docker compose exec unbound-mailcow dig example.com
# List volumes
docker volume ls | grep mailcow
# Inspect volume
docker volume inspect mailcowdockerized_vmail-vol-1
# Remove orphaned volumes (careful!)
docker volume prune
# Check memory usage
docker stats --no-stream
# Limit container memory (edit docker-compose.yml)
services:
dovecot-mailcow:
deploy:
resources:
limits:
memory: 2G
Avoid latest tag in production:
services:
postfix-mailcow:
image: ghcr.io/mailcow/mailcow-dockerized:2026-01
For enhanced security:
services:
dovecot-mailcow:
read_only: true
tmpfs:
- /tmp
- /run
Prevent resource exhaustion:
services:
clamav-mailcow:
deploy:
resources:
limits:
memory: 2G
cpus: '2.0'
Add health checks to critical services:
services:
mysql-mailcow:
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 30s
timeout: 10s
retries: 3
Any questions?
Feel free to contact us. Find all contact information on our contact page.