NETAPORT MailStack v3 — One-Shot Installer + Full Operations Reference
Posted: Sat Jun 20, 2026 4:08 am
NETAPORT MailStack v3 — One-Shot Installer + Full Operations Reference
Everything needed to stand up the complete NETAPORT mail stack on AlmaLinux 9 from a single git clone, plus the day-2 commands for managing domains/mailboxes, checking DKIM/SPF/DMARC, reading the Maildir layout, running the health check, and fixing Roundcube if login breaks. One thread, start to finish.
The full source is on GitHub: github.com/redhatmurali/MailServer-Setup
──────────────────────────────────────────────
What's in the repo
19 scripts, numbered to run in order. 00-run-all.sh is the orchestrator — it calls 01 through 17 in sequence, so a fresh box goes from bare AlmaLinux 9 to a fully running, monitored, secured mail stack in one invocation. The two netaport-* tools at the bottom are day-2 admin utilities, not part of the install sequence.
Run the numbered scripts in order if you ever need to re-run a single layer (e.g. just 06-rspamd.sh after a config change). For a fresh install, 00-run-all.sh is the only command you need.
──────────────────────────────────────────────
Step 1: Prerequisites
Step 2: Clone and run
Read every script before running it on a production box, especially if you're adapting it to a domain or network layout that isn't yours. The 01-prerequisites.sh through 17-backup-validation.sh scripts each handle one layer and are individually re-runnable if you need to redo a step.
Note: expect this to take a while — MariaDB, Postfix, Dovecot, Rspamd, ClamAV (with virus DB download), Roundcube, the full Prometheus/Grafana/Loki monitoring stack, CrowdSec, and AIDE/rkhunter all install and configure in sequence. ClamAV's database sync in particular can take several minutes depending on your connection.
──────────────────────────────────────────────
Step 3: Confirm it's healthy
Once 00-run-all.sh finishes, install and run the health check (covered in full further down):
Don't move on to DNS/mailbox setup until this comes back PASS (warnings are fine — see the health check section for which ones are expected).
──────────────────────────────────────────────
═══ OPERATIONS REFERENCE ═══
──────────────────────────────────────────────
Step 4: Domain & mailbox management (netaport-mailctl)
Install once:
Add a domain, then a mailbox:
Or set your own password and quota:
Verify the login actually works — this doubles as your "send a test email" sanity check:
Then log into Roundcube at https://<box-ip>/ with that address and password.
Everyday operations:
Note: full command reference (domain/user/alias add/list/enable/disable/del, quota, passwd, test) was covered in detail in the dedicated netaport-mailctl thread — this section is the quick-start subset for day-to-day use.
──────────────────────────────────────────────
Step 5: Manual mailbox insert (if you ever need to bypass mailctl)
Normally netaport-mailctl handles this, but if you ever need to insert a mailbox directly:
Then confirm with the same IMAP auth check:
This bypasses the validation and confirmation prompts netaport-mailctl gives you, so prefer the CLI tool for normal operations — this is here for troubleshooting only.
──────────────────────────────────────────────
Step 6: DKIM, SPF & DMARC — where to check each one
DKIM — the keypair and DNS record were generated for you during install. The exact TXT record to publish is in the install report:
The actual key files and Rspamd's signing config live here:
Once DNS is published, verify all three records resolve:
Confirm Rspamd is actually validating these on inbound mail (not just that the modules exist):
If dig comes back empty, that's a DNS publishing problem, not a server problem — the records were generated locally; you still have to paste them into your DNS provider.
──────────────────────────────────────────────
Step 7: Mailbox layout & useful commands
On-disk Maildir structure per mailbox:
──────────────────────────────────────────────
Step 8: Health check (netaport-healthcheck)
What it checks, by layer:
Note: the Redis and MariaDB ping checks need the secrets file to be readable, so run as root. If Redis shows a false failure, check that first. The TLS banner checks need openssl (present on EL9 by default) and may need a longer timeout on a slow box.
──────────────────────────────────────────────
Step 9: Roundcube won't log in? Fix the IMAP/SMTP host
If Roundcube can't reach IMAP/SMTP (common after a hostname or cert change), point it back at localhost instead of the external TLS hostname:
To check current settings without changing anything:
A backup (config.inc.php.bak) is taken automatically by the cp command above before any sed edit — keep that habit if you adjust this by hand. This swaps Roundcube to connect over plaintext loopback (localhost:143 / localhost:587) instead of the external TLS hostname/ports, which sidesteps cert/hostname mismatches since the connection never leaves the box.
──────────────────────────────────────────────
Step 10: Misc credential lookups
Note: /etc/netaport/netaport-secrets.env is the single source of truth for every generated credential on this stack. Treat it like a vault — root-readable only, never copy its contents into a forum post or ticket.
──────────────────────────────────────────────
Quick-reference: the whole lifecycle in one block
Questions, problems with a specific layer, or want the individual numbered scripts walked through one at a time — reply below.
Everything needed to stand up the complete NETAPORT mail stack on AlmaLinux 9 from a single git clone, plus the day-2 commands for managing domains/mailboxes, checking DKIM/SPF/DMARC, reading the Maildir layout, running the health check, and fixing Roundcube if login breaks. One thread, start to finish.
The full source is on GitHub: github.com/redhatmurali/MailServer-Setup
──────────────────────────────────────────────
What's in the repo
19 scripts, numbered to run in order. 00-run-all.sh is the orchestrator — it calls 01 through 17 in sequence, so a fresh box goes from bare AlmaLinux 9 to a fully running, monitored, secured mail stack in one invocation. The two netaport-* tools at the bottom are day-2 admin utilities, not part of the install sequence.
Code: Select all
00-run-all.sh orchestrator — runs everything below in order
01-prerequisites.sh base packages, repos (CRB, EPEL), firewalld, SELinux prep
02-mariadb.sh MariaDB install + mailserver DB schema + mailadmin account
03-postfix.sh Postfix (SMTP, submission, virtual mailbox maps)
04-dovecot.sh Dovecot (IMAP/POP3, SQL passdb/userdb, Maildir)
05-redis.sh Redis (Rspamd backend, session cache)
06-rspamd.sh Rspamd (filtering, DKIM signing, SPF/DMARC checks)
07-clamav.sh ClamAV (clamd@scan, milter integration)
08-roundcube.sh Roundcube webmail + Nginx vhost
09-prometheus.sh Prometheus
10-grafana.sh Grafana + Nginx /grafana/ reverse proxy
11-exporters.sh node/redis/mysqld/blackbox/postfix exporters
12-alertmanager.sh Alertmanager
13-loki-promtail.sh Loki + Promtail log pipeline
14-crowdsec.sh CrowdSec + firewall bouncer
15-aide-rkhunter.sh AIDE + rkhunter scheduled scans
16-hardening.sh SELinux booleans, sshd, sysctl, firewalld zones
17-backup-validation.sh backup timer + restore validation
netaport-healthcheck.sh (admin tool, not part of install sequence)
netaport-mailctl.sh (admin tool, not part of install sequence)
──────────────────────────────────────────────
Step 1: Prerequisites
- A clean AlmaLinux 9 host (minimal install is fine), reachable as root
- A domain you control, with the ability to edit DNS (MX, SPF, DKIM, DMARC records come later in the guide)
- git installed — if it isn't: dnf install -y git
- Outbound internet access for package repos (CRB, EPEL, MariaDB/Grafana/Prometheus upstream repos as needed by each script)
Step 2: Clone and run
Code: Select all
cd /opt
git clone https://github.com/redhatmurali/MailServer-Setup.git
cd MailServer-Setup
chmod +x *.sh
# Full one-shot install — runs 01 through 17 in order
MAIL_FQDN=mail.example.com PRIMARY_DOMAIN=example.com ./00-run-all.sh
Note: expect this to take a while — MariaDB, Postfix, Dovecot, Rspamd, ClamAV (with virus DB download), Roundcube, the full Prometheus/Grafana/Loki monitoring stack, CrowdSec, and AIDE/rkhunter all install and configure in sequence. ClamAV's database sync in particular can take several minutes depending on your connection.
──────────────────────────────────────────────
Step 3: Confirm it's healthy
Once 00-run-all.sh finishes, install and run the health check (covered in full further down):
Code: Select all
sudo install -m 750 netaport-healthcheck.sh /usr/local/sbin/netaport-healthcheck
sudo netaport-healthcheck
──────────────────────────────────────────────
═══ OPERATIONS REFERENCE ═══
──────────────────────────────────────────────
Step 4: Domain & mailbox management (netaport-mailctl)
Install once:
Code: Select all
sudo install -m 750 netaport-mailctl.sh /usr/local/sbin/netaport-mailctl
Code: Select all
netaport-mailctl domain add example.com
netaport-mailctl user add test1@example.com
# → prints a generated password once; save it
Code: Select all
netaport-mailctl user add john1@example.com --quota 5120 --password 'S3cret!'
Code: Select all
netaport-mailctl user test test@example.com
# enter the password — "auth succeeded" means the login works
Everyday operations:
Code: Select all
netaport-mailctl user list # all mailboxes
netaport-mailctl user list example.com # just one domain
netaport-mailctl user passwd test@example.com # reset password
netaport-mailctl user quota test@example.com 2048
netaport-mailctl user disable test@example.com
netaport-mailctl domain list # domains + user/alias counts
netaport-mailctl alias add info@example.com test@example.com # forwarding
──────────────────────────────────────────────
Step 5: Manual mailbox insert (if you ever need to bypass mailctl)
Normally netaport-mailctl handles this, but if you ever need to insert a mailbox directly:
Code: Select all
# 1. Generate a password hash (prompts for the mailbox password twice)
doveadm pw -s SHA512-CRYPT
# copy the output, looks like: {SHA512-CRYPT}$6$xxxx....
# 2. Pull the DB credentials the app user uses
grep MAILADMIN_DB_PASSWORD /etc/netaport/netaport-secrets.env
# 3. Insert the user (replace <HASH> and <DBPASS>)
mysql -u mailadmin -p'<DBPASS>' mailserver <<'SQL'
INSERT INTO virtual_users (domain_id, email, password)
VALUES (
(SELECT id FROM virtual_domains WHERE name='example.com'),
'test@example.com',
'<HASH>'
);
SQL
Code: Select all
doveadm auth test test@example.com
# "passdb: ... auth succeeded" = working
──────────────────────────────────────────────
Step 6: DKIM, SPF & DMARC — where to check each one
DKIM — the keypair and DNS record were generated for you during install. The exact TXT record to publish is in the install report:
Code: Select all
cat /root/netaport-reports/dkim-dns-records.txt
Code: Select all
ls -l /var/lib/rspamd/dkim/ # the key pair (selector 'mail')
cat /etc/rspamd/local.d/dkim_signing.conf # signing config
Code: Select all
# DKIM
dig +short TXT mail._domainkey.example.com
# SPF
dig +short TXT example.com | grep spf1
# DMARC
dig +short TXT _dmarc.example.com
Code: Select all
ls /etc/rspamd/local.d/ | grep -E 'spf|dkim|dmarc'
rspamadm configtest
──────────────────────────────────────────────
Step 7: Mailbox layout & useful commands
On-disk Maildir structure per mailbox:
Code: Select all
/var/vmail/example1.com/test/
├── cur/ ← read/seen messages (one file per email)
├── new/ ← newly delivered, unread
├── tmp/ ← in-flight during delivery
├── .Sent/ ← the Sent folder (Maildir++ subfolders are dot-prefixed)
├── .Drafts/
├── .Junk/
├── .Trash/
└── dovecot-uidlist, dovecot.index* ← Dovecot's index files
Code: Select all
# See all mailboxes and their sizes
du -sh /var/vmail/*/*
# Count messages in a specific mailbox (inbox)
ls /var/vmail/example1.com/test/cur/ /var/vmail/example1.com/test/new/ 2>/dev/null | wc -l
# Dovecot's own view (cleaner — shows folders, counts, sizes)
doveadm mailbox status -u test@example1.com 'messages vsize' '*'
# Where a user's mail physically lives, per Dovecot
doveadm user -f home test@example1.com
Step 8: Health check (netaport-healthcheck)
Code: Select all
sudo install -m 750 netaport-healthcheck.sh /usr/local/sbin/netaport-healthcheck
sudo netaport-healthcheck
- Services — active and enabled-at-boot for MariaDB, Redis, Postfix, Dovecot, Rspamd, ClamAV (clamd@scan), Nginx, PHP-FPM, Prometheus, Grafana, Alertmanager, Loki, Promtail, all five exporters, CrowdSec + bouncer, firewalld. Also confirms Fail2Ban is inactive (correct — CrowdSec is the active defender) and SELinux is Enforcing.
- IMAP/POP3 — checks 143/993 (IMAP/IMAPS) and explicitly reports POP3/POP3S as ENABLED or DISABLED based on whether 110/995 are listening. POP3 is optional in this build, so it's a warning (not a failure) when off.
- Live protocol banners — not just "is the port open" but "does the daemon actually answer": opens real connections and checks for the SMTP 220 greeting, IMAP/POP3 OK greetings, and IMAPS/POP3S banners over TLS via openssl. Catches a service that's bound but wedged.
- Backends — Rspamd milter (11332) and controller (11334) loopback-only, ClamAV socket present, Redis PING→PONG, MariaDB ping.
- Web — HTTPS responds, Roundcube returns 200/302, /grafana/ proxy works.
- Monitoring — Prometheus targets up vs down, Loki /ready, Grafana health.
- Timers — backup, AIDE, rkhunter, dnf-automatic, cert renewal.
Code: Select all
netaport-healthcheck --quiet || echo "stack has problems" | mail -s alert you@example.com
──────────────────────────────────────────────
Step 9: Roundcube won't log in? Fix the IMAP/SMTP host
If Roundcube can't reach IMAP/SMTP (common after a hostname or cert change), point it back at localhost instead of the external TLS hostname:
Code: Select all
cd /var/www/roundcube/config
cp config.inc.php config.inc.php.bak
sed -i "s|\$config['imap_host'] = 'ssl://mail.example.com:993';|\$config['imap_host'] = 'localhost:143';|" config.inc.php
sed -i "s|\$config['smtp_host'] = 'tls://mail.example.com:587';|\$config['smtp_host'] = 'localhost:587';|" config.inc.php
# verify
grep -E "imap_host|smtp_host" config.inc.php
Code: Select all
grep -E "imap_host|imap_port|smtp_host|smtp_port" /var/www/roundcube/config/config.inc.php
grep -E "imap_host|imap_port|smtp_host|smtp_port|ssl|verify" /var/www/roundcube/config/config.inc.php | grep -v "^//"
──────────────────────────────────────────────
Step 10: Misc credential lookups
Code: Select all
# Grafana admin password
grep GRAFANA_ADMIN_PASSWORD /etc/netaport/netaport-secrets.env
# Mailadmin DB password (used by netaport-mailctl and manual SQL)
grep MAILADMIN_DB_PASSWORD /etc/netaport/netaport-secrets.env
──────────────────────────────────────────────
Quick-reference: the whole lifecycle in one block
Code: Select all
# 1. Install
cd /opt && git clone https://github.com/redhatmurali/MailServer-Setup.git
cd MailServer-Setup && chmod +x *.sh
sudo ./00-run-all.sh
# 2. Confirm healthy
sudo install -m 750 netaport-healthcheck.sh /usr/local/sbin/netaport-healthcheck
sudo netaport-healthcheck
# 3. Add a domain + mailbox
sudo install -m 750 netaport-mailctl.sh /usr/local/sbin/netaport-mailctl
netaport-mailctl domain add example.com
netaport-mailctl user add test1@example.com
# 4. Verify login
netaport-mailctl user test test1@example.com
# 5. Publish DNS, then verify
cat /root/netaport-reports/dkim-dns-records.txt
dig +short TXT mail._domainkey.example.com
dig +short TXT example.com | grep spf1
dig +short TXT _dmarc.example.com