Repository navigation
Secure Defaults
Source of truth: hmailserver/docs/SecureDefaults.md, kept in the repository and copied here. Copied on 27 September 2026 from the document as the tag v6.3.4 (51e2dbda0) has it, again on 5 October 2026 from the document as the tag v6.3.6 (e3c448aa6) has it (SecureDefaults.md at v6.3.6): port 25 is a Honeypot on a new installation, which takes no password there and bans an address that offers one; and on 18 October 2026 from the document as the tag v6.3.7 (31d656c0c), the release, tagged and published on 17 October 2026, has it (SecureDefaults.md at v6.3.7): the Control Panel's caption for the setting is Require TLS before authentication, in the range's dialog too. The document is new in 6.3.4. Security Hardening §17.1 and Encryption and Certificates give the short version. Its links to the repository's other documents point at them as the tag has them, or at this wiki's copy where there is one. Re-copied on 26 October 2026 from the document as
masterhas it from batch55, 36bd59faa, onmastersince 26 October 2026 (SecureDefaults.md at 36bd59faa), which changes it in two places: The certificate says that a host added toAcmeDomainslater is issued for at once, which is so from the release after 6.3.7 (up to 6.3.7 a host added waits until the certificate in use is due for renewal: Encryption and Certificates, When it renews); and Where it runs says that from the release after 6.3.7 the Compose file and the Helm chart run--apply-new-install-defaults --if-markedbefore the server, so that their first start secures the database the image's entrypoint has just created, that the Compose file also gives the REST listener the pair this makes, and that the charts of 6.3.6 and 6.3.7 do not run it, a container started otherwise being one of those secured by hand. The document was the same at batch54, 9336fdd59, as at the tag v6.3.7, from which this copy was last made. Its links to the repository's other documents point at them as 36bd59faa has them, where they pointed at the tag, or at this wiki's copy where there is one.
Until 25 September 2026 a new installation listened on ports 25, 587, 110 and 143 with no TLS at all, and neither IP range it shipped with required TLS for authentication - so a mail client that signed in from another machine sent its password across the network in the clear, from the first minute, until an administrator noticed. The create scripts still make exactly that database. What changed is what the installer does with it, and what the Linux packages' first start does with it: before the server first listens, a new installation is given a certificate, STARTTLS on every listener, and a rule that a password from anywhere but this computer waits for TLS.
| Before | A new installation now | An upgraded server | |
|---|---|---|---|
| Port 25, SMTP | no TLS | STARTTLS offered, optional; SMTP authentication Honeypot: AUTH offered, refused without the password being looked at, the address banned (SmtpAuthOnDeliveryPorts.md) |
as it was, AUTH On; the checklist flags it |
| Port 587, SMTP submission | no TLS | STARTTLS offered, optional | as it was |
| Port 110, POP3 | no TLS | STARTTLS (STLS) offered, optional | as it was |
| Port 143, IMAP | no TLS | STARTTLS offered, optional | as it was |
| IP range Internet (every IPv4 address) | a password taken without TLS | TLS required for authentication | as it was; the checklist flags it |
| IP range My computer (127.0.0.1) | a password taken without TLS | unchanged | as it was |
| Certificates | none | Self-signed (made at installation) | as they were |
So, on a new installation:
-
From another machine,
AUTHon SMTP is not offered before STARTTLS and is refused with530 A SSL/TLS-connection is required for authentication.if it is sent; IMAPLOGINandAUTHENTICATEanswerBAD A SSL/TLS-connection is required for authentication.; POP3USERandAUTHanswer-ERR. After STARTTLS, every one of them works as it always has. - From this computer - a webmail on the same machine, a script, a monitoring check on 127.0.0.1 - nothing has changed.
-
Mail from other servers on port 25 is taken with or without TLS, as before.
Nobody signs in there: an
AUTHon port 25 is answered502 5.5.1and bans the address that sent it forAutoBanMinutes- never this computer, nor an address onAutoBanNeverBan. Mail clients sign in on 587.
On port 25 a requirement would refuse mail from every server that cannot start TLS, which is still a real share of the internet's senders. On 587, 110 and 143 it would refuse this computer's own clients too, because a port's requirement applies to every address. The range's rule is the one that protects the password: it refuses a password before TLS from every address but the loopback, on every listener, and it is the one an administrator can see and change in one place. An administrator who wants a port to refuse everything before TLS sets the port to STARTTLS (required), as always.
<data directory>/Certificates/self-signed-certificate.pem and its key
self-signed-key.pem (C:\Program Files\hMailServer\Data\Certificates on a
default Windows installation, /var/lib/hmailserver/Certificates from the Linux
packages), recorded as the certificate Self-signed (made at installation).
- RSA 2048, which every mail client of the last fifteen years takes.
- 825 days, the longest a TLS server certificate may run and still be taken by Apple's clients when somebody has chosen to trust it.
- The subject names the host, as its common name and its subjectAltName, and gives the organisation as hMailServer, self-signed at installation. The host is the host name setting (the name the server gives itself in its greetings) when one is set, else the machine's DNS name; the organisation says where the certificate came from, so that the warning a mail client shows names something recognisable.
-
The key is written readable by the server's account and the machine's
administrators only - on Windows a protected ACL naming SYSTEM, the
Administrators group, the account that wrote it and, when the service logs on
as anything but LocalSystem (a virtual account such as
NT SERVICE\hMailServer, orServiceAccountName), that account, read from the service controller; on Linux mode 0600 owned by the service's user - as the DKIM rotation's keys are. A key that account could not be given is removed, and nothing is changed. The Windows installer runs the verb while the service is still LocalSystem's, so the key it writes names SYSTEM, the Administrators and the administrator who installed; the move toNT SERVICE\hMailServerthat follows grants that account Read on the key, as it grants the key of every certificate the database names (/GrantServiceAccess, The Service Account).
Mail clients warn about it. It encrypts - nobody on the network reads the password - but nothing vouches for it, so a client asks its user to accept it the first time, and a user taught to click through that warning is a user who will click through the one an attacker's certificate raises. Replace it:
-
With ACME (Let's Encrypt), the easy way: set
AcmeEnabled,AcmeContactEmailandAcmeDomains, publish the names and let the challenge port be reached. When the first certificate arrives the ACME client gives it to every listener that holds the self-signed one, as it gives it to a TLS listener with none - and leaves alone any listener holding a certificate somebody chose. A host added toAcmeDomainslater is issued for at once (Acme.md). - With your own: add it on the certificates page (Control Panel: SSL certificates; Deck: Certificates) and set it on each port.
Until then the setup checklist's TLS on the submission port says so
(submission_self_signed, below), rather than calling the port done.
The defaults are applied to a database this installation created, and never to one that existed before, whatever its size. A small server in service - three accounts, the stock listeners and ranges, never given a certificate - looks from its configuration alone exactly like a new one, and requiring TLS for its sign-ins would lock out every client it has that has never used TLS.
So the create step marks the database it makes, with the hm_settings row
NewInstallDefaultsPending, and nothing else writes it:
| Writes the mark | Does not |
|---|---|
COM Database.CreateInternalDatabase - DBSetupQuick making the built-in database |
COM Database.SetDefaultDatabase - DBSetup attaching a database that already exists |
COM Database.CreateExternalDatabase - DBSetup when it creates the database |
any upgrade, by DBUpdater, --upgrade-database or the server's own |
hmailserver --create-database when it runs the create script |
--create-database finding a database that already holds a schema |
The verb requires the mark and removes it once it has decided, either way, so a database is weighed once. The mark carries the time it was made, and the Linux unit's first start does not act on a mark made more than a day from its clock, either way (exit 5, the file removed, the mark kept): the unit runs at the first start, so an older mark belongs to a server that has run without these defaults since - a first start that never reached its database, or an image built with the database and first booted days later - and whose clients, a script relaying through it among them, may have come to depend on listeners without TLS. A mark from more than a day ahead is a clock that was wrong when the database was made or is wrong now - one that jumps forward when the time is first synchronised, say - and cannot say how old the database is either. A run by hand has no such limit, and neither has the installer's, a minute after its create: it is somebody asking for the defaults now, and the mark the unit kept is there for it. And it still asks the shape the create script leaves:
- the four listeners - SMTP 25 and 587, POP3 110, IMAP 143 - on every address, with no TLS, no certificate, no client certificate asked for and SMTP authentication On, and no fifth;
- the two IP ranges with the options, priorities and addresses the create scripts give them, and no third;
- no certificate at all;
- no account, no domain and no route - none is in a create script - the sample domain and its accounts not counted.
Anything else - no mark, or a database somebody has already configured, signed in to or routes mail through - is left as it is: the verb says which it found and exits 5. A database it cannot read well enough to tell is not judged at all, and neither is one whose listeners and ranges it cannot read again just before it changes them: exit 2, the mark and the Linux file kept, and the next run asks again. The Internet range is set only once all four listeners offer STARTTLS. The mark is a record of the database's history, not a setting: it is in no create script, has no default, is not in the list of what is changed from stock, and is left out of the XML backup, so that a restore never carries it onto another database.
An upgrade is never changed: its database has no mark. The installer does
not even run the verb for one (IsNewDatabase: the settings file had no database
before the installation), and on Linux --create-database marks nothing it did
not create. A server upgraded from any earlier release keeps its listeners and
its ranges, and the setup checklist tells its administrator what is left (below).
The Windows installer, after the database step of a new installation and
before the profile the last page chose, the stored-secrets key, the move to
NT SERVICE\hMailServer and the service's start - with the
service stopped, since the verb refuses to change a running server (exit 4). On
the built-in database's branch, DBSetupQuick has just created the database, so
anything but 0 is said in a message box (suppressed by /SUPPRESSMSGBOXES). On
the external database's branch the installer cannot see whether DBSetup created
the database or attached one - its exit code is 0 for both - and the mark decides:
a database DBSetup created carries it and is secured; one it attached does not,
and the verb's exit 5 is only logged. Every result goes to Inno's log. The
message offers running the verb by hand only where that still would do it - the
mark untouched - and after an exit 5 or 1 points to the checklist instead,
saying for an exit 1 that where the error log says nothing was changed, a second
run still applies them. A
database COM created but could not mark is HM7512 in the error log, which is
then the reason for the exit 5 that follows. The installation succeeds either way.
When the database step of a new installation did not complete, none of this has
run: setup run again, or DBSetup.exe run as an administrator, sets the database
up and then runs the verb the same way, with the service stopped
(The Service Account, "A new installation whose database step
did not complete"); until 6.3.6 DBSetup did not, and the database kept its mark
with no certificate made.
The Linux packages (.deb, .rpm). The packages do not create a database:
the administrator names one in [Database] and runs
sudo -u hmailserver hmailserver --create-database, which creates it (or fills
an empty one), marks it, and leaves a one-line file, .new-installation, in the
DataFolder the configuration names - /var/lib/hmailserver as shipped, or
wherever the administrator moved it before this step. A database that was already
there - a reinstall, a purge and reinstall, a move to a new machine - gets
neither. The unit runs, before every start,
ExecStartPre=-/usr/bin/hmailserver --apply-new-install-defaults --if-marked
TimeoutStartSec=300--if-marked with no file looks for .new-installation in the DataFolder of
the same configuration, /etc/hmailserver/hMailServer.ini, found by reading its
DataFolder and ProgramFolder and nothing else - not the whole configuration,
whose loader would write its reports (a PreferredHashAlgorithm it does not take,
HM5528 or HM5607) into the error log a second time at every start; the first
start of a new installation, which loads it to reach the database, is the one
time they are. It still takes a file named after it
(--if-marked /path/to/file), which it then looks for instead. The step exits at
once, opening nothing, when the file is not there - every start but the first
after --create-database - and otherwise tries the database once, applies the
defaults to a database marked within a day of its clock, and removes the file. It runs as the service's
user, so the key it writes is the service's. A database that cannot be reached at
that one try leaves the file, so the next start asks again, and the server's own
start says what is wrong with the database; TimeoutStartSec gives the one try
room, since the - forgives a failure and not a timeout.
A DataFolder outside the unit's ReadWritePaths (/var/lib/hmailserver
and /var/log/hmailserver) is read-only to the service - /home is not even
visible to it (ProtectHome=yes) - so the first start can neither write the
certificate under it nor remove the file, and the server cannot write its mail
there either. Add the directory with a drop-in before that first start:
sudo systemctl edit hmailserver
# [Service]
# ReadWritePaths=/srv/mail--create-database says which of these it found when it made the database: that
the first start under systemd secures it, if it comes within a day of
--create-database, and a run by hand after that; that it will once the unit may write to
the data directory; that the OpenRC service's next start does (below); or - with a
configuration other than the packaged one, or on a machine neither systemd runs
nor OpenRC is installed on, such as a container - that nothing will do it by
itself, and the command that does.
The Arch package installs the same unit, and --create-database is the same
command there, so its first start does the same; it needs no maintainer script
for it.
The Alpine package's OpenRC service (/etc/init.d/hmailserver,
Alpine.md) runs the same --apply-new-install-defaults --if-marked
as the service's account before every start, so its next start does the same
within the same day. --create-database says so where openrc-run and that
service are installed and the configuration is the packaged one; OpenRC has no
ReadWritePaths to add a moved DataFolder to.
The Compose file and the Helm chart
(ContainerImage.md) run the same verb, from the release
after 6.3.7: their container's command is hmailserver --config /etc/hmailserver/hMailServer.ini --apply-new-install-defaults --if-marked; exec hmailserver --config /etc/hmailserver/hMailServer.ini --foreground, after the
image's entrypoint has created the database and stored the environment's
settings, as the image's user. The ; forgives a failure, as the unit's -
does. So their first start secures the database the entrypoint's
--create-database made a moment before, although that step says nothing will:
the image alone does not, and the step cannot tell what runs it. The Compose file
also gives the REST listener the pair this makes. The charts of 6.3.6 and 6.3.7 do
not run it.
By hand, for anything else - the AppImage, a container started otherwise than
by the Compose file or the chart, a server started
by hand rather than by the unit, or a database whose defaults were left for
later - with the service stopped, and on Linux as the service's user: the verb
refuses to run as root, whose key the service could not read. With a
configuration other than the packaged one, name it (--config) before the verb.
Stop-Service hMailServer
& "C:\Program Files\hMailServer\Bin\hMailServer.exe" --apply-new-install-defaults
Start-Service hMailServersudo systemctl stop hmailserver
sudo -u hmailserver hmailserver --apply-new-install-defaults
sudo systemctl start hmailserverBy hand it too changes only a marked database: one made by the create step whose
defaults were never decided - however long ago, since a run by hand has no
deadline: an image first booted days after its --create-database, whose first
start left the database alone, is secured this way. An upgraded server cannot be given these defaults
by the verb, and neither can one the verb has already answered (exit 5, or an
exit 1 part way): the checklist's Passwords only over TLS item (sign_in_tls,
below) says what to do, and gives the range's change as one click once the
listeners can start TLS.
| Exit | Meaning |
|---|---|
| 0 | Applied; or, with --if-marked, no file and nothing to do |
| 1 | Something could not be made or written; the lines say what was, and the error log has HM7510 (the certificate could not be made or written - nothing was changed) or HM7511 (the certificate's record could not be written - nothing was changed - or a listener or the range could not be; the rest stays as it was written). The mark is kept, so where nothing was changed a second run applies them |
| 2 | No database named in the configuration, it could not be opened or read well enough to judge, or its listeners and ranges could not be read again before the change (the mark and the Linux file are kept, and the next run asks again), or (Linux) run as root |
| 4 | Windows: the service is running |
| 5 | Not a database this installation created, not as the create script left it, or - at the unit's start, with --if-marked - marked more than a day from this machine's clock, either way: nothing was changed, and the line says why (the application log too, when it is on). In that last case the mark is kept for a run by hand |
Each change is on the audit trail under hmailserver --apply-new-install-defaults, as the certificate, port and IP range rows it
wrote, and in the application log as one line. When a listener cannot be given
the certificate, the Internet range is not set to require TLS: a listener
with no TLS behind that rule would be one nobody outside this computer could sign
in to at all. The line says which, and the checklist shows what is left.
The setup checklist (SetupChecklist.md) judges every server the same way, new or upgraded:
-
Passwords only over TLS (
sign_in_tls): for every IP range that lets a client connect from an address other than this computer's, whether it requires TLS for authentication (done), whether every listener it may use starts TLS before a password anyway (done), or neither. When every listener the range uses offers STARTTLS with a certificate that is there, the finding issign_in_plain, and it carries a fix: thePUT /api/v1/ipranges/{id}withrequire_tls_for_auth: true, which the Control Deck offers as a button, Require TLS for sign-in from this range. When one of them has no TLS to start, requiring it would refuse every client there on that listener, so the finding issign_in_no_tlsinstead: it names those listeners, says to give them a certificate and STARTTLS first, carries no fix, and the Deck's button opens the Ports view. The Control Panel shows the same item and opens its IP ranges page, where the setting is Require TLS before authentication, the same caption in the range's dialog; its advice says to give a listener without TLS a certificate and STARTTLS on the TCP/IP ports page first. -
TLS on the submission port says
submission_self_signed, action needed, while the port's certificate is the self-signed one - with the sentence that says to replace it and that ACME can. -
No password on port 25 (
delivery_port_auth) saysdelivery_port_auth_on, action needed and with no fix, while an SMTP listener on 25 takes AUTH - an upgraded server's does until it is changed, because changing it is what arms the ban. A new installation's port 25 is a honeypot, and the item is done (SmtpAuthOnDeliveryPorts.md).
Nothing the benches are built from has changed. The Windows benches are made by
build/upgrade-test-database.ps1 and bench-setup.ps1, and the Linux suite by
hmailserver --create-database: all of them run the create scripts, which still
make the four listeners without TLS and the two ranges without the requirement,
and none of them runs the installer or the unit. A bench whose database the
create path made carries the mark - the Linux suite's does, and a Windows bench
set up afresh by DBSetupQuick would - and that is harmless: nothing runs the verb
against a bench but its own fixture. Every fixture's PerformBasicSetup puts the
ports and the ranges back with TCPIPPorts.SetDefault and
SecurityRanges.SetDefault, which write the classic defaults as they always
have. And the suite's thousands of plain-text sign-ins are made from 127.0.0.1,
which the My computer range covers.
The fixture Security/NewInstallDefaults makes the bench into a database the
create step has just made - every port deleted and the four put back, the ranges
and certificates reset, its domain deleted, the mark written two days old, which
a run by hand acts on as the unit's start would not - runs the verb with the
service stopped, restarts the service, makes the domain again with an account in
it, and puts all of it back afterwards, step by step, restarting the service
whatever failed. A run killed half way leaves
C:\HMTest\Data\Certificates, which build/preflight-tests.ps1 names as that
fixture's leftover; -Clean puts the database back, removes it and restarts the
service.
The regression fixtures drive the verb without the installer. The proof that the
installer does it is an installation on a clean machine: the installer smoke
test, build/ci/installer-smoke.ps1, which build/ci/installer-smoke-run.sh runs
on a throwaway Windows Server VM, reverted to a clean snapshot before the run and
after it (ContinuousIntegration.md, "The installer
smoke test"). It makes the checks below, each named in its output as this list
names the property. A new installation and an upgrade are two runs, each from the
clean snapshot: the new installer on its own, and the previous release's
installer followed by the new one.
-
After a new installation with the built-in database (an installer from
6.3.4 on): the certificate record Self-signed (made at installation) over COM,
and no other; its two files under
Data\Certificates, and the key's ACL granting nothing toBUILTIN\Users- protected, and naming no one but SYSTEM, the Administrators, the administrator who installed and the account the service logs on as, which must be able to read it; the certificate RSA 2048, for 825 days, naming the host as its common name and its subjectAltName, with the organisation hMailServer, self-signed at installation; each of 25, 587, 110 and 143 with optional STARTTLS and that certificate, and no fifth port; Internet requiring TLS for authentication and My computer not; noNewInstallDefaultsPendingrow left (read from the database itself); the verb'sexited 0in Inno's log; each change on the audit trail underhmailserver --apply-new-install-defaults- the certificate, the four ports and the Internet range; and the checklist over COM answeringsign_in_tlsdone (sign_in_tls_requiredfor Internet) andsubmission_self_signedfor 587. Then, with an account made: from 127.0.0.1, STARTTLS offered and completing on all four (EHLO, IMAPCAPABILITY, POP3CAPAwithSTLS) with a peer certificate whose subject says self-signed at installation and which is the file on disk; from the machine's own LAN address, noAUTHin EHLO on 587,AUTH LOGINanswered 530, IMAPLOGINansweredBAD(itsCAPABILITYsayingLOGINDISABLED), POP3USERanswered-ERR(itsCAPAleavingUSERout), and each taking the account's password after STARTTLS; from that address, mail on port 25 taken without TLS and after STARTTLS, with noAUTHoffered before it; and the existing sign-ins over 127.0.0.1 passing without TLS as before - SMTPAUTHon 587, POP3 and IMAP. -
After an upgrade from a release that had no such defaults: the installer
not even running the verb (no line for it in Inno's log); every port's security
and both ranges exactly as they were - every property the COM objects have,
read before the upgrade and after it; no certificate record made, and no key
under
Data\Certificates; noNewInstallDefaultsPendingrow; the checklist over COM (Utilities.GetSetupChecklist(false)) answeringsign_in_tlsas action needed withsign_in_no_tlsfor Internet, and no fix; and the verb, run by hand with the service stopped, refusing the upgraded server with exit 5 because its database has no mark, and changing nothing. An upgrade from a release that had the defaults is held to the same, but for the checklist. -
After a new installation attached to an existing external database (DBSetup,
Select a new hMailServer default database): nothing changed, and exit 5 in
Inno's log. This one is not in the smoke test: DBSetup is a wizard with no
silent mode -
Tools/DBSetup/Program.csshows it whatever the command line says - so a silent installation cannot choose an external database, and the VM has no database server to attach. What stands for it is the rule it rests on, which the upgrade run proves: a database no create step marked is refused (exit 5) and left exactly as it was. That the installer then only logs the exit 5 on that branch isApplyNewInstallDefaults(false), read in the code, not run. -
After the uninstall:
Data\Certificateskept with the rest of the data directory.
The run that proves them, on 27 September 2026, against the released 6.3.4
installer as signed (hMailServer-6.3.4-x64.exe, SHA-256
843cb07b753f8d3d764b3120029e47ea407aa6874468cb3351a06777e3f2310f), on the
smoke VM:
| Run | Result |
|---|---|
| 6.3.4 on its own: a new installation, then the uninstall | 59 passed, 0 failed; 21 of the checks are new |
| 6.3.3, then 6.3.4 over it, then the uninstall | 73 passed, 0 failed; 8 of the checks are new |
Every check passed, the new ones and the old, and they found no defect in the
product. The first run of the new installation failed one old check, not one
of these: the uninstall left Bin\hMailServerApiKeys.ini, the REST API's key
store, which /GrantServiceAccess makes empty when a new installation's service
moves to NT SERVICE\hMailServer. The server writes that file, not the
installer, and it sits beside hMailServer.INI, which the uninstall keeps, so
the check's list of what the uninstall keeps was short of it; every earlier run
had kept LocalSystem and never made it.
The Linux packaging job of the GitHub workflow linux-build.yml (in the git
history at faca023d4) has these checks for the .deb - the file and the mark
removed, the record, the four listeners, the two ranges, the key's owner and
mode, STARTTLS on all four, and AUTH refused before STARTTLS from the runner's
own address and offered after it - but it ran only on GitHub Actions, and GitLab
CI's linux-package-deb job and the package install tests (deb-*-install,
rpm-*-install and the AppImage's) do not carry them yet, so nothing runs them
today. The Windows smoke test cannot: it installs the Windows installer, and no
package.
Common/Application/NewInstallDefaults.{h,cpp} (the verb, the certificate, the
mark and the test for a new database; CertificateDirectory() names the folder
whatever grants a service account its folders must also grant, since the key's
ACL inherits nothing); hMailServer.cpp and platform/main.cpp (the verb on each
platform, and --create-database's mark and file); COM/InterfaceDatabase.cpp
(the two create methods' mark); Common/Application/PropertySet.cpp (the mark
kept out of the backup); Common/Util/SetupChecklist.cpp (the item and the fix);
Common/Util/AcmeClient.cpp (ApplyCertificate_, which takes the self-signed
certificate's listeners over); installation/hMailServerInnoExtension.iss
(ApplyNewInstallDefaults); platform/packaging/hmailserver.service. The
regression fixtures are test/RegressionTests/Security/NewInstallDefaults.cs (the
verb, with the service stopped: refused while it runs, applied to the bench made
into a new database, the refusal of a password from a non-loopback address before
STARTTLS and its acceptance after, and exit 5 on a second run, the mark gone) and
test/RegressionTests/API/RestApiSetupChecklist.cs (the item, the fix, the
finding with no fix and the self-signed finding).
hMailServer 6.3.7, the latest release (17 October 2026, schema 6108) · AGPL-3.0-or-later · Repository · Mirror on GitHub · Report a documentation error (a GitLab issue naming the page)
Home — full index
Start here
1. Install and run
- Before You Install
- Installing hMailServer
- Installing on Linux
- Running in a Container
- Profiles and Environment
- The Control Panel
- The Control Deck
- Your First Domain and Mailbox
- Connecting a Mail Client
- The Webmail
- DNS for Your Domain
- Migration
2. Secure it
- Encryption and Certificates
- Database TLS
- Encryption for People
- Stopping Spam
- Stopping Viruses
- Security Hardening
- Secure Defaults
- The Service Account
- Secrets at Rest
- Sign-in and Identity
- Security Policy
3. Operate it
- Monitoring and Health
- The SNMP Agent
- Backup and Restore
- Message Storage
- Compliance and eDiscovery
- Troubleshooting
- Diagnosing Stalled Mail
- Relocating an Installation
- Upgrading hMailServer
- Upgrading Guide
- The Update Feed
- Migrating the Database Backend
- High Availability Runbook
- Warm Standby
- Runbooks Digest
4. Extend it
- Rules and Sieve
- Aliases Lists and Public Folders
- Rooms and Equipment
- Mailing Lists
- Routes and Relays
- Mail In to a Webhook
- Outbound Shaping
- The COM API and Scripting
- Event Scripts
- The REST API
- Command Line
- APIs Reference
5. Contribute to it
- Project Handbook
- Architecture
- Contributing
- Release Process
- Governance
- Assurance Case
- Regression Test Environment
- Fuzzing
- Regulatory Scope
- Third-Party Binaries
Look it up — from any journey
- Settings Reference
- Settings Overview
- Capabilities and Configuration
- Control Panel Reference
- Control Panel Pages
- Ports Reference
- Changes-Since-6210
- Version-History-to-6210
- Glossary
About this wiki
- hMailServer 6.3.7 is the latest release (17 October 2026, schema 6108)
- Pages say since 6.3.4 where a feature is new in it
- AGPL-3.0-or-later
- Repository, the one place the project is worked on
- Its mirror on GitHub, since 18 October 2026, with no Issues
- Questions: a GitLab issue with the Question template, GitHub Discussions Q&A, or the forum
- Report a documentation error: a GitLab issue naming the page