I've been curious about self-hosting email for a long time. The idea of owning the entire mail stack — no third-party scanning your messages, no sending limits, no "per-user" pricing — is genuinely appealing if you run a small team or want privacy for your personal domain.
I finally set it up last year using Mailcow, and it's been running reliably ever since. Fair warning upfront: email is the most complex self-hosting project you can take on. DNS misconfiguration will get you flagged as spam. But if you follow this guide carefully, you'll have a fully functional mail server with webmail, spam filtering, and DKIM/DMARC signing running in a few hours.
I run Mailcow on Tencent Cloud Lighthouse. For email, the 4 GB RAM / 2 vCPU plan is needed — Mailcow's Docker stack is substantial. Two things matter specifically for email deliverability on Lighthouse: the instances come with dedicated IPs (not shared with other customers' traffic), which means a clean IP reputation by default. Port 25 (outbound SMTP) is blocked by default on cloud servers to prevent spam abuse, but Tencent Cloud supports port 25 unblocking requests via a support ticket for verified legitimate email servers — this is covered in the guide.
- Key Takeaways
Running your own mail server makes sense in specific situations:
The real requirements: a VPS with a dedicated IP, willingness to configure DNS carefully, and patience for the initial setup. Mailcow handles most of the hard parts (spam filtering, webmail, SSL) through Docker Compose.
| Requirement | Details |
|---|---|
| Server | 4 GB RAM minimum (6 GB recommended), 2 vCPU |
| OS | Ubuntu 22.04 LTS |
| Domain | A domain you control — you'll be adding DNS records |
| Dedicated IP | Your Lighthouse instance IP — check it's not on major blocklists |
| Ports | 25, 80, 443, 110, 143, 465, 587, 993, 995 must be open |
| Reverse DNS | Set PTR record for your IP (request from Lighthouse support if needed) |
Important: Check your IP isn't blocklisted before starting. Run it through MXToolbox Blacklist Check. Fresh cloud IPs are usually clean.
Mailcow uses your system hostname as the mail server name. Set it to your mail domain:
sudo hostnamectl set-hostname mail.yourdomain.com
Verify:
hostname -f
# Should output: mail.yourdomain.com
sudo apt update && sudo apt upgrade -y
sudo apt install -y git curl wget
💡 Faster start: Select the Lighthouse Docker CE application image when creating your instance and Docker will already be installed and running. Skip this section and verify with
docker --version.
curl -fsSL https://get.docker.com | sh
sudo systemctl enable docker
sudo systemctl start docker
Verify:
docker --version
docker compose version
sudo ufw allow 25/tcp # SMTP
sudo ufw allow 80/tcp # HTTP (for Certbot)
sudo ufw allow 443/tcp # HTTPS (webmail)
sudo ufw allow 110/tcp # POP3
sudo ufw allow 143/tcp # IMAP
sudo ufw allow 465/tcp # SMTPS
sudo ufw allow 587/tcp # Submission
sudo ufw allow 993/tcp # IMAPS
sudo ufw allow 995/tcp # POP3S
sudo ufw reload
Also open these ports in your Lighthouse firewall console under Firewall → Add Rule.
Get this right before installing Mailcow. Bad DNS = rejected mail.
Go to your domain registrar's DNS management panel and add:
| Type | Name | Value | TTL |
|---|---|---|---|
| A | YOUR_SERVER_IP | 300 | |
| MX | @ | mail.yourdomain.com | 300 |
| TXT | @ | v=spf1 mx ~all | 300 |
| CNAME | autoconfig | mail.yourdomain.com | 300 |
| CNAME | autodiscover | mail.yourdomain.com | 300 |
The DKIM TXT record comes after Mailcow generates it — you'll add it in Part 6.
This is critical. Your IP's PTR record should resolve to mail.yourdomain.com.
In your Lighthouse console, look for Reverse DNS settings. If it's not available in the UI, open a support ticket — Tencent Cloud support can set PTR records for Lighthouse IPs.
Without a matching PTR record, Gmail and other providers will reject or heavily filter your outgoing mail.
cd /opt
sudo git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized
sudo ./generate_config.sh
You'll be prompted for your mail server hostname:
Mail server hostname (FQDN): mail.yourdomain.com
Timezone: America/New_York
This creates mailcow.conf with your settings.
Open mailcow.conf and verify:
sudo nano mailcow.conf
Key fields:
MAILCOW_HOSTNAME=mail.yourdomain.com
TZ=America/New_York
DBPASS=<auto-generated — save this>
sudo docker compose pull
sudo docker compose up -d
This pulls about 2–3 GB of images. The first startup takes 3–5 minutes as containers initialize.
Check status:
sudo docker compose ps
All containers should show Up.
Navigate to https://mail.yourdomain.com in your browser.
You'll see a certificate warning on first access — that's normal, Mailcow is fetching the Let's Encrypt certificate. Wait 2–3 minutes and refresh.
Default admin credentials:
adminmoohooChange this immediately after logging in: Configuration → Access → Administrator → Change Password.
In the admin panel:
yourdomain.comGo to System → SSL/TLS → Certificates. You should see a valid Let's Encrypt certificate for your domain. If it shows "pending", wait a few minutes — Mailcow automatically requests it.
you (becomes you@yourdomain.com)Mailcow includes SOGo webmail at https://mail.yourdomain.com/SOGo.
Log in with you@yourdomain.com and the password you set.
Use these settings for Thunderbird, Apple Mail, or any IMAP client:
Incoming (IMAP):
mail.yourdomain.com993you@yourdomain.comOutgoing (SMTP):
mail.yourdomain.com587you@yourdomain.comSend a test email to a Gmail address. Check:
If it lands in spam, the DKIM setup in Part 6 usually fixes it.
In the Mailcow admin panel:
Back in your domain's DNS panel, add:
| Type | Name | Value |
|---|---|---|
| TXT | dkim._domainkey | v=DKIM1;k=rsa;t=s;s=email;p=YOUR_PUBLIC_KEY |
| Type | Name | Value |
|---|---|---|
| TXT | _dmarc | v=DMARC1;p=quarantine;rua=mailto:dmarc@yourdomain.com |
Use mail-tester.com — send a test email to their address and you'll get a deliverability score. Target 9/10 or higher.
Common issues and fixes:
Port 25 was blocked by my cloud provider.
Many cloud providers block outbound port 25 by default to prevent spam abuse — and Tencent Cloud Lighthouse is no exception. Without outbound port 25, your server can receive mail but can't send to other servers.
How I discovered it:
telnet smtp.gmail.com 25
# Connection timed out
The fix: Submit a support ticket to Tencent Cloud requesting port 25 be unblocked for your instance. Explain you're running a legitimate mail server for your own domain. In my experience, approval takes 1–2 business days. You'll need to provide your instance ID and the domain name.
While waiting, you can still receive mail and test internal delivery. Once port 25 is unblocked, outbound delivery to external servers starts working.
| Issue | Likely Cause | Fix |
|---|---|---|
| Container won't start | Port 25 or 80 already in use | sudo lsof -i :25, stop conflicting process |
| SSL certificate not issued | DNS not propagated yet | Wait 30 min, run ./renew-ssl.sh |
| Mail goes to spam | Missing DKIM/SPF/DMARC | Complete Part 6, run mail-tester.com |
| Can't send outbound | Port 25 blocked | Open support ticket with cloud provider |
| Login fails | Wrong credentials | Reset via ./helper-scripts/mailcow-reset-admin.sh |
| High memory usage | Too many containers | Check docker stats, increase to 6 GB RAM |
| PTR mismatch | PTR not set correctly | Verify with nslookup YOUR_IP, contact support |
Here's the honest picture after running it for a year:
| Aspect | Reality |
|---|---|
| Reliability | Excellent — 99.9%+ uptime on a cloud VPS |
| Deliverability | Good once DKIM/DMARC/PTR are configured correctly |
| Maintenance | Low — docker compose pull && docker compose up -d for updates |
| Setup complexity | High — DNS configuration takes the most time |
| Cost | ~$6–12/month for the server (vs per-user SaaS pricing) |
| Privacy | Complete — data stays on your server |
Best suited for: developers and sysadmins who want control over their mail infrastructure, small teams with a fixed number of users, privacy-conscious individuals.
The key insight: Mailcow does the hard work. The Docker stack handles spam filtering, webmail, SSL, and DKIM signing out of the box. Your job is to configure DNS correctly and get port 25 unblocked. After that, it runs with minimal intervention.
✅ Summary — What You Built
Is self-hosted email difficult to set up?
It's the most complex self-hosting project in this series. DNS configuration must be precise, and port 25 may need to be unblocked by contacting support. Mailcow handles most complexity through Docker, but DNS setup requires careful attention.
What DNS records are required for email to work correctly?
MX record (mail routing), SPF (authorized senders), DKIM (cryptographic signature), DMARC (policy), and PTR (reverse DNS/rDNS). Missing or misconfigured records result in email being rejected as spam.
Why is outbound port 25 blocked on cloud servers?
Cloud providers block port 25 by default to prevent spam. Submit a support ticket to Tencent Cloud explaining you're running a legitimate email server for your domain. Approval typically takes 1–2 business days.
How do I check if my email is likely to go to spam?
Use mail-tester.com — send a test email to their address and you'll get a deliverability score and specific issues to fix. Target 9/10 or higher before sending production email.
👉 Get started with Tencent Cloud Lighthouse
👉 View current pricing and launch promotions
👉 Explore all active deals and offers