Server Operator Guide
Use this guide for production mail-server operations that sit below the web app: Postfix routing, logs, queues, service health, backups, DNS, and S/MIME runtime readiness.
Scope
The web app manages tenants, users, aliases, S/MIME identities, archives, support requests, and UI workflows. The server operator is responsible for the mail stack that actually accepts and delivers mail.
| Area | Operator responsibility |
|---|---|
| Postfix | Inbound SMTP, authenticated submission, alias maps, queue health, DKIM/milter integration, and delivery logs. |
| Dovecot | IMAP login, mailbox storage, Maildir ownership, and local passdb behavior. |
| DNS | MX, SPF, DKIM, DMARC, PTR, MTA-STS, and TLS-RPT correctness for hosted domains. |
| Secrets | Root-owned application secrets, DKIM private keys, S/MIME master key material, MFA secret access, and backup passphrases. |
| Evidence | Metadata-only mail events, Postfix logs, service status, deployment backups, and restore notes. |
Daily Checks
systemctl --user status email-reseller systemctl is-active postfix dovecot opendkim clamav-milter mailq curl -fsS http://localhost:3000/health
In the app, Super Admins can open the Security or mail-controls areas to review the server's reported mail-layer controls. Treat failed mail-stack services as production issues even when the web app is still running.
Alias Routing
Aliases exist in two places: the app database and the mail stack. The portal can show an alias under Active Aliases while Postfix still lacks the production route. Both must be correct.
On home-directory based mailbox servers, every hosted mailbox and alias should have a full-address route in /etc/postfix/virtual. Do not rely on local-part fallback for hosted domains: addresses such as support@example.com can otherwise deliver to the wrong Unix mailbox when multiple domains use the same local part.
The sync tool refuses shared physical mailbox targets for multiple active users. Aliases may point to the same owning mailbox, but two tenant users must not both collapse to one Unix/Dovecot mailbox such as support.
cd /home/jason/email-servers/emailresellerserver node scripts/sync_postfix_virtual_from_db.js sudo node scripts/sync_postfix_virtual_from_db.js --apply postmap -q support@visasvista.com hash:/etc/postfix/virtual postmap -q jason@mucache.app hash:/etc/postfix/virtual postmap -q no-reply@mucache.app hash:/etc/postfix/virtual
no-reply addresses should not normally route into a human mailbox. For hosted domains where bounces should be suppressed, the full-address lookup should return no virtual route so the local no-reply alias can discard it.
For ongoing protection, configure the app's route-sync hook so alias and mailbox changes update Postfix immediately and fail loudly if the sync cannot run.
sudo bash scripts/install_mail_route_sync_hook.sh jason /home/jason/email-servers/emailresellerserver systemctl --user restart email-reseller jason ALL=(root) NOPASSWD: /usr/bin/node /home/jason/email-servers/emailresellerserver/scripts/sync_postfix_virtual_from_db.js --apply MAIL_ROUTE_SYNC_SCRIPT="sudo -n /usr/bin/node /home/jason/email-servers/emailresellerserver/scripts/sync_postfix_virtual_from_db.js --apply" MAIL_ROUTE_SYNC_CWD=/home/jason/email-servers/emailresellerserver MAIL_ROUTE_SYNC_TIMEOUT=30000
A healthy alias test should show Postfix delivery with the alias in orig_to and the mailbox destination in to.
grep -E 'orig_to=<jason@mucache.app>|to=<support@mucache.app>' /var/log/mail.log
Mail Event Logs
Mail events are metadata-only records for send/receive operations. They are meant to answer whether mail was accepted, queued, rejected, delivered, or routed through an alias without storing message bodies or attachments.
Import Recent Postfix Events
cd /home/jason/email-servers/emailresellerserver node scripts/import-mail-events.js --since-minutes=1440 /var/log/mail.log
Query Recent Events
cd /home/jason/email-servers/emailresellerserver
node -e "const db=require('./src/database/db');(async()=>{const r=await db.query('SELECT event_time,direction,source,queue_id,envelope_from,envelope_to,original_recipient,status,dsn FROM mail_events ORDER BY event_time DESC LIMIT 20');console.table(r.rows);})().finally(()=>db.pool.end())"
The importer is safe to rerun. It deduplicates events by source line hash. Webmail and outbound API sends are recorded by the app when SMTP acceptance succeeds or when the send attempt fails.
Queues
mailq postqueue -p postcat -q QUEUE_ID
Use postcat only when a deeper investigation requires it. Queue inspection can expose message content, so keep it limited and avoid copying message bodies into tickets or logs.
Services
| Service | Healthy state | What to check when unhealthy |
|---|---|---|
| email-reseller | User service active and /health returns OK. | Application logs, database credentials, migrations, and restored .env. |
| postfix | Active, listening on port 25, queue moving. | MX delivery, relay restrictions, virtual maps, milters, and logs. |
| dovecot | Active, IMAPS on 993, auth works. | Passdb, Maildir ownership, TLS files, and auth logs. |
| opendkim | Active and signing configured domains. | Signing table, key table, private-key permissions, and selector DNS. |
| clamav-milter | Active and listening where Postfix expects it. | Stale processes, milter socket/port, ClamAV daemon status, and timeout behavior. |
DNS
dig MX mucache.app dig TXT mucache.app dig TXT default._domainkey.mucache.app dig TXT _dmarc.mucache.app dig -x SERVER_IP
Preserve domain-specific production records in docs when onboarding a new domain. Current domain-specific runbooks include docs/PASSAGEWATCHTV_MAIL_SETUP.md for passagewatchtv.com.
For customer-facing DNS steps, use the Tenant Domain Setup Guide. Operators should also confirm PTR/rDNS with the IP provider because tenants usually cannot change it themselves.
WebAuthn / YubiKey Login
Security-key login uses browser WebAuthn/FIDO2. Users enroll a YubiKey from the portal MFA settings, and the server stores only credential public-key metadata for later challenge verification.
WEBAUTHN_RP_NAME=Secure Mail Portal WEBAUTHN_RP_ID=mail.visasvista.com WEBAUTHN_ORIGIN=https://mail.visasvista.com WEBAUTHN_CHALLENGE_TTL_MINUTES=5
WEBAUTHN_RP_ID must be the browser host domain without scheme or port. WEBAUTHN_ORIGIN must exactly match the HTTPS origin users visit. If the portal hostname changes, users may need to enroll keys again for the new relying party.Break-Glass Admin Recovery
Use this only when a Super Admin is locked out because MFA, WebAuthn, or YubiKey login is unavailable. Recovery requires SSH access to the production server; it is not exposed as a public web action.
Keep SSH tunnel recovery settings in a private .env.recovery.local file or an operator-managed .env. The Windows ssh_tunnel_admin.bat helper reads those files and should not contain real usernames, hosts, ports, admin emails, keys, or passwords.
Open a Local SSH Tunnel
ssh_tunnel_admin.bat ssh_tunnel_admin.bat --show ssh -N -L 9898:localhost:3000 your-ssh-alias
Then open http://localhost:9898 from the operator workstation. Keep the SSH session open while testing login.
Inspect Super Admin Recovery State
cd /home/your-ssh-user/email-servers/emailresellerserver node scripts/break-glass-admin.js --list node scripts/break-glass-admin.js --email=admin@example.com --dry-run
Recover a Locked Super Admin
cd /home/your-ssh-user/email-servers/emailresellerserver node scripts/break-glass-admin.js \ --email=admin@example.com \ --disable-webauthn \ --invalidate-sessions \ --confirm=admin@example.com
If the account is also locked by authenticator-app MFA, add --disable-mfa. If the account was disabled, add --set-active. If the password is unknown, use node scripts/reset-admin-password.js --email=admin@example.com after reviewing the dry run.
S/MIME Runtime
Server-side S/MIME signing requires SMIME_MASTER_KEY and an available openssl runtime. PKCS#12/PFX import also requires OpenSSL and the uploaded bundle must include the user's certificate plus matching private key.
openssl version systemctl --user show email-reseller --property=Environment
ykman piv certificates export --format pem 9c jason@mail.visasvista.com.pem, using the certificate owner's email address in the filename, and add it as a recipient certificate. A local sidecar can safely help detect OpenSC/Yubico PIV tooling, enumerate PIV certificate slots, export the public certificate, and upload that public certificate to the portal. It must not attempt to extract a non-exportable private key.Archive Mounts
Network-share archive storage should be prepared by the server operator before tenant admins can choose it in the web interface. Mount approved shares under a controlled path such as /mnt/email-reseller-archives; do not let the web app accept arbitrary server paths or raw Samba credentials from users.
sudo mkdir -p /mnt/email-reseller-archives/customer-a sudo mount -t cifs //fileserver/customer-a-archives /mnt/email-reseller-archives/customer-a \ -o credentials=/etc/email-reseller/samba-customer-a.cred,uid=jason,gid=jason,dir_mode=0750,file_mode=0640,vers=3.0 findmnt /mnt/email-reseller-archives/customer-a
Backups
Use secure backups for disaster recovery. Standard deployments also create a pre-deploy backup under /home/jason/email-servers/backups.
cd /home/jason/email-servers/emailresellerserver bash scripts/secure_backup.sh
Protect backup passphrases separately from backup archives. Secure backups may include database data, S/MIME/MFA secret access, Dovecot users, DKIM keys, mail-stack configuration, and optional Maildir contents.
Troubleshooting
| Symptom | First checks |
|---|---|
| Alias visible but mail missing | Check /etc/postfix/virtual, postmap -q, Postfix logs, and imported mail_events for orig_to. |
| External sender gets bounce | Check MX, local recipient table, virtual alias maps, and reject lines in /var/log/mail.log. |
| Outbound stuck | Check mailq, SMTP authentication, DKIM/milter status, DNS reputation, and remote deferral responses. |
| Users cannot log in by IMAP | Check Dovecot status, passdb entries, password sync, TLS, and client username format. |
| S/MIME unavailable | Check SMIME_MASTER_KEY, OpenSSL, certificate validity, and certificate email match. |