8.2.25 Poste.io — Deploying a Mail Server
Poste.io simplifies deploying a full-featured mail server: SMTP/IMAP/POP3, web admin, webmail, anti-spam/antivirus. This article addresses: correct networking and ports, issuing a TLS certificate, configuring DNS records (MX, SPF, DKIM, DMARC), reverse zone (rDNS/PTR) and basic diagnostics. We rely on the project’s current recommendations: Docker host network, the list of ports to open, rDNS and DNS requirements, and Poste.io environment parameters.
Server preparation
Requirements
- Purchased Virtual or Dedicated server.
- root access or a sudo user.
- Active Domain name.
- Access to manage your domain’s DNS records.
- Resources: 2 vCPU, 4 GB RAM, 20 GB disk space.
Environment variables
The article uses placeholders (replace with your own values):
SERVER_IP
— public IP of the serverDOMAIN_NAME
— root domain (example:example.com
)MAIL_HOSTNAME
— A record of the mail host (example:mail.example.com
)TIMEZONE
— system timezone (example:Europe/Kyiv
)DATA_DIR
— data directory on the host (example:/srv/poste/data
)ADMIN_EMAIL
— your admin email (you will create it in the admin panel)
Installation
Prepare Docker and Compose
Install Docker and Compose in the usual way (if not already installed). See the full guide in our article: Docker and Docker Compose.
Check versions:
docker --version
docker-composer version
Create the data directory
First, create a directory for data and grant permissions:
sudo mkdir -p DATA_DIR
sudo chown -R $USER:$USER DATA_DIR
Note: all mail data, logs and configs live in /data inside the container. We will mount DATA_DIR there to simplify backups.
Prepare docker-compose.yml
Create a Compose file with the recommended host network (correct outgoing IPs, simple firewall, IPv6 readiness):
version: "3.8"
services:
poste:
image: analogic/poste.io:2
container_name: poste
hostname: MAIL_HOSTNAME
restart: unless-stopped
network_mode: host # recommended mode
environment:
- TZ=TIMEZONE
# If proxying through external Nginx/Traefik you can disable HTTPS redirect:
# - HTTPS=OFF
# Additionally, if RAM is tight (not recommended for production):
# - DISABLE_CLAMAV=TRUE
# - DISABLE_RSPAMD=TRUE
# Integration if needed:
# - ELASTICSEARCH=127.0.0.1:9200
volumes:
- DATA_DIR:/data
Alternative without host network: instead of network_mode: host
you can use ports:
and map 25, 80, 110, 143, 443, 465, 587, 993, 995 and, if needed, 4190 (Sieve). But the project recommends the host network.
Start the container
Bring the service up and confirm it is running:
docker compose up -d
docker compose ps
Open firewall ports
Allow mail and web ports.
sudo ufw allow 25,80,110,143,443,465,587,993,995,4190/tcp
sudo ufw reload
sudo ufw status numbered
sudo iptables -A INPUT -p tcp -m multiport --dports 25,80,110,143,443,465,587,993,995,4190 -j ACCEPT
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -j DROP
Initial admin login
Open the admin/webmail in a browser at: https://<MAIL_HOSTNAME>
.
Use the setup wizard to create an administrator, a domain and the first mailbox.
After that you will enter the administrative panel:
Configuring additional features
Sieve filters (port 4190)
Sieve is the standardized server-side mail filtering language (RFC 5228). Rules run on the server before the mail reaches the client: folder sorting, forwarding, auto-replies, anti-spam tags, etc. Editing rules from Roundcube/Thunderbird via ManageSieve (port 4190) is convenient.
Enable management of delivery rules:
ss -lntp | grep :4190 || echo "Open port 4190 (see UFW/iptables above)"
Why this is useful
Unified server-side rules work the same for all clients and devices (unlike local filters in a mail client).
Elasticsearch integration
Elasticsearch is a full-text search and analytics engine. For Poste.io it is useful for aggregating and searching mail events and logs (deliveries, bounces, spam scores, quarantine, etc.), building dashboards and investigating incidents (together with Kibana/OpenSearch). The feature is optional but simplifies monitoring and auditing the mail system.
Add the following line to your docker-compose.yml
under environment
:
environment:
- ELASTICSEARCH=127.0.0.1:9200
Data backup
Save critical data from the mounted DATA_DIR
:
docker compose down
tar -C $(dirname DATA_DIR) -czf /root/poste-backup-$(date +%F).tar.gz $(basename DATA_DIR)
docker compose up -d
Verification and testing
View container logs
Check that the service started without critical errors:
docker logs -f poste
Check listening ports
Ensure the required TCP ports are open and bound to processes:
ss -lntp | egrep ':25|:80|:110|:143|:443|:465|:587|:993|:995|:4190'
Quick breakdown of ss -lntp
columns
- LISTEN — socket is waiting for incoming connections
- Local Address:Port — where the port is bound
- 0.0.0.0:PORT — listening on all IPv4 interfaces
- [::]:PORT or *:PORT — listening on all IPv6
- 127.0.0.1:PORT — available only locally
- users:(… pid=…) — which process holds the port (name and PID)
Common issues
Error / Symptom | Cause | Fix | Diagnostics | |
---|---|---|---|---|
Certificate not issued | Port 80 is occupied/closed | Free port 80 or proxy /.well-known to Poste.io; retry issuance |
ss -lntp or grep :80 |
|
Messages land in spam | Missing/incorrect SPF/DKIM/DMARC, missing rDNS | Fix records; request PTR from your provider | dig , nslookup , hints in admin UI |
|
Sending to external domains fails | Provider blocks port 25 | Request unblock / use a smart relay | telnet mx.gmail.com 25 from server |
|
Admin UI not reachable over HTTPS | Port 443 conflict | Free port 443 on the host or use a proxy + HTTPS=OFF + HTTP_PORT/HTTPS_PORT |
ss -lntp or grep :443 |
|
IMAP/POP3 not working | Ports / firewall | Allow ports in UFW/iptables | ufw status , iptables -S |
Official documentation and resources
Official documentation