Before renting a virtual machine, make sure the host allows outgoing connections on ports 25 and 495. Some hosts do not allow deploying an SMTP server. For others, it is standard practice to unblock the port upon request to technical support!
This guide is written and tested for Ubuntu 24.04
Create a server with at least 2C x 4G x 20G SSD (mailcow will consume 75% of this volume in memory and disk).
Check what occupies port 25:
ss -tulnp | grep :25
If the output is not empty, note the PID of the process.
Check what process it is (substitute the noted PID):
ps -p PID -o pid,ppid,user,stat,cmd
Check the list of services and try to understand what needs to be disabled based on the output of the previous command:
systemctl list-units --type=service --state=running
If you see something like:
exim4.service # in this case the service name is exim4
postfix@-.service # in this case the service name is postfix
Disable it (for example, postfix, you substitute what you determined is occupying port 25):
systemctl disable postfix
Verify:
systemctl is-enabled postfix
Check port 25 again:
ss -tulnp | grep :25
The output should be empty!
Point the domain to the server smtp.YOUR_HOST
.
Install ufw
and allow ports 22
, 80
, and 443
:
apt install ufw
ufw default deny incoming && ufw default allow outgoing && ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp
Install docker
and docker-compose
First, check the release number and replace 2.29.2
with the latest release number:
apt update && apt -y install software-properties-common git nano htop && apt update && apt -y install ca-certificates curl gnupg lsb-release && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null && apt-get update && apt-get -y install docker-ce docker-ce-cli containerd.io && curl -L "https://github.com/docker/compose/releases/download/v2.29.2/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose && apt install docker-compose-plugin
Install in opt
:
cd /opt && git clone https://github.com/mailcow/mailcow-dockerized && cd mailcow-dockerized
Generate the config:
./generate_config.sh
Specify the domain without the protocol, enter the timezone
in the format Europe/London
, and choose the stable
option (number 1).
Download the containers:
docker compose pull
Start:
docker compose up -d
Wait until everything is created and started, then stop:
docker compose down
ipv6
:This guide assumes we will be operating on the standard
ipv4
nano /opt/mailcow-dockerized/docker-compose.yml
Search: Ctrl
+ W
β enable_ipv6
β enter
Change to false
and comment out the ipv6
network line:
networks:
mailcow-network:
[...]
enable_ipv6: false # <<< set to false
ipam:
driver: default
config:
- subnet: ${IPV4_NETWORK:-172.22.1}.0/24
# - subnet: ${IPV6_NETWORK:-fd4d:6169:6c63:6f77::/64} # <<< comment out with #
[...]
Create an additional settings file (since this is yml
, spaces are super important! You can't just add or remove them):
nano /opt/mailcow-dockerized/docker-compose.override.yml
services:
ipv6nat-mailcow:
image: bash:latest
restart: "no"
entrypoint: ["echo", "ipv6nat disabled in compose.override.yml"]
Disable IPv6
in unbound-mailcow
:
nano /opt/mailcow-dockerized/data/conf/unbound/unbound.conf
Search: Ctrl
+ W
β do-ip6
β enter
Set to no
:
server:
[...]
do-ip6: no # here put no
[...]
Disable IPv6
in postfix-mailcow
:
nano /opt/mailcow-dockerized/data/conf/postfix/extra.cf
Add the lines:
smtp_address_preference = ipv4
inet_protocols = ipv4
Adjust the config for Nginx
, Dovecot
, and php-fpm
:
sed -i '/::/d' /opt/mailcow-dockerized/data/conf/nginx/listen_* && sed -i '/::/d' /opt/mailcow-dockerized/data/conf/nginx/templates/listen* && sed -i '/::/d' /opt/mailcow-dockerized/data/conf/nginx/dynmaps.conf && sed -i 's/,\[::\]//g' /opt/mailcow-dockerized/data/conf/dovecot/dovecot.conf && sed -i 's/\[::\]://g' /opt/mailcow-dockerized/data/conf/phpfpm/php-fpm.d/pools.conf
Start (you should still be in the folder cd /opt/mailcow-dockerized
):
docker compose up -d
Check the log for obtaining the SSL certificate:
docker compose logs --tail=200 -f acme-mailcow
You should see some messages about obtaining the certificate, wait a few minutes!
Create a script clear_docker_logs.sh
in the folder /opt/mailcow-dockerized
:
nano /opt/mailcow-dockerized/clear_docker_logs.sh
#!/bin/bash
containers=(
mailcowdockerized-olefy-mailcow-1
mailcowdockerized-solr-mailcow-1
mailcowdockerized-sogo-mailcow-1
mailcowdockerized-memcached-mailcow-1
mailcowdockerized-redis-mailcow-1
mailcowdockerized-unbound-mailcow-1
mailcowdockerized-watchdog-mailcow-1
mailcowdockerized-dockerapi-mailcow-1
mailcowdockerized-php-fpm-mailcow-1
mailcowdockerized-clamd-mailcow-1
mailcowdockerized-mysql-mailcow-1
mailcowdockerized-dovecot-mailcow-1
mailcowdockerized-postfix-mailcow-1
mailcowdockerized-nginx-mailcow-1
mailcowdockerized-acme-mailcow-1
mailcowdockerized-netfilter-mailcow-1
mailcowdockerized-rspamd-mailcow-1
mailcowdockerized-ofelia-mailcow-1
mailcowdockerized-ipv6nat-mailcow-1
)
for container in "${containers[@]}"; do
sudo sh -c 'echo "" > $(docker inspect --format="{{.LogPath}}" '"$container"')'
done
Make it executable:
chmod +x /opt/mailcow-dockerized/clear_docker_logs.sh
Set it on cron
:
crontab -e
0 3 * * 0 /opt/mailcow-dockerized/clear_docker_logs.sh
Go to the admin panel at the domain specified during configuration: https://HOST
Default login is admin
, password is moohoo
.
Change the admin password in system
β configuration
To add a new mail domain, go to the Configuration
β Mail Setup
section and click the green button + Add Domain
.
β domain = YOUR_EMAIL_DOMAIN
β aliases = 0
β mailboxes = 1
β selector = tcdkim
β Add domain and restart SOGo
Configure DNS:
Click DNS next to the domain and see the settings table. Expected values and values obtained by the scanner.
Configure on your DNS server according to the recommendations.
The PTR
record is important for mail servers. Through it, the owner of the IP pool kind of confirms that they know about your domain. Sometimes its change is available through the virtual server settings (for example at https://vultr.com), but sometimes you need to write to the hoster's support (https://netangels.ru).
SPF
value (replace YOUR_SMTP_IP
with the IP of your server):
v=spf1 ip4:YOUR_SMTP_IP ~all
DMARC
record value:
v=DMARC1; p=reject;
Add a mailbox on the adjacent tab:
β no-reply
(standard) or totum-noreply
(here it is recommended to use your meaningful name instead of totum
)
β select the previously created domain
β create a password
β disable POP
, IMAP
, and Sieve
β Save
Go to the Email
β Configuration
β Filters
section
Comment out #
all lines in Global prefilter
and add at the end:
require ["reject"];
# Reject all incoming messages
reject "This SMTP-server does not accept incoming messages!";
Click Validate
and then Save changes
.
This way the server will not be cluttered with incoming spam messages.
Conf.php
in TotumIn totum
, the smtp
parameters need to be filled in the settings
table in the custom_smtp_setings_for_schema
field:
{
"host": "ssl://smtp.gmail.com",
"port": 465,
"login": "totum@totum.online",
"password": "password_here"
}
By default, emails are sent from the address no-reply@HOST
, but if you created another mailbox, or the mail domain does not match the Totum installation domain, you need to specify the full mailbox address in the settings
table in the default_email
field.
If you want to set a strict smtp
for all schemas on the server:
Fill in the SMTP parameters for all schemas in Conf.php
:
nano /home/totum/totum-mit/Conf.php
Switch Conf.php
to use SMTP
by commenting out WithPhpMailerTrait
and uncommenting WithPhpMailerSmtpTrait
β this disables custom_smtp_setings_for_schema
in the settings
table.
Uncomment the SMTP parameters and fill in your data:
protected $SmtpData = [
'host' => 'ssl://smtp.ttmapp.ru',
'port' => 465,
'login' => 'no-reply@alliance.ttmapp.ru',
'pass' => 'CFbhNe3exyi2hdUfDq',
];
Use https://www.mail-tester.com/ to check email deliverability.
We also recommend enabling PRO List-unsubscribe
If you need to increase/decrease the maximum email size (default is 100 Mb in main.cf
):
nano /opt/mailcow-dockerized/data/conf/postfix/extra.cf
Add the parameter:
message_size_limit = LIMIT_IN_BYTES
Then you need to restart postfix
:
cd /opt/mailcow-dockerized && docker-compose restart postfix-mailcow