One time I wanted to run a mail server on my QNAP but couldn’t find software that would allow me to deploy my own mail server. I tried to look for other ways to run my server and decided to use Container Station and container based applications. After a short search, I found Mailu mail server, which fits my needs perfectly.
I will now give a brief description of how I planned to run this application. Firstly it was to launch Mailu application using generator from their documentation to create docker-compose.yml file. Then after starting the application the next step was to correctly configure the DNS records for mail service. The final stage would be to provide access to my QNAP, which is running on a LAN and is not exposed to the Internet (for security reasons and recently discovered vulnerabilities). This will be accomplished by setting up a proxy on my nginx web server and port forwarding on my router running on pfSense. In the following post I will describe how this was configured and welcome you to read on.
Installing Mailu on QNAP
The first step is to install the Container Station software on our QNAP. To do this, start AppCenter and enter “container station” in the search box and then click install. After successful installation, you need to create your own application in Container Station. To do this, click Create, then Create Application and enter a name for the application. To create your own application, delete everything and leave only the line: version ‘3’ (this will be changed), but for now you need to create a directory with the application and then click Create.
Now application directory in our case is /share/Container/container-station-data/application/mailutest. FileStation shows that docker-compose.yml file is stored on that location so next step is to create our docker-compose.yml and env files. To do that we may use generator on Mailu site. I share my file below:
# This file is auto-generated by the Mailu configuration wizard.
# Please read the documentation before attempting any change.
# Generated for compose flavor
version: '2.2'
services:
# External dependencies
redis:
image: redis:alpine
restart: always
volumes:
- "/share/Container/container-station-data/application/mailutest/redis:/data"
dns:
- 192.168.203.254
# Core services
front:
image: mailu/nginx:1.9
restart: always
env_file: mailu.env
logging:
driver: json-file
ports:
- "0.0.0.0:81:80"
- "0.0.0.0:444:443"
- "0.0.0.0:25:25"
- "0.0.0.0:465:465"
- "0.0.0.0:587:587"
- "0.0.0.0:110:110"
- "0.0.0.0:995:995"
- "0.0.0.0:143:143"
- "0.0.0.0:993:993"
volumes:
- "/share/Container/container-station-data/application/mailutest/certs:/certs"
- "/share/Container/container-station-data/application/mailutest/overrides/nginx:/overrides:ro"
depends_on:
- resolver
dns:
- 192.168.203.254
resolver:
image: mailu/unbound:1.9
env_file: mailu.env
restart: always
networks:
default:
ipv4_address: 192.168.203.254
admin:
image: mailu/admin:1.9
restart: always
env_file: mailu.env
volumes:
- "/share/Container/container-station-data/application/mailutest/data:/data"
- "/share/Container/container-station-data/application/mailutest/dkim:/dkim"
depends_on:
- redis
- resolver
dns:
- 192.168.203.254
imap:
image: mailu/dovecot:1.9
restart: always
env_file: mailu.env
volumes:
- "/share/Container/container-station-data/application/mailutest/mail:/mail"
- "/share/Container/container-station-data/application/mailutest/overrides/dovecot:/overrides:ro"
depends_on:
- front
- resolver
dns:
- 192.168.203.254
smtp:
image: mailu/postfix:1.9
restart: always
env_file: mailu.env
volumes:
- "/share/Container/container-station-data/application/mailutest/mailqueue:/queue"
- "/share/Container/container-station-data/application/mailutest/overrides/postfix:/overrides:ro"
depends_on:
- front
- resolver
dns:
- 192.168.203.254
antispam:
image: mailu/rspamd:1.9
hostname: antispam
restart: always
env_file: mailu.env
volumes:
- "/share/Container/container-station-data/application/mailutest/filter:/var/lib/rspamd"
- "/share/Container/container-station-data/application/mailutest/overrides/rspamd:/etc/rspamd/override.d:ro"
depends_on:
- front
- resolver
dns:
- 192.168.203.254
# Optional services
# Webmail
webmail:
image: mailu/roundcube:1.9
restart: always
env_file: mailu.env
volumes:
- "/share/Container/container-station-data/application/mailutest/webmail:/data"
- "/share/Container/container-station-data/application/mailutest/overrides/roundcube:/overrides:ro"
depends_on:
- imap
- resolver
dns:
- 192.168.203.254
networks:
default:
driver: bridge
ipam:
driver: default
config:
- subnet: 192.168.203.0/24
As you can see my file was modified with directories which is the same as on my QNAP.
# Mailu main configuration file
#
# This file is autogenerated by the configuration management wizard for compose flavor.
# For a detailed list of configuration variables, see the documentation at
# https://mailu.io
###################################
# Common configuration variables
###################################
# Set to a randomly generated 16 bytes string
SECRET_KEY=xxxxxxxxxxxxxxxxx
# Subnet of the docker network. This should not conflict with any networks to which your system is connected. (Internal and external!)
SUBNET=192.168.203.0/24
# Main mail domain
DOMAIN=bnrs.pl
# Hostnames for this server, separated with comas
HOSTNAMES=mailutest.bnrs.pl
# Postmaster local part (will append the main mail domain)
POSTMASTER=admin
# Choose how secure connections will behave (value: letsencrypt, cert, notls, mail, mail-letsencrypt)
TLS_FLAVOR=mail-letsencrypt
# Authentication rate limit per IP (per /24 on ipv4 and /56 on ipv6)
AUTH_RATELIMIT_IP=60/hour
# Authentication rate limit per user (regardless of the source-IP)
AUTH_RATELIMIT_USER=100/day
# Opt-out of statistics, replace with "True" to opt out
DISABLE_STATISTICS=False
###################################
# Optional features
###################################
# Expose the admin interface (value: true, false)
ADMIN=true
# Choose which webmail to run if any (values: roundcube, rainloop, none)
WEBMAIL=roundcube
# Dav server implementation (value: radicale, none)
WEBDAV=none
# Antivirus solution (value: clamav, none)
ANTIVIRUS=none
###################################
# Mail settings
###################################
# Message size limit in bytes
# Default: accept messages up to 50MB
# Max attachment size will be 33% smaller
MESSAGE_SIZE_LIMIT=50000000
# Message rate limit (per user)
MESSAGE_RATELIMIT=200/day
# Networks granted relay permissions
# Use this with care, all hosts in this networks will be able to send mail without authentication!
RELAYNETS=
# Will relay all outgoing mails if configured
RELAYHOST=
# Fetchmail delay
FETCHMAIL_DELAY=600
# Recipient delimiter, character used to delimiter localpart from custom address part
RECIPIENT_DELIMITER=+
# DMARC rua and ruf email
DMARC_RUA=admin
DMARC_RUF=admin
# Welcome email, enable and set a topic and body if you wish to send welcome
# emails to all users.
WELCOME=false
WELCOME_SUBJECT=Welcome to your new email account
WELCOME_BODY=Welcome to your new email account, if you can read this, then it is configured properly!
# Maildir Compression
# choose compression-method, default: none (value: gz, bz2, lz4, zstd)
COMPRESSION=
# change compression-level, default: 6 (value: 1-9)
COMPRESSION_LEVEL=
# IMAP full-text search is enabled by default. Set the following variable to off in order to disable the feature.
# FULL_TEXT_SEARCH=off
###################################
# Web settings
###################################
# Path to redirect / to
WEBROOT_REDIRECT=/webmail
# Path to the admin interface if enabled
WEB_ADMIN=/admin
# Path to the webmail if enabled
WEB_WEBMAIL=/webmail
# Website name
SITENAME=Mailu bnrs.pl
# Linked Website URL
WEBSITE=https://mailutest.bnrs.pl
###################################
# Advanced settings
###################################
# Log driver for front service. Possible values:
# json-file (default)
# journald (On systemd platforms, useful for Fail2Ban integration)
# syslog (Non systemd platforms, Fail2Ban integration. Disables `docker-compose log` for front!)
# LOG_DRIVER=json-file
# Docker-compose project name, this will prepended to containers names.
COMPOSE_PROJECT_NAME=mailu
# Number of rounds used by the password hashing scheme
CREDENTIAL_ROUNDS=12
# Header to take the real ip from
REAL_IP_HEADER=
# IPs for nginx set_real_ip_from (CIDR list separated by commas)
REAL_IP_FROM=
# choose wether mailu bounces (no) or rejects (yes) mail when recipient is unknown (value: yes, no)
REJECT_UNLISTED_RECIPIENT=
# Log level threshold in start.py (value: CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET)
LOG_LEVEL=WARNING
# Timezone for the Mailu containers. See this link for all possible values https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
TZ=Etc/UTC
###################################
# Database settings
###################################
DB_FLAVOR=sqlite
##################################
# ADMIN # After creating user please remove below lines !!!
##################################
INITIAL_ADMIN_ACCOUNT = admin
INITIAL_ADMIN_DOMAIN = bnrs.pl
INITIAL_ADMIN_PW = xxxxxx
INITIAL_ADMIN_MODE = update
If we have generated files env file must be uploaded to directory on QNAP. To do that you need to upload file or log via ssh and create it. Then edit application and paste content of docker-compose.yml. After that start application.
Let’s check that the application is running in Container Station. I checked this using the docker command and the application is running and the web panel is accessible.
Expose Mailu server to the Internet
The first thing will be to add an MX record in DNS (I won’t write how to do this, as there are many tutorials on the internet about this). The next step is to create port forwarding on the router. In my case, the router is running pfSense, on which I created the port forwarding rules needed to serve mail to my QNAP. Once the forwarding rules are created, the WAN rules are created automatically and can be turned off when needed. I will not describe how to create these rules and feel free to use the documentation as I did 🙂
The next step is to create a proxy rule on the web server, as I only have one in my infrastructure, so the domain I want to use as my mail server (mailutest.bnrs.pl) needs to be redirected to the correct server – in my case it’s the QNAP IP address. I pasted the code below into my nginx configuration and reloaded the server.
server {
listen 80;
listen 443 ssl;
server_name mailutest.bnrs.pl;
access_log /var/log/nginx/bnrs.pl.access.log;
error_log /var/log/nginx/bnrs.pl.error_log;
ssl_certificate /etc/letsencrypt/live/bnrs.pl/cert.pem;
ssl_certificate_key /etc/letsencrypt/live/bnrs.pl/privkey.pem;
location / {
proxy_pass http://192.168.xxx.xxx:81/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
After this configuration, we now have everything we need to run the mail server. Let’s test it in a basic test – sending and receiving emails. As you can see below the mail server is running on my domain.
As you can see from the example above, it is possible to run an email server on a QNAP device. With Container Station, we can take advantage of docker technology and deploy container-based applications. This gives us a lot of possibilities and makes QNAP more useful than just a file storage device. Today, many applications can be run in containers and their popularity continues to grow. I hope you will find the above tutorial useful and it will save you some searching if like me you would like to have a mail server on your QNAP.