NETAPORT MailStack v3 — One-Shot Installer + Full Operations Reference

DNS, DHCP, NTP, SSH, Apache, Nginx, HAProxy, web servers, mail servers, file servers, MySQL, MariaDB, PostgreSQL, Redis, and application hosting.
Post Reply
Murali Krishna
Posts: 38
Joined: Wed Jun 10, 2026 8:34 am

NETAPORT MailStack v3 — One-Shot Installer + Full Operations Reference

Post by Murali Krishna »

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.

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)
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
  • 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
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):

Code: Select all

sudo install -m 750 netaport-healthcheck.sh /usr/local/sbin/netaport-healthcheck
sudo netaport-healthcheck
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:

Code: Select all

sudo install -m 750 netaport-mailctl.sh /usr/local/sbin/netaport-mailctl
Add a domain, then a mailbox:

Code: Select all

netaport-mailctl domain add example.com
netaport-mailctl user add test1@example.com
# → prints a generated password once; save it
Or set your own password and quota:

Code: Select all

netaport-mailctl user add john1@example.com --quota 5120 --password 'S3cret!'
Verify the login actually works — this doubles as your "send a test email" sanity check:

Code: Select all

netaport-mailctl user test test@example.com
# enter the password — "auth succeeded" means the login works
Then log into Roundcube at https://<box-ip>/ with that address and password.

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
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:

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
Then confirm with the same IMAP auth check:

Code: Select all

doveadm auth test test@example.com
# "passdb: ... auth succeeded" = working
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:

Code: Select all

cat /root/netaport-reports/dkim-dns-records.txt
The actual key files and Rspamd's signing config live here:

Code: Select all

ls -l /var/lib/rspamd/dkim/            # the key pair (selector 'mail')
cat /etc/rspamd/local.d/dkim_signing.conf   # signing config
Once DNS is published, verify all three records resolve:

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
Confirm Rspamd is actually validating these on inbound mail (not just that the modules exist):

Code: Select all

ls /etc/rspamd/local.d/ | grep -E 'spf|dkim|dmarc'
rspamadm configtest
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:

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
What it checks, by layer:
  • 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.
Reading the output: green ✓ = good, yellow ! = warning (review but not broken — e.g. POP3 off by choice, a target down), red ✗ = real problem. It ends with a PASS/WARN/FAIL summary and exits 1 on any failure, so it drops straight into cron or a monitoring hook:

Code: Select all

netaport-healthcheck --quiet || echo "stack has problems" | mail -s alert you@example.com
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:

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
To check current settings without changing anything:

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 "^//"
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

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
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

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
Questions, problems with a specific layer, or want the individual numbered scripts walked through one at a time — reply below.
Post Reply