Full Step-by-Step Guide: Installing Mailcow via Dokploy (No SSH)
Tuwan Roshan ( Naveen )
Part 1 — Check your server has enough resources
Step 1.1 — Confirm RAM and disk
- Open Dokploy’s dashboard in your browser.
- Go to the Server section (usually in the left sidebar, sometimes under “Monitoring” or “Settings → Server”).
- Look at current RAM usage and disk usage, since Dokploy + Nextcloud are already running there.
- Mailcow needs roughly 6 GB of RAM minimum, 8 GB recommended for real use, plus around 10–15 GB of disk for containers, mail storage, and databases.
- If your server is close to its limits, note this now — you may need to set
SKIP_CLAMD=yandSKIP_FTS=ylater (Part 4) to reduce RAM usage, or upgrade the server before continuing.
Step 1.2 — Confirm which ports are already in use
- In Dokploy, check each existing service (Nextcloud, Dokploy itself) for which ports/domains they’re bound to.
- Note whether ports
8080and8443are free. These are the two ports mailcow’s web interface will use internally (Part 4 sets this). - If either is already taken by another service, you’ll pick different numbers in Part 4 — just keep this in mind.
Part 2 — Set up DNS in Cloudflare
Step 2.1 — Log into Cloudflare
- Go to
https://dash.cloudflare.comin your browser. - Select the domain you want to use for mail (e.g.
yourdomain.com). - Click into the DNS section from the left sidebar.
Step 2.2 — Add the mail subdomain A record
- Click Add record.
- Set Type to
A. - Set Name to
mail(this createsmail.yourdomain.com). - Set IPv4 address to your server’s public IP address.
- Set Proxy status to DNS only (grey cloud icon, not orange). This step matters a lot — if it’s left orange/proxied, Cloudflare will try to route the traffic through its HTTP proxy, which breaks SMTP/IMAP entirely and can also break the TLS certificate issuance later.
- Click Save.
Step 2.3 — Add the MX record
- Click Add record again.
- Set Type to
MX. - Set Name to
@(this represents your root domain,yourdomain.com). - Set Mail server to
mail.yourdomain.com. - Set Priority to
10. - Click Save.
Step 2.4 — Add the SPF record
- Click Add record.
- Set Type to
TXT. - Set Name to
@. - Set Content to:
v=spf1 mx ~all
- Click Save.
Step 2.5 — Add the DMARC record
- Click Add record.
- Set Type to
TXT. - Set Name to
_dmarc. - Set Content to (replace the email with your own inbox where you want DMARC reports sent):
v=DMARC1; p=quarantine; rua=mailto:[email protected]
- Click Save.
Step 2.6 — Leave DKIM for later
You cannot create the DKIM record yet — mailcow generates the actual key itself after it’s running (Part 7 covers this). Just keep the Cloudflare DNS tab open or bookmarked, since you’ll come back to it.
Part 3 — Set up reverse DNS (PTR) with your hosting provider
Cloudflare cannot do this part — PTR records belong to whoever owns your server’s IP address block, which is your VPS/hosting provider, not your DNS registrar.
Step 3.1 — Find the PTR/rDNS setting
- Log into your hosting provider’s dashboard (Hetzner, DigitalOcean, Contabo, Vultr, etc. — whichever one hosts your server).
- Navigate to your specific server/instance.
- Look for a section named Reverse DNS, rDNS, PTR Record, or sometimes found under Networking or IP Management.
Step 3.2 — Set the PTR value
- Enter
mail.yourdomain.comas the reverse DNS value for your server’s IP address. - Save the change.
- Note that propagation for PTR records can take anywhere from a few minutes to a few hours depending on the provider.
Step 3.3 — If your provider doesn’t expose this in their panel
- Open a support ticket with your provider through their web-based support system (still no SSH required).
- Ask them to set the PTR record for your IP to
mail.yourdomain.com. - Wait for their confirmation before moving on to sending real mail — inbound mail servers check this and will often reject or spam-flag mail from IPs without matching rDNS.
Part 4 — Create the Compose service in Dokploy
Step 4.1 — Start a new project (or use an existing one)
- In Dokploy’s dashboard, click Projects in the sidebar.
- Either click into your existing project (if you want mailcow alongside Nextcloud) or click Create Project to make a dedicated one for mailcow.
Step 4.2 — Create the Compose service
- Inside the project, click Create Service.
- Choose Compose as the service type (not “Application” and not “Database” and not “Template”).
- Give it a name, e.g.
mailcow.
Step 4.3 — Set the source to raw/paste
- In the service’s Source settings, look for the source-type selector.
- Choose the option for pasting the compose file directly (sometimes labeled “Raw”, “Editor”, or you simply get a code-editor box by default when you don’t connect a Git provider).
- Open the
docker-compose.ymlfile you downloaded earlier on your own computer. - Copy its entire contents.
- Paste the entire contents into Dokploy’s compose editor, replacing any placeholder/default content that was there.
- Click Save on that section.
Part 5 — Set environment variables in Dokploy
Step 5.1 — Open the Environment tab
- In the same Compose service, click the Environment (or Env Vars) tab.
- You’ll see a text box or key/value list where you can enter variables.
Step 5.2 — Enter the core required variables
Add each of these on its own line (or its own key/value row, depending on Dokploy’s UI):
MAILCOW_HOSTNAME=mail.yourdomain.com
Replace yourdomain.com with your actual domain — this must exactly match the A record you created in Step 2.2.
DBNAME=mailcow
DBUSER=mailcow
These can stay as-is unless you have a reason to change them.
DBPASS=<generate a strong password>
DBROOT=<generate a different strong password>
Use a password generator (Dokploy’s UI may have a built-in random-value button — look for a dice/refresh icon next to the field). Make sure DBPASS and DBROOT are different from each other. Store both somewhere safe, like a password manager, since you won’t be shown them again through the Dokploy UI after saving.
TZ=Continent/City
Replace with your actual timezone, e.g. Europe/Berlin or Asia/Colombo.
Step 5.3 — Enter the port variables
HTTP_PORT=8080
HTTPS_PORT=8443
These control the internal port mailcow’s web UI listens on. They were already set as the ports mapping default in your compose file, but setting them explicitly here avoids any ambiguity. If you found in Step 1.2 that 8080 or 8443 are already used by another service, change these to something else free, e.g. 8081/8444.
Step 5.4 — Enter the certificate variable
SKIP_LETS_ENCRYPT=y
This is important. It tells mailcow’s own certificate container not to try to issue a Let’s Encrypt certificate itself — because Traefik (via Dokploy) is going to handle that instead, using the labels already built into your compose file. If you skip this, mailcow’s ACME container and Dokploy’s Traefik will both try to grab port 443/80 and conflict with each other.
Step 5.5 — Optional: reduce resource usage
If Step 1.1 showed your server is tight on RAM, also add:
SKIP_CLAMD=y
SKIP_FTS=y
SKIP_CLAMD disables antivirus scanning of incoming mail. SKIP_FTS disables full-text search indexing of mailbox contents. Both are safe to skip for a small personal setup, though SKIP_CLAMD=y does mean incoming attachments won’t be virus-scanned.
Step 5.6 — Save
- Click Save on the Environment tab.
- Dokploy will write these into a
.envfile that sits next to your compose file — this is what the${VARIABLE}references throughout the compose file will pick up automatically.
Part 6 — Deploy
Step 6.1 — Trigger the deployment
- Go back to the Compose service’s main page.
- Click the Deploy button (usually top-right).
Step 6.2 — Watch the logs
- Click into the Logs or Deployments tab.
- You’ll see Docker pulling around 15–18 images (unbound, mysql, redis, postfix, dovecot, nginx, sogo, rspamd, and several others). This can take several minutes depending on your server’s internet speed.
- Once images are pulled, containers start one by one. Watch for any container repeatedly restarting/crashing — that usually points to a missing or wrong environment variable from Part 5.
Step 6.3 — Confirm all containers are healthy
- In the service’s Overview or Containers tab, check that each of the ~18 containers shows a “running” or “healthy” status.
mysql-mailcowandunbound-mailcowtypically start first, since other containers depend on them.- Give it 3–5 minutes after all containers show “running” before testing — some internal setup (database migrations, key generation) happens automatically on first boot.
Part 7 — Point the domain and check the certificate
Step 7.1 — Add the domain in Dokploy (if not automatic)
- Because the Traefik labels are already baked into your compose file, Dokploy should route
mail.yourdomain.comautomatically. Check the Domains tab on the Compose service to confirm it’s listed. - If it isn’t listed automatically, click Add Domain, enter
mail.yourdomain.com, select thenginx-mailcowservice/container, and set the port to8443(or whatever you setHTTPS_PORTto in Step 5.3).
Step 7.2 — Confirm the certificate issued correctly
- Open
https://mail.yourdomain.comin your browser. - If the certificate is valid (padlock icon, no warning), Traefik successfully issued a Let’s Encrypt certificate.
- If you get a certificate warning, check: (a) that the Cloudflare A record from Step 2.2 is set to DNS only, not proxied — Let’s Encrypt needs to reach the server directly to verify domain ownership, and (b) that enough time has passed for DNS to propagate (can take up to a few hours, though usually much faster).
Part 8 — First login and initial mailcow setup
Step 8.1 — Log in
- At
https://mail.yourdomain.com, you’ll see the mailcow login screen. - Username:
admin - Password:
moohoo
Step 8.2 — Change the admin password immediately
- Once logged in, click the admin/gear icon (top right).
- Go to Admin Details or Edit Admin Details.
- Set a new, strong password.
- Save.
Step 8.3 — Add your mail domain inside mailcow
- Go to Email → Domains (or similar, under the admin panel’s mail section).
- Click Add domain.
- Enter
yourdomain.com(without themail.prefix — this is the domain mail will actually be sent/received from, e.g.[email protected]). - Leave the default quota/mailbox settings unless you have specific limits in mind.
- Save.
Step 8.4 — Generate the DKIM key
- Go to Configuration → ARC/DKIM keys (exact menu wording can vary slightly by version).
- Select the domain you just added.
- Click Add DKIM key (2048-bit is the standard recommended size).
- Mailcow will display a DNS record it wants you to add — it’ll look like a long TXT value under a name like
dkim._domainkey.yourdomain.com. - Copy this value — you’ll need the exact selector name and the exact content.
Step 8.5 — Add the DKIM record in Cloudflare
- Go back to your open Cloudflare DNS tab.
- Click Add record.
- Set Type to
TXT. - Set Name to exactly what mailcow showed you (e.g.
dkim._domainkey). - Set Content to exactly the value mailcow generated (usually starts with
v=DKIM1; k=rsa; p=...). - Click Save.
- Back in mailcow’s admin panel, there’s usually a “Verify DKIM” or similar check button — use it once DNS has propagated to confirm it’s picked up correctly.
Step 8.6 — Create your first mailbox
- Go to Email → Mailboxes.
- Click Add mailbox.
- Fill in the local part (e.g.
you), select the domain (yourdomain.com), and set a password for this mailbox. - Save.
- This mailbox (
[email protected]) is now ready to send/receive mail, and can be added to any email client using IMAP/SMTP settings shown in mailcow’s documentation panel.
Part 9 — Verify everything end-to-end
Step 9.1 — Test deliverability
- Go to
https://www.mail-tester.comin your browser. - It will give you a temporary email address.
- From your new mailcow mailbox (either via the built-in webmail at
https://mail.yourdomain.com/SOGo/or a mail client), send a test email to that address. - Go back to mail-tester.com and check your score — it will separately confirm whether SPF, DKIM, DMARC, and rDNS all pass.
Step 9.2 — Fix anything that’s flagged
- SPF fails → double check the TXT record from Step 2.4 exists and exactly matches.
- DKIM fails → re-check the TXT record name/content from Step 8.5, and allow more time for propagation.
- DMARC fails → re-check the TXT record from Step 2.5.
- rDNS/PTR fails or doesn’t match → go back to Part 3 and confirm with your hosting provider that the PTR value is set and has propagated.
Step 9.3 — Confirm mail ports are reachable from outside
- From a different network (e.g. your phone on mobile data, not the same WiFi as your server), try connecting to
mail.yourdomain.comon ports 25, 465, 587, 993 using any online “port checker” tool (search for “open port checker” and enter your domain and each port number one at a time). - If any of these show closed, double-check your server/provider’s external firewall (separate from Docker) isn’t blocking them — this is usually a setting in your hosting provider’s dashboard, again no SSH needed.
You’re done. From here, ongoing management (adding more mailboxes, aliases, spam settings, quotas) all happens through the https://mail.yourdomain.com admin panel — no further server-level configuration needed for day-to-day use.