Setting up Mailcow on VPS with SendGrid and SSL/TLS using OpenResty as Reverse Proxy
Introduction
This guide provides detailed steps to set up Mailcow on a VPS, configure it to bypass port 25 by using SendGrid for outgoing emails, and manage SSL/TLS using OpenResty as a reverse proxy.
Prerequisites
A VPS with Docker and Docker Compose installed.
A domain name (e.g.,
lunapapa.eu).A SendGrid account with an API key.
OpenResty installed and configured via Docker.
Cloudflare account for managing SSL/TLS certificates.
Step-by-Step Guide
1. Clone Mailcow Repository
git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized
./generate_config.sh2. Configure mailcow.conf
Edit the mailcow.conf file to match your domain setup:
MAILCOW_HOSTNAME=<your-mail-server-domain>
# Internal HTTP port for Mailcow containers
HTTP_PORT=8001
HTTP_BIND=0.0.0.0
# HTTPS is handled by reverse proxy
HTTPS_PORT=
HTTPS_BIND=3. Adjust nginx-mailcow service with following setup
nginx-mailcow:
depends_on:
- sogo-mailcow
- php-fpm-mailcow
- redis-mailcow
image: nginx:mainline-alpine
dns:
- ${IPV4_NETWORK:-172.22.1}.254
command: /bin/sh -c "envsubst < /etc/nginx/conf.d/templates/listen_plain.template > /etc/nginx/conf.d/listen_plain.active &&
envsubst < /etc/nginx/conf.d/templates/sogo.template > /etc/nginx/conf.d/sogo.active &&
. /etc/nginx/conf.d/templates/server_name.template.sh > /etc/nginx/conf.d/server_name.active &&
. /etc/nginx/conf.d/templates/sites.template.sh > /etc/nginx/conf.d/sites.active &&
. /etc/nginx/conf.d/templates/sogo_eas.template.sh > /etc/nginx/conf.d/sogo_eas.active &&
nginx -qt &&
until ping phpfpm -c1 > /dev/null; do sleep 1; done &&
until ping sogo -c1 > /dev/null; do sleep 1; done &&
until ping redis -c1 > /dev/null; do sleep 1; done &&
until ping rspamd -c1 > /dev/null; do sleep 1; done &&
exec nginx -g 'daemon off;'"
environment:
- HTTP_PORT=${HTTP_PORT:-8001}
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
- IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
- TZ=${TZ}
- SKIP_SOGO=${SKIP_SOGO:-n}
- ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
- ADDITIONAL_SERVER_NAMES=${ADDITIONAL_SERVER_NAMES:-}
volumes:
- ./data/web:/web:ro,z
- ./data/conf/rspamd/dynmaps:/dynmaps:ro,z
- ./data/assets/ssl/:/etc/ssl/mail/:ro,z
- ./data/conf/nginx/:/etc/nginx/conf.d/:z
- ./data/conf/rspamd/meta_exporter:/meta_exporter:ro,z
- sogo-web-vol-1:/usr/lib/GNUstep/SOGo/
ports:
- "${HTTP_BIND:-}:${HTTP_PORT:-8001}:${HTTP_PORT:-8001}"
restart: always
networks:
mailcow-network:
aliases:
- nginx
4 Remove the listen_ssl.active file:
rm ./data/conf/nginx/listen_ssl.active5 Adjust the sites.template.sh
echo '
server {
listen 127.0.0.1:65510;
include /etc/nginx/conf.d/listen_plain.active;
include /etc/nginx/conf.d/server_name.active;
include /etc/nginx/conf.d/includes/site-defaults.conf;
}
';
for cert_dir in /etc/ssl/mail/*/ ; do
if [[ ! -f ${cert_dir}domains ]] || [[ ! -f ${cert_dir}cert.pem ]] || [[ ! -f ${cert_dir}key.pem ]]; then
continue
fi
# do not create vhost for default-certificate. the cert is already in the default server listen
domains="$(cat ${cert_dir}domains | sed -e 's/^[[:space:]]*//')"
case "${domains}" in
"") continue;;
"${MAILCOW_HOSTNAME}"*) continue;;
esac
echo -n '
server {
include /etc/nginx/conf.d/listen_plain.active;
server_name '${domains}';
include /etc/nginx/conf.d/includes/site-defaults.conf;
}
';
done
6. Start all the services
docker-compose up -d7. Set Up SendGrid as SMTP Relay
Enter the Postfix container and edit the main.cf configuration file:
docker exec -it mailcowdockerized-postfix-mailcow-1 /bin/bash
nano /opt/postfix/conf/main.cf
Add the following lines to use SendGrid for outgoing emails
relayhost = [smtp.sendgrid.net]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/opt/postfix/conf/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = may
header_size_limit = 4096000
Create and populate the sasl_passwd file:
nano /opt/postfix/conf/sasl_passwdAdd your SendGrid credentials:
[smtp.sendgrid.net]:587 apikey:YOUR_SENDGRID_API_KEYHash the password file and reload Postfix:
postmap /opt/postfix/conf/sasl_passwd
postfix reload
exit8. Configure SSL/TLS with OpenResty
Create a website with reverse proxy setup
server {
listen 80;
server_name <your-mail-server-domain>;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name <your-mail-server-domain>;
ssl_certificate /etc/ssl/certs/cert.pem;
ssl_certificate_key /etc/ssl/private/key.pem;
location / {
proxy_pass http://mailcow-nginx-mailcow:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /webmail {
proxy_pass http://mailcow-nginx-mailcow:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Restart OpenResty to apply the changes:
docker restart openresty9. Set Up SPF, DKIM, and DMARC Records
Check details in Cloudflare as well as SendGrid to have your specific configuration.
10 Check the SSL configuration using OpenSSL:
openssl s_client -connect <YOUR_MAILCOW_HOSTNAME>:993 -crlf
openssl s_client -connect <YOUR_MAILCOW_HOSTNAME>:995 -crlf
openssl s_client -connect <YOUR_MAILCOW_HOSTNAME>:465 -crlf
openssl s_client -connect <YOUR_MAILCOW_HOSTNAME>:587 -crlf
Conclusion
By following these steps, you've successfully set up Mailcow on a VPS with SendGrid for outgoing emails and managed SSL/TLS using OpenResty as a reverse proxy. This setup ensures secure and reliable email communication for your domain.