Skip to content
hMailServer 6.3.8 is the current release, signed 2 November 2026. Windows and Linux, and an in-place upgrade from 5.3 or later, but for the original hMailServer's 5.7.Download 6.3.8 Documentation FAQ

Linux

5 Topics 5 Posts

6.3 on Linux: .deb and .rpm for x86-64 and AArch64, with a package for each distribution release from 6.3.6 (6.3.4 and 6.3.5 were x86-64 only), the AppImages, the container image, PKGBUILD, systemd units, /etc/hmailserver, PostgreSQL or MariaDB. No Control Panel and no COM here.

  • hMailServer on Linux: what you get and what you do not

    Pinned linux control-deck api
    1
    0 Votes
    1 Posts
    134 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.8 (2 November 2026) is the current release, a security release: its Linux packages are, as 6.3.7's (17 October 2026) were, the generic .deb and .rpm for x86-64 and AArch64, a package for each of Debian 12 and 13, Ubuntu 24.04, Fedora 43 and 44, Enterprise Linux 9 and 10 and openSUSE Leap 16.0 and Tumbleweed, and both AppImages, and its container image is registry.gitlab.com/hmailserver/hmailserver:6.3.8; the AArch64 files are cross-compiled and tested under emulation, and the Ubuntu PPA and Fedora Copr, which have published 6.3.7 since 21 October 2026, are sent it by a job started by hand. From 6.3.6 or 6.3.7 the live update installs it through the root helper, for the generic packages and the AppImages; a server from one of 6.3.6's or 6.3.7's per-distribution packages upgrades with its package manager and its release's own package, and from 6.3.4 or 6.3.5 install it with apt, dnf or zypper. 6.3.8 names files in UTF-8 whatever LANG says, so on a server that ran under a LANG that is not UTF-8, rename each file and folder named outside ASCII to its UTF-8 name before upgrading. See the 6.3.8 announcement. Since 6.3.0 the same source tree builds, installs and delivers mail on Linux, as a systemd service, from a .deb, an .rpm or an Arch PKGBUILD: on x86-64 and AArch64 up to 6.3.3, and on x86-64 only in 6.3.4's packages. That is why the release is numbered 6.3 and not 6.2.29. Read §35.1 before you install anything. Finding out about a missing capability after the mailboxes are populated is the wrong order. What crossed The SMTP, POP3 and IMAP engines, delivery, routing, rules, anti-spam, anti-virus, ACME, the backup manager, the REST API, the Control Deck and the self-service portal. The on-disk message format is unchanged: one directory per domain and per mailbox, one file per message. The database schema is the same version the Windows build of the same release needs. The packages 6.3.4's .deb installs with apt on Ubuntu 26.04, where it is built, and on Ubuntu 24.04. It needs glibc 2.38 or later, so it does not install on Debian 12. Its .rpm installs with dnf on Fedora 43 and 44 and Enterprise Linux 10, not on Enterprise Linux 9, whose glibc and libstdc++ are older than it asks for, and nothing has been tried with openSUSE's zypper. There is no apt or dnf repository yet. Arch is a PKGBUILD in the tree, built with makepkg -si from the git tag, which it clones from https://gitlab.com/hmailserver/hmailserver. 6.3.4's packages are x86-64 only: the AArch64 packages were last published with 6.3.3, built natively on the CI the project lost with its GitHub account. Nothing else is packaged. The AppImage is not for running mail. It runs as whoever started it, with no hmailserver user, no unit and none of the systemd hardening, it puts its store under the invoking user's home directory, and it cannot bind port 25. Use it to see the thing work on a laptop. 6.3.4 has none; the last AppImage is 6.3.3's. §35.2. Where things live Configuration is /etc/hmailserver/hMailServer.ini at 0640 root:hmailserver. The store is /var/lib/hmailserver, the logs /var/log/hmailserver, the unit /usr/lib/systemd/system/hmailserver.service. The service runs as hmailserver:hmailserver and never as root. Full table: §35.6. The backends are PostgreSQL through libpq, a link-time dependency, and MySQL or MariaDB through a client loaded with dlopen at run time, which nothing pulls in for you. SQL Server and SQL Server Compact are refused by name, so an INI carried across from Windows is repointed or it does not start. The gaps, stated plainly No Control Panel and no COM API. Both are Windows-only and are not compiled here. Every third-party COM script stops at that boundary. Event scripts in JavaScript only, since 6.3.4. EventHandlers.js runs in an embedded QuickJS engine, for the same events under the same function names; the scripting language must be set to JavaScript (hmctl settings set scripting --set-string language=JavaScript), and VBScript and JScript are refused. Scripting is off until you turn it on. A script has no file, process or network access and cannot create COM objects, so a Windows script is ported, not copied (EventScripts.md). Domain properties were not writable when Linux arrived, and are now. At 6.3.0 per-domain DKIM, per-domain size limits and the domain signature were readable over REST and not writable. 6.3.3 made PUT /api/v1/domains/{domain} take the whole domain - its DKIM signing, its limits, its signature and its relay host among the rest - and gave the Control Deck full domain editing. At 6.3.4 the project's census finds 458 of the 466 properties the Windows Control Panel writes writable over REST and reachable from a Deck view, and the eight it lists as missing, an account's directory fields, are written on a route of their own, PUT /api/v1/accounts/{address}/directory, which the Deck's account editor uses (DeckParity.md). Self-update from 6.3.4, but not to it. The packages now install hmailserver-update, a root helper outside the server's unit, and the live update, when an administrator asks it to install, applies a .deb, an .rpm or an AppImage through it, rolling back when the server does not come back. The update check is off until you turn it on. 6.3.4 itself carries no Sigstore bundle, so no live update installs it: every server reaches 6.3.4 with apt or dnf, and the helper has nothing to apply before a release signed on the project's GitLab CI. Arch upgrades with pacman. No tested path from Windows. The schema and the store format are shared, so the pieces are there. Nobody has run a Windows installation onto Linux and verified the result. §35.14 says what is known. Administration is therefore the Control Deck at / on the REST listener, and the REST API behind it. §35.10 covers the daily jobs.
  • 0 Votes
    1 Posts
    176 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026), a security release, keeps the schema at 6108: an upgrade from 6.3.6 changes no table, from 6.3.5 or older the upgrade runs 6.3.6's three one-way steps to 6108, and from 6.3.3 or older 6.3.4's steps come first. On MySQL and MariaDB an insert that forgives a duplicate key now forgives that alone and any other failure is reported (HM5032), where it was taken as done with no row written, and a lost connection is reconnected and the statement run again, so the statement after a database restart, a failover or wait_timeout no longer fails; an error log that was quiet may now name failures that were hidden. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026), a security release, adds no schema step, so an upgrade from 6.3.6 or 6.3.7 changes no table. With TlsMode=verify-full on MySQL and MariaDB it checks the database server's certificate against the CA file TlsCaFile names, where in 6.3.4 to 6.3.7 a name holding a character outside ASCII reached the client as no CA file (work item 112); and through MariaDB Connector/C it refuses verify-full without TlsCaFile to a database server on this machine (127.0.0.1, ::1, localhost, an empty Server= or a Server= list that holds one) with HM7535, where Connector/C 3.4.1 and later checked neither that certificate's issuer nor its name (work item 113): before upgrading, name in TlsCaFile a CA that issues nothing but the database's certificates, the one that issued its certificate, or set TlsMode=require. The distribution's client should be MariaDB Connector/C 3.4.11 or later; where it is older and not patched, name the CA in TlsCaFile. On a server that ran under a LANG that is not UTF-8, rename a TlsCaFile named outside ASCII to its UTF-8 name before upgrading. The container image now carries MariaDB's client, so a container told MySQL reaches MariaDB. See the 6.3.8 announcement. A Linux server keeps its accounts, domains, settings and message metadata in PostgreSQL or in MySQL/MariaDB, and in nothing else. There is no built-in database on Linux, hmailserver --create-database refuses SQL Server and SQL Server Compact by name, and SQL Server is refused on Linux as HM6390, because ADO is Windows. This topic covers the choice between the two, the client library each needs, the [Database] section, each backend's set-up step by step, TLS to the database, and what the schema upgrade asks of MySQL and MariaDB. The one thing to know first: since 6.3.4 every table on MySQL and MariaDB is utf8mb4, which needs MySQL 5.7.9 or MariaDB 10.2.2, or later. On an older server the create script stops at its first table, and an upgrade from 6.3.3 stops at its first step, 6040 to 6041, with error 1071, having changed nothing. Check the version before anything else. The first install as a whole is First install on Linux. Choosing between the two PostgreSQL MySQL / MariaDB [Database] Type PostgreSQL MySQL (MariaDB is MySQL) Port 5432 3306 Proven on PostgreSQL 18 for the first Linux proof; PostgreSQL 17 in the TLS lab MySQL 5.7.44 and 8.4.11, MariaDB 10.6.28, 11.4.13 and 11.8.9, on 25 September 2026 Client library libpq, pulled in by the package's own dependencies libmariadb.so.3, opened at run time, installed by you Upgrade steps that ship every step from 5001 onwards every step, back to schema 0 Transactions DDL is transactional, so a failed schema step rolls back cleanly used only if every table reports InnoDB at connect time TLS with nothing set libpq's own prefer prefer since 6.3.4; never encrypted through MariaDB Connector/C before MigratingDatabaseBackend.md calls PostgreSQL the best-behaved backend in this codebase, and gives "you already run it" as the reason for MySQL or MariaDB. Both are proven live on Linux: schema created, account made, message delivered and read back. That the PostgreSQL steps begin at 5001 is not a gap, because PostgreSQL was not a backend before hMailServer 5. Moving between the two later is not an upgrade, and the chain does not do it: it is a new installation against the new backend and a migration. The client library to install The .deb and the .rpm declare their library dependencies automatically (dpkg-shlibdeps for the .deb, rpm's own auto-requires), so libpq is pulled in with the package. MySQL and MariaDB are different: the server opens libmariadb.so.3 at run time rather than linking it, so the wiki tells you to install the client library yourself. Distribution Package Debian, Ubuntu libmariadb3 Fedora, RHEL mariadb-connector-c Arch mariadb-libs Where there is no libmariadb, the server loads Oracle's libmysqlclient. Only Oracle's 8.x client is given TLS options: with any other, require and verify-full are refused with the version it reports, rather than claimed. Oracle's libmysqlclient.so.24, MySQL 8.4's client as Ubuntu 26.04 packages it, is not among the names the Linux server loads. On Windows the client is bundled, as libmysql.dll; it is the same MariaDB Connector/C: 3.4.9 in the x64 installers of 6.2.3 to 6.3.6, and 3.4.11 in 6.3.7's, built from MariaDB's source, with seven authentication plugins where 6.3.6's had nine. The Linux server loads the distribution's libmariadb.so.3, which the distribution patches. The [Database] section The file is root's, so edit it with sudoedit /etc/hmailserver/hMailServer.ini. Key What to write Type PostgreSQL or MySQL Server, Database, Username, Password Where the database is and who the server connects as. The Linux package's template reads a password typed with Passwordencryption=0 as plain text Port Written out, 5432 or 3306: it goes into the connection string as it stands, and there is no useful default TlsMode, TlsCaFile Since 6.3.4, optional (below) UpgradeOnStart Since 6.3.4: 1 lets the service bring its own schema forward as it starts. Off by default, and not needed on a package installation, whose post-install step runs the upgrade NumberOfConnections The pool that deliveries, sign-ins and the rest share; five as shipped These keys are read at the service's start, and are in the file rather than the settings store because they say how the store is reached. sudo -u hmailserver hmailserver --check-config prints the database type it made of what you wrote, without opening the database; Database type: 0 means the Type key did not take. PostgreSQL, step by step Create the role. With --createdb the server creates the database itself in step 3: sudo -u postgres createuser --pwprompt --createdb hmailserver If you would rather the service role could not create databases, which the wiki calls the tidier policy, leave the flag off and create the database yourself: sudo -u postgres createuser --pwprompt hmailserver sudo -u postgres createdb --owner hmailserver hmailserver Write [Database]: Type=PostgreSQL, the server, the database, the role, its password, and Port=5432. Create the schema, as the service user: sudo -u hmailserver hmailserver --create-database If TLS is to be required, that is the database server's to say: ssl=on and a certificate in postgresql.conf, and hostssl lines in pg_hba.conf. MySQL or MariaDB, step by step Check the version against the floor. The reason is key size: a key over a varchar(255), an account's address or a folder's name, is 1020 bytes at four bytes a character, more than an InnoDB key part may be in the older COMPACT rows (767 bytes). MySQL 5.7.9 and MariaDB 10.2.2 are the first releases whose InnoDB, as it ships, takes a key part of up to 3072 bytes and makes its tables DYNAMIC. On a MySQL 5.7 set up as 5.6 shipped, the error is 1071, "Specified key was too long; max key length is 767 bytes". Install the client library from the table above. Create the database, or let the verb create it, and give the account the server connects with every privilege on it: CREATE DATABASE hmailserver CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; GRANT ALL PRIVILEGES ON hmailserver.* TO 'hmailserver'@'%'; Every table names its own character set, so a database made with another default still works. But the upgrade steps create, alter and index tables, and the 6102 step also changes the database's own default character set, which needs the ALTER privilege on the database itself; ALL PRIVILEGES ON <database>.* has it. A database the server creates itself is made utf8mb4 already. Make sure every table ends up InnoDB: the server issues transactions only if they all report it at connect time, and silently does not if any does not. Write [Database]: Type=MySQL, the server, Port=3306, the database, the account and its password. Create the schema: sudo -u hmailserver hmailserver --create-database. Why utf8mb4. MySQL's utf8 is utf8mb3, three bytes a character, so nothing outside the Basic Multilingual Plane fits in it: no emoji. Before schema 6102 such a character in a contact, a folder or a signature was refused with error 1366 under strict mode, and made a question mark where strict mode is off. utf8mb4_general_ci is the twin of utf8_general_ci, the collation the tables had, so an address or a name compares as it did. What it does not do is tell characters outside the Basic Multilingual Plane apart: two address books of one account whose names differ only in an emoji are one name to its unique index. What --create-database does The package installs the server enabled and stopped, on purpose: there is no database yet, and the schema is made afterwards with this verb. If the database is not there, the verb connects to the server [Database] names with no database selected, creates it in the backend's own dialect and reconnects; if it is there and empty, it uses it. Either way it then runs the create script for the backend's type, which is what DBSetupQuick does over COM on Windows, and a database 6.3.4 creates is stamped 6104. Since 6.3.4, says the wiki's Installing on Linux, a database that already holds this build's schema, or an older one, is left as it is with exit 0, and a newer one exits 1; 6.3.3 refused any database that already held a schema. The documentation page (35.4, 35.7) still says a schema is refused, exit 1: test it on a scratch database first. Two things follow a create, both since 6.3.4. The database, an empty one you created first included, is marked for a new installation's secure defaults, which the systemd unit's first start applies if it comes within a day; a database that already held a schema is not marked. And run as the service user on a package installation, the verb cannot write the root-owned configuration, so it makes no stored-secrets key and says what to run instead, sudo hmailserver --create-stored-secrets-key. Export that key and keep the file with the database dumps, apart from them: a dump restored without it brings back every second factor and [Settings] secret unopenable. Both are covered in full in Hardening a public mail server. TLS to the database (since 6.3.4) Before 6.3.4 the connection to MySQL and MariaDB through MariaDB Connector/C, the client Ubuntu packages, was never encrypted, and a database account made with REQUIRE SSL refused the server. Since 6.3.4 two [Database] keys choose the behaviour on every backend (on PostgreSQL the older PostgreSQLSslMode and PostgreSQLSslRootCert win when they are set), and are read at the service's start: TlsMode, one of disable, prefer, require or verify-full (empty is prefer: TLS when the server offers it, the certificate unchecked), and TlsCaFile, a PEM file of CA certificates that verify-full checks against: [Database] TlsMode=verify-full TlsCaFile=/etc/hmailserver/db-ca.pem Only verify-full checks the certificate, and only it protects against someone on the path who can alter the traffic. On Linux, MySQL and MariaDB take the CA file or the system's store, /etc/ssl/certs, and which client library is loaded matters: of Oracle's clients only 8.x is given TLS options (above). On PostgreSQL, verify-full with no TlsCaFile is sslrootcert=system on Linux, with libpq 16 and later. What each mode does, the refusals (HM7530, HM7531), what each backend did before 6.3.4 and does now, and what is thinner than it could be are in Choosing a database, creating it, encrypting the link. The server cannot make the database server refuse plaintext: that is REQUIRE SSL on the MySQL account, or hostssl lines only in pg_hba.conf. Try it before the service is given it. --check-database makes one connection with the TLS settings in the configuration, prints the result, and exits 0 connected, 1 not, 2 not configured. It changes nothing, so it runs beside a running service: $ sudo -u hmailserver hmailserver --check-database Configuration: /etc/hmailserver/hMailServer.ini Database: MySQL/MariaDB on 192.0.2.20:3306, database hmailserver, as hmailserver TLS: TlsMode verify-full, the certificate checked against /etc/hmailserver/db-ca.pem Connected. MySQL/MariaDB: encrypted with TLS (TLS_AES_256_GCM_SHA384), the server's certificate checked (TlsMode verify-full) The application log says the same of the running service's connection, in a line that begins Database connection:. In a container (no image of 6.3.4 is published, so one you build from the .deb) the entrypoint writes [Database] from the environment at every start, so there HM_DB_TLS_MODE and HM_DB_TLS_CA set these, not a line in the file. Upgrading the schema, and the floor On a package upgrade the maintainer scripts run hmailserver --upgrade-database as the service user, only when a database is configured, then try-restart the service if it was running. A failure of the schema upgrade is reported and does not fail the package, so read what it printed. The verb is safe to run by hand, and says "nothing to do" when done: sudo -u hmailserver hmailserver --upgrade-database The chain is applied one step at a time from whatever version the database reports, which select value from hm_dbversion answers. A database from a newer build is refused, and a server refuses to start on a database that is too old or too new for it. Take the dump first, with pg_dump or mysqldump, and the data directory with it: the upgrade is one-way, and rolling back is restoring that dump and reinstalling the older package (Upgrading from 5.x, 6.2 or 6.3: rolling back). From 6.3.3 to 6.3.4 the schema moves from 6040 to 6104 in 64 steps. On PostgreSQL step 6102 only moves the version. On MySQL and MariaDB it converts every table to utf8mb4 under utf8mb4_general_ci, InnoDB and ROW_FORMAT=DYNAMIC: What Detail Time and disk It takes as long as copying the database. Each table is rebuilt in turn, and hm_messages, hm_message_metadata, hm_messageindexterms, hm_messagerecipients and hm_audit take the time: one to five seconds on an empty schema, minutes or more on a large installation. Have free disk space of the order of the largest table The floor, error 1071 Steps 6041, 6101 and 6102 each open with a statement that needs a long key; 6041 and 6101 make and drop a probe table, hm_upgradeprobe. On a server older than MySQL 5.7.9 or MariaDB 10.2.2 the upgrade stops at 6040 to 6041 with error 1071, and nothing has been changed. Upgrade the database server, then the schema The privilege, error 1044 The step changes the database's default character set, which needs ALTER on the database itself, not only on its tables A duplicate, error 1062 Two rows that the old collation told apart and the new one does not, under one unique index, stop the step; the error names the index and the value. The wiki's examples are straße and strase in one address book on MariaDB 11.8, and on MySQL 8.4 two names that differ only in an emoji. Rename or remove one and run the upgrade again: every statement of the step can run twice Afterwards A text column may read mediumtext, and a mediumtext one longtext. The server reads both alike Until 26 September 2026 steps 6041 and 6101 made their first tables before the index that failed, so an unreleased build from before that day could leave a database half done at 6040 or 6100. No upgrade path repairs that; Upgrading.md gives the DROP TABLE statements that do. Two nodes on one database are serialised step by step if they start together: on PostgreSQL by the version row, which each step locks for the length of its transaction, and on MySQL and MariaDB by a named lock, GET_LOCK('hmailserver_schema_upgrade'), held until the step is over and waited on for up to four hours. A server that refuses GET_LOCK altogether, MariaDB Galera with wsrep on, still upgrades with the row lock alone, and the output says so for each step: there, upgrade from one node at a time. Where to read more Installing on Linux and Before You Install, section 3.5, on the wiki. DatabaseTls.md, with the Linux lab run of 25 September 2026 line by line; Upgrading.md for the 6102 step and the repair of a half-done one; MigratingDatabaseBackend.md for why utf8mb4. The documentation page: 3.5 A database, 35.4 Creating the database and 35.13 Upgrading and rollback. On this forum: First install on Linux, Upgrading to 6.3.4 and Moving to new hardware; choosing a database and encrypting the link for TLS on every backend, and the hmailserver command line on Linux for the other verbs.
  • 0 Votes
    1 Posts
    145 Views
    ProgressiverobotP
    Written at 6.3.4, when no image was published. hMailServer 6.3.6 (6 October 2026) publishes the first: registry.gitlab.com/hmailserver/hmailserver:6.3.6, also tagged 6.3 and latest, one index over linux/amd64 and linux/arm64, signed with cosign as the release's pipeline; the Helm chart is a file of the release, and the packaging Compose file now runs PostgreSQL 18. See the 6.3.6 announcement and ContainerImage.md at the v6.3.6 tag. hMailServer 6.3.7 (17 October 2026), a security release, is registry.gitlab.com/hmailserver/hmailserver:6.3.7, for linux/amd64 and linux/arm64; in the container the update check names the image to pull and installs nothing, and the schema stays at 6108, so a container recreated on it from 6.3.6 changes no table. Its IMAP and POP3 listeners take the client's address from a PROXY protocol header, as the SMTP listener does, each from the peers on a list of its own, both off by default. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026), a security release, is registry.gitlab.com/hmailserver/hmailserver:6.3.8, and 6.3 and latest now name it; the schema stays at 6108. Its image carries MariaDB Connector/C, so HM_DB_TYPE=MySQL reaches a MariaDB server, where the images of 6.3.7 and earlier stopped at the first start, and its entrypoint secures a new installation at its first start by itself, whatever the container's command, so the run by hand below is left for a database its --create-database made more than a day before. The Helm charts of 6.3.6 and 6.3.7 never started a pod; 6.3.8's runs it as 999:999, fills new volumes in an init container and leaves database.port to the type's own port. Move a release of the older chart with helm repo update, helm upgrade <release> hmailserver/hmailserver --reset-then-reuse-values (Helm 3.14 or later) and then kubectl delete pod <release>-hmailserver-0, since the StatefulSet never replaces the stuck pod by itself (--reuse-values would keep the old chart's fsGroup 1000 and database.port 5432); where a cluster's policy refuses every sysctl, set unprivilegedPortsSysctl: false. For a Compose deployment made from an earlier Compose file, keep its HM_REST_CERTIFICATE_FILE, HM_REST_PRIVATE_KEY_FILE and ./certs mount in the new file, the key readable by 999, or its REST API does not start. See the 6.3.8 announcement. The Linux server is also a container image: the x86-64 .deb installed on Ubuntu 24.04 and run as the hmailserver user, with the database connection, the REST listener, the administrator password and, since 6.3.4, every other setting taken from the environment at start. This topic describes the image, the Compose file and the Helm chart as they are in the tree at the v6.3.4 tag, and the HM_* variables the entrypoint reads. The one thing to know first, from the 6.3.4 changelog: no container image of 6.3.4 is published. The images published on ghcr.io/progressiverobot/hmailserver before GitHub's suspension answer 404, and registry.gitlab.com/progressiverobot/hmailserver, which the Compose file and the chart now name, holds nothing. Meanwhile, build the image from the release's .deb and use your own tag wherever the files say registry.gitlab.com/.... What a Linux server is and is not is in hMailServer on Linux. Building the image yourself Use the docker directory of the same tag as the package: 6.3.4's entrypoint runs verbs a 6.3.3 binary does not have. git clone https://gitlab.com/hmailserver/hmailserver.git cd hmailserver git checkout v6.3.4 cd hmailserver/source/Server/platform/packaging/docker mkdir packages curl -L -o packages/hmailserver_6.3.4_amd64.deb \ https://gitlab.com/hmailserver/hmailserver/-/releases/v6.3.4/downloads/hmailserver_amd64.deb docker build -t hmailserver:6.3.4 . The Dockerfile copies packages/hmailserver_*_amd64.deb (the HM_DEB build argument names another path) and pins its Ubuntu base by digest. 6.3.4's package carries no Sigstore bundle to check it against, as 6.3.3's did. The image is x86-64 only: the Dockerfile takes the amd64 package. What is in the image, and what is not In: /usr/bin/hmailserver, the packaged hMailServer.ini at /etc/hmailserver/, the database scripts, the Control Deck, the fail2ban filter and jail the package carries, ca-certificates for the TLS the server does as a client, and an entrypoint. About 40 MB. The process runs as hmailserver; ports below 1024 are bound through cap_net_bind_service on the binary, not through root. Since 6.3.4 the Dockerfile installs the package's two Recommends, 7zip and python3: without them a backup failed with "Could not add the backup settings to the archive", and hmctl had no Python 3. Not in: the database, and it never will be. Point the image at a PostgreSQL or MariaDB server, in another container or anywhere else; on first start the entrypoint creates the schema there, and on every later start upgrades it if it is behind. Also not in: curl, an editor, or a shell of any consequence beyond bash. There is no Control Panel and no COM; the REST API is the administration. The variables the entrypoint reads itself Sixteen of these are older than 6.3.4 and keep their names and meanings. Variable Writes Notes HM_DB_TYPE [Database] Type PostgreSQL or MySQL (MariaDB is MySQL). Setting this is what makes the section be written at all HM_DB_HOST, HM_DB_PORT Server, Port Defaults db, and the type's port (5432 or 3306) HM_DB_NAME, HM_DB_USER, HM_DB_PASSWORD Database, Username, Password Defaults hmailserver, hmailserver, empty. The user needs the right to create the database on first start HM_DB_TLS_MODE, HM_DB_TLS_CA TlsMode, TlsCaFile Since 6.3.4. disable, prefer (empty, the default), require or verify-full, and the path of a PEM file of CA certificates mounted into the container HM_ADMIN_PASSWORD [Security] AdministratorPassword Through the server's own --set-admin-password: the file holds a PBKDF2 hash, never the password. Without one the REST API refuses to start HM_REST_PORT RestApiPort The packaged file has 0, which is off; 8045 is the conventional port HM_REST_BIND_ADDRESS RestApiBindAddress The packaged file has 127.0.0.1, which inside a container is the container's own loopback HM_REST_CERTIFICATE_FILE, HM_REST_PRIVATE_KEY_FILE RestApiCertificateFile, RestApiPrivateKeyFile Paths inside the container, readable by the hmailserver user. Paths the server reads, not _FILE forms HM_DB_PASSWORD_FILE, HM_ADMIN_PASSWORD_FILE the same two secrets Read from a file: a Docker or Compose secret. Setting both a variable and its _FILE is refused HM_CREATE_DATABASE, HM_UPGRADE_DATABASE nothing 0 skips --create-database or --upgrade-database at start. Both are no-ops when there is nothing to do HM_INI which file is written Default /etc/hmailserver/hMailServer.ini HM_PROFILE, HM_PROFILE_CONFIRM nothing Since 6.3.4: the setting profile applied once, and its confirmation On 6.3.3 and earlier the entrypoint writes these into hMailServer.ini on every start, and every other setting is the packaged file in the /etc/hmailserver volume. Since 6.3.4 the database is the settings store, so the four HM_REST_* variables are aliases of HM_SETTING_RestApiPort and its three neighbours, stored like any other setting; [Database] is still written into the file at every start, because it says where the database is. The image's /etc/hmailserver is owned by the hmailserver user, so there, unlike on a package installation, the server keeps its copy of each stored value in the file, and a --create-database that creates the schema also makes the stored-secrets key. Every setting from the environment (since 6.3.4) After --create-database and --upgrade-database, and before the server, the entrypoint runs the server's own verb, hmailserver --apply-environment, whenever there is anything for it to do. HM_SETTING_<key> sets a [Settings] key and HM_PROPERTY_<name> a database setting (HM_SETTING_MaxSubmissionsPerIPPerMinute=30, HM_PROPERTY_maxmessagesize=20480), and either with _FILE reads the value from a file, a Compose secret or a Kubernetes Secret mounted as one. The environment wins, at every start. An administrator cannot change a setting the environment names until the variable is unset and the container restarted, and a ConfigMap change, too, is applied by a restart and not live. Everything is checked before anything is stored. When anything is refused, nothing is stored and the container does not start; docker compose logs or kubectl logs says which variable and why. The naming rule, every refusal, what the REST API and the Control Deck show for a setting the environment manages, and how a value from a file stays secret are in configuration from the environment. HM_PROFILE names one of the four setting profiles, office, gateway, relay or sender, which the entrypoint applies once, with --apply-profile NAME --once, after the environment's settings and before the server starts. HM_PROFILE=none records that none is wanted. A profile that closes a listener, trusts an address range, turns a filter or a check off or weakens it, or shortens a window of days needs HM_PROFILE_CONFIRM=yes, and without it the container does not start. A profile skips a setting the environment manages, naming its variable. Not yet run in a container. The verb has been run as hMailServer.exe --apply-environment on Windows, and its POSIX half compiles; the entrypoint as a whole, the POSIX verb against a database, the chart and the Compose file have been read and not executed. HM_PROFILE is in the same state. Try it on a scratch database first. Reaching the API The server requires TLS on a REST listener that is not bound to loopback. A listener left at the packaged bind address answers only processes inside the container, which is none, since the image carries no client. Two ways out; either will do: Bind it and give it a certificate. HM_REST_BIND_ADDRESS=0.0.0.0, with HM_REST_CERTIFICATE_FILE and HM_REST_PRIVATE_KEY_FILE pointing at a certificate and key mounted into the container, and the port published. Publish it on the host's loopback (127.0.0.1:8045:8045) unless the certificate is one clients will trust. Host networking (--network host). The container's 127.0.0.1 is the host's, the packaged bind address is right as it is, no certificate is needed, and the API is at http://127.0.0.1:8045 from the host and through an SSH tunnel from elsewhere. The mail ports are then the host's ports directly. First start, with Compose The file to use is in the repository, at hmailserver/source/Server/platform/packaging/docker/docker-compose.yml. It reads the two passwords as Compose secrets from files, mounts the REST certificate and key in, and carries the HM_SETTING_ and HM_PROPERTY_ examples commented out. A workflow resolved it with docker compose config on every change until 18 September 2026, so it is known to parse. Below is the mail service of the wiki's shorter example, which puts it under services: beside a postgres:16 service named db and declares the named volumes. It names the image built above, not the empty registry: mail: image: hmailserver:6.3.4 depends_on: - db environment: HM_DB_TYPE: PostgreSQL HM_DB_HOST: db HM_DB_NAME: hmailserver HM_DB_USER: hmailserver HM_DB_PASSWORD: change-me HM_ADMIN_PASSWORD: change-me-too HM_REST_PORT: 8045 HM_REST_BIND_ADDRESS: 0.0.0.0 HM_REST_CERTIFICATE_FILE: /certs/rest.pem HM_REST_PRIVATE_KEY_FILE: /certs/rest.key volumes: - ./certs:/certs:ro - hm-data:/var/lib/hmailserver - hm-logs:/var/log/hmailserver - hm-etc:/etc/hmailserver ports: - "25:25" - "587:587" - "993:993" - "127.0.0.1:8045:8045" restart: unless-stopped restart: unless-stopped matters on the first start: depends_on starts the database container first but does not wait for it to accept connections, so the entrypoint's --create-database fails and the container exits if the database is not there yet; the restart tries again a moment later. The first start logs the [Database] section written, the administrator password hashed and the database created at schema 6104, and also what --create-database did about the stored-secrets key and one line per HM_SETTING_ or HM_PROPERTY_ variable stored. The first domain is then one request away (First install on Linux). Passwords in a Compose file are passwords in a file: for anything beyond a first look, use HM_DB_PASSWORD_FILE and HM_ADMIN_PASSWORD_FILE with Compose secrets. The secure defaults of a new installation are not applied in a container by themselves. On a package installation the systemd unit's first start gives a database --create-database made a self-signed certificate, STARTTLS and TLS required for authentication on the Internet IP range. The image has no unit, so that step is run by hand, as the service's user, with the server stopped. SecureDefaults.md gives the verb and no container command for it, and lists its refusal of a running service (exit 4) for Windows only: hmailserver --apply-new-install-defaults A run by hand has no one-day deadline, so an image first booted days after its --create-database is secured this way. What it changes, its exit codes and the checklist item that says what is left are in Hardening a public mail server. Kubernetes, with the chart Since 6.3.4 the chart is at hmailserver/source/Server/platform/packaging/helm/hmailserver. It is not published to a chart repository; install it from a checkout. It renders a StatefulSet of one, because a message store is not something two pods may share: the three volumes the image declares become claim templates, and the database is pointed at, never bundled. The chart refuses to render without database.host. Its image.repository names the empty registry, so set it to the image you built, pushed to a registry the cluster's nodes can pull from: helm install mail ./hmailserver \ --set image.repository=<your registry>/hmailserver \ --set database.host=postgres.databases.svc.cluster.local \ --set database.password=... \ --set administrator.password=... \ --set rest.tlsSecretName=mail-tls Value What it is image.repository registry.gitlab.com/progressiverobot/hmailserver as shipped, which holds no image: set it to where you pushed the image you built database.host, .type, .name, .user The PostgreSQL or MariaDB the server uses; required. database.create and database.upgrade make the schema on first start and move it forward on an upgrade database.password, administrator.password Go into a Secret the chart makes. Point existingSecret at a Secret you made and the chart writes none; its two keys are named by existingSecretKeys rest.enabled, .port, .tlsSecretName The one listener that serves the REST API, the Control Deck and the webmail. Off loopback the server requires TLS, so without a kubernetes.io/tls Secret named here the listener will not start; the chart's notes say so on install settings, properties, settingsFrom Since 6.3.4: any [Settings] key (rendered as HM_SETTING_<key>), any database setting (HM_PROPERTY_<name>), and a key of a Secret you made, projected into /run/secrets/hmailserver-settings/<n> and named through _FILE, so a secret value is in no environment variable, rendered manifest or log line service.type, .ports, .externalTrafficPolicy A LoadBalancer with the mail ports, and the REST port when the listener is on. Local keeps the client's address, which auto-ban, greylisting and the Received header all need persistence.* The message store, the logs and the configuration, one claim each Probes Port 25, as the image's own health check is, or the metrics listener's /livez and /readyz when that listener is on settings, properties and settingsFrom look like this in a values file; a settingsFrom entry says setting: in place of property: for a [Settings] key: settings: MaxSubmissionsPerIPPerMinute: "30" properties: maxmessagesize: "20480" settingsFrom: - secretName: mail-relay key: password property: smtprelayerpassword A switch is 1 or 0 for a [Settings] key; true, false, yes, no, on and off are taken as well for a setting whose REST settings row is a switch, and only there: maxmessagesize=off is refused rather than stored as 0, which is "no limit". Volumes, ports and the health check Path What lives there /var/lib/hmailserver The mail store, and .hmailserver-secret-key, the key under which this machine's stored secrets are protected. The key travels with this directory: a database restored beside a fresh data volume cannot open the secrets it holds /var/log/hmailserver The logs. Bind-mount it to a host path if fail2ban on the host is to read it /etc/hmailserver hMailServer.ini; since 6.3.4 the only home of PasswordPepper and [Security] StoredSecretsKey, which no database and no backup holds The image exposes 25, 110, 143, 465, 587, 993, 995, 4190 (ManageSieve) and 8045; publish the ones you serve. The health check opens a connection to the container's own port 25 every 30 seconds after a 60-second start period, and three failures in a row mark the container unhealthy. Upgrades, backups and auto-ban Upgrading is building the image of the new tag, as no 6.3.4 image is published to pull, and recreating the container: the entrypoint runs --upgrade-database on every start, so a schema that is behind is brought forward before the server opens a port. On MySQL and MariaDB the 6102 step rebuilds every table as utf8mb4, which takes time on a large database and needs the database server version that Databases on Linux gives. An upgraded database gets no stored-secrets key until one is made with hmailserver --create-stored-secrets-key, and never the new installation's defaults. Take the backup first (Upgrading to 6.3.4). Backups are three things together: the database, the /var/lib/hmailserver volume, and /etc/hmailserver. Two things are only in /etc/hmailserver/hMailServer.ini: a PasswordPepper, if one is set, without which a restored database refuses the password of every account hashed with Argon2id or scrypt, and the stored-secrets key, without which a restored database opens no account's second factor and no [Settings] secret. Export the key once, and keep the file with the database backups, apart from them: docker exec <container> hmailserver --export-stored-secrets-key /var/lib/hmailserver/stored-secrets.key docker cp <container>:/var/lib/hmailserver/stored-secrets.key ./stored-secrets.key A replacement container takes it back with --import-stored-secrets-key FILE. Auto-ban works as everywhere: an address that fails to log on repeatedly is refused by the listeners. The hook that adds the address to the host's firewall cannot run from inside a container, so run fail2ban on the host instead: bind-mount /var/log/hmailserver, copy the filter and jail out of the image, point the jail's logpath at the mounted directory, and enable it. What was proven, and what has not been Until 18 September 2026 the image job of the GitHub workflow built the image on every release tag, pushed it, and smoke-tested the pushed image as the example above runs it: the container healthy, the SMTP banner and the API answering, and the administrator password in no file in clear. Nothing has run it since. GitLab CI's linux-image job is written to build the image from the .deb with kaniko and inspect it; it neither pushes nor runs the image, so the smoke test has no replacement yet. The chart-and-compose job is written to lint and render the chart and resolve the Compose file. Neither had run when ContinuousIntegration.md was written, on 25 September 2026. The changelog's 6.3.4 section records the first full pipeline the day after: it finished failed, the image job among those that passed, and no pipeline has run on the fixed tree since. 6.3.4 was made by hand. Where to read more Running in a Container and Profiles and Environment on the wiki. EnvironmentConfiguration.md for the naming rule and every refusal, Profiles.md for every entry of the four profiles, SecureDefaults.md for the verb run by hand, and DatabaseTls.md for HM_DB_TLS_MODE. The 6.3.4 section of CHANGELOG.md for the state of the image. On this forum: the 6.3.4 announcement, hMailServer on Linux, First install on Linux and Upgrading to 6.3.4; configuration from the environment for HM_SETTING_ and HM_PROPERTY_ in full, and the hmailserver command line on Linux for the verbs the entrypoint runs.
  • 0 Votes
    1 Posts
    145 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.6 (6 October 2026) carries .deb and .rpm packages for x86-64 and AArch64, one for each of Debian 12 and 13, Ubuntu 24.04, Fedora 43 and 44, Enterprise Linux 9 and 10, and openSUSE Leap 16.0 and Tumbleweed, both AppImages and the first container image. Its commands say what they did on their own output rather than in the error log, and the store commands run as root refuse a data directory that another account could have linked into. See the 6.3.6 announcement. From 6.3.6 the root helper applies hMailServer 6.3.7 (17 October 2026), a security release, by the live update, for the generic .deb and .rpm of either architecture and both AppImages; a server from one of 6.3.6's per-distribution packages is offered the generic package, which apt refuses on Debian 12 and dnf on Enterprise Linux 9, and is upgraded with its package manager and its release's own package, and from 6.3.4 or 6.3.5 install 6.3.7 with apt, dnf or zypper, not by the live update. A server whose UpdateSigningIdentity, UpdateSigningIssuer and UpdateSourceRepository still name the path the project began at refuses 6.3.7 and 6.3.8 until sudo hmailserver --set-setting <name>= clears each of the three and the service is restarted. See the 6.3.7 announcement. From 6.3.6 or 6.3.7 the root helper applies hMailServer 6.3.8 (2 November 2026), a security release, by the live update in the same way; a server from one of 6.3.6's or 6.3.7's per-distribution packages is upgraded with its package manager and its release's own package, and from 6.3.4 or 6.3.5 install 6.3.8 with apt, dnf or zypper rather than by the live update, or turn the live update's apply off first with systemctl disable --now hmailserver-update.path. 6.3.8 names files in UTF-8 whatever LANG says: on a server that ran under a LANG that is not UTF-8, rename each file and folder named outside ASCII to its UTF-8 name before upgrading, for example with convmv -f iso-8859-1 -t utf-8 -r --notest <folder>. The values --set-setting, --apply-profile, --account and --domain take, and --config, are read as UTF-8 and refused with exit 2 when they are not. See the 6.3.8 announcement. On Linux the server is one executable, /usr/bin/hmailserver, and what cannot be done over the REST API, from creating the schema to rewriting the message store, is done with its own options. This topic lists every verb 6.3.4's documents name and who runs it, where the package puts its files, what the systemd unit lets the service touch, and how the root helper applies a live update. What a Linux server has and lacks is in hMailServer on Linux, the first install in First install on Linux. The one thing to know first: on a package installation /etc/hmailserver/hMailServer.ini is 0640 root:hmailserver, and the service cannot write it. Where the package puts things Path What it is /usr/bin/hmailserver the server, one file /usr/bin/tlds.txt, /usr/bin/dh2048.pem read from beside the binary; the second is required by every TLS context /usr/share/hmailserver/DBScripts/*.sql the schema creation and upgrade scripts /usr/lib/systemd/system/hmailserver.service the unit /etc/hmailserver/hMailServer.ini the configuration, a conffile an upgrade never overwrites; since 6.3.4 its [Settings] section is a copy of the database /usr/bin/hmctl since 6.3.4, the command-line client over the REST API /etc/logrotate.d/hmailserver the log rotation rule /etc/fail2ban/filter.d/hmailserver.conf, /etc/fail2ban/jail.d/hmailserver.conf the fail2ban filter and jail, the jail disabled as shipped /usr/lib/hmailserver/hmailserver-update, /usr/lib/hmailserver/autoban-hook the root update helper (since 6.3.4) and the firewall hook /var/lib/hmailserver, with temp, events and database under it the mail store, one directory per domain and one per mailbox; scratch space; event scripts /var/log/hmailserver the logs What the maintainer scripts do. They create the hmailserver system user and group, and the last two rows above, all 0750 hmailserver:hmailserver; install the configuration; and enable hmailserver.service without starting it, on purpose, because there is no database yet. On an upgrade they run hmailserver --upgrade-database as the service user, only if a database is configured, then try-restart the service if it was running. On removal they stop and disable, and leave the mail store and the logs alone; a .deb purge removes the configuration as well, and /etc/hmailserver with it when nothing else is in it, and still leaves the store. Which packages exist. 6.3.4 carries two x86-64 packages, hmailserver_6.3.4_amd64.deb and hmailserver-6.3.4-1.x86_64.rpm, and no container image of 6.3.4 is published. The .deb installs on Ubuntu 26.04 and 24.04, not on Debian 12; the .rpm on Fedora 43 and 44 and Enterprise Linux 10, not on Enterprise Linux 9; the rest of the platform picture is in hMailServer on Linux. The packages carry no Sigstore bundle, so no live update installs them: 6.3.4 is installed by hand (Upgrading to 6.3.4). Who runs what As root (sudo), because the file is root's: --set-admin-password, --set-setting, and the four stored-secrets verbs. All but the export write /etc/hmailserver/hMailServer.ini, and the export a new file of its own (below); --set-setting keeps the file's owner and mode. As the service user (sudo -u hmailserver), because the verb opens the database or writes the data directory: the rest, from --create-database to the store walks. --apply-new-install-defaults refuses to run as root (exit 2), whose certificate key the service could not read, and --sample-domain run as root leaves today's log owned by root. Two things follow from the file being root's. The running server cannot write its copy of a stored value back into [Settings]: a value stored over the API is in effect and the file's line goes stale, which the server says once a start in the application log, not as an error. The stale line is never taken for an edit, so nothing needs changing by hand; only what reads the file itself, such as a server that cannot read its table, sees the older value (how the settings store works). The exception is a [Settings] secret sealed once the stored-secrets key exists (below): its line cannot be rewritten, and HM5803 names it at each start until sudo hmailserver --set-setting <name> writes it. And a server that verifies an older MD5 or SHA-256 administrator password hash cannot write it again as PBKDF2: it says so once a process (HM7494), and sudo hmailserver --set-admin-password does it. Every verb Every row from --check-database to the stored-secrets verbs is new since 6.3.4. Verb Runs as What it does --config <file> The configuration to read. Default: hMailServer.ini beside the executable, then /etc/hmailserver/hMailServer.ini --foreground the unit Run in the foreground and log to standard error as well as the log directory. It is the default --check-config service user Print the configuration path, the directories and the database type, and exit without opening a listener or the database. Database type: 0 means the Type key did not take --set-admin-password root Read a new administrator password from standard input with echo off, hash it with PBKDF2, and write it to the file --create-database service user Create the database [Database] names, or use an existing empty one, and run the create script for its type. Refuses SQL Server and SQL Server Compact by name. Since 6.3.4, says the wiki's Installing on Linux, a database that holds this build's schema or an older one is left as it is with exit 0, and a newer one exits 1; 6.3.3 refused any database that held a schema. The documentation page (35.4, 35.7) still says a schema is refused, exit 1: test it on a scratch database first --upgrade-database service user Run every upgrade script from the database's version to this build's, in order. Refuses a database from a newer build; stops rather than loops if a script runs and the version does not move. Safe to run by hand --check-database service user Connect once with TlsMode and TlsCaFile, say whether the connection is encrypted and the certificate checked: exit 0 connected, 1 not, 2 not configured. Changes nothing, and runs beside a running service --set-setting NAME=VALUE, --set-setting NAME root Store one [Settings] value in the database and write the server's copy into the file (below) --apply-environment service user Store every setting the environment names (HM_SETTING_<key>, HM_PROPERTY_<name>, their _FILE forms, the container's HM_REST_*), each checked first: exit 1, with nothing stored, if one is refused. It writes the file's copy too, so the file must be writable, as the container's is; where it is not, as for the service user on a package installation, the verb refuses before it stores anything. EnvironmentConfiguration.md says it has been run on Windows only, and on Linux compiled but not run: try it on a scratch database first --apply-profile NAME [--confirm] [--once] [--if-new], --revert-profile service user, server stopped Apply one of the four setting profiles (office, gateway, relay, sender; none records that none is wanted), or put back what the last one changed. Exit 3 means an entry needs --confirm, 5 that --if-new found ten accounts or more. On Windows the verb refuses while the service runs (exit 4); Profiles.md says that on Linux nothing tells it whether a server runs, and that the POSIX verbs have been written and read, and not run: try them on a scratch database first --apply-new-install-defaults [--if-marked [<file>]] service user, server stopped The secure defaults of a new installation (below). Exit 0 applied, 1 something could not be written (HM7510, HM7511), 2 no database or run as root, 5 not a database this installation created, or not as the create script left it --sample-domain create, --sample-domain remove service user Make or remove the throwaway example.test. Exit 0 done, 2 refused, 1 failed. Remove it before the server takes real mail --measure-store service user What compressing the store and holding its large parts once would save. Changes nothing, and runs beside a running server --compress-store [--decompress], --recount-store service user, server stopped Rewrite the store in place, or back to plain RFC 822; recount the parts held once. Refused while the server runs (exit 4): on Linux the server holds a lock in the log directory for as long as it runs --encrypt-store, --decrypt-store service user, server stopped Encrypt what a mailbox encrypted at rest stored before it was switched on, or put it back. --decrypt-store reads the domain's recovery passphrase from HMAILSERVER_RECOVERY_PASSPHRASE, never from the command line --copy-store-to-object-storage, --copy-store-from-object-storage service user Copy the message store into the object store the settings name, or back, each file verified and a stopped run resumed. They run beside the server; --remove-source removes what was copied from once its copy is verified, and only with the server stopped (exit 4 beside it) --verify-update <file> Check a downloaded package or AppImage against the Sigstore bundle beside it; exit 0 when a signer the server trusts made it --create-stored-secrets-key, --export-stored-secrets-key <file>, --import-stored-secrets-key <file>, --forget-sealed-secrets <key id> root Make the stored-secrets key, export it, import it on another node, or, with the server stopped (exit 4 beside it), clear what was sealed under a key lost for good (below) --version, --help --measure-store, --compress-store, --encrypt-store, --decrypt-store and the two copies take --account <address> for one mailbox or --domain <name> for one domain's, and neither for the whole store; --decrypt-store needs one of the two. Signals. SIGTERM and SIGINT stop the server. SIGHUP, which systemctl reload hmailserver sends, re-reads the configuration; it stops and restarts the listeners inside the running process, so do it when nothing is mid-delivery. Since 6.3.4 the server drains before it stops: the mail listeners close at the socket, so senders retry, and ShutdownDrainSeconds lets what is in flight finish. At its default of 0 nothing is waited for, and a session mid-DATA at the moment of the stop is cut: set it on a production server, where Upgrading.md calls 30 a reasonable figure. --set-setting: the way in before the API exists (since 6.3.4) From schema 6042 the database is the settings store. Every [Settings] key the file holds at the server's first start is taken into the database then, the packaged RestApiPort=0 among them. Afterwards a line edited in the file is reported in the error log as HM5804 and the stored value is used, so an edit to the file brings no listener up, and with the listener off nothing that speaks the API can change it either. sudo hmailserver --set-setting RestApiPort=8045 sudo hmailserver --set-setting RestApiBindAddress=127.0.0.1 sudo systemctl restart hmailserver The verb stores the value as the REST API does and prints what it stored. It refuses a name the server's settings loader does not read, a whole number that is not one, a port that is not 0 to 65535, and a RestApiBindAddress that is not an IP address or localhost: the checks --apply-environment makes, in the same words. A secret's value is refused on the command line, where the shell's history, the process list and sudo's log would keep it. Give the name alone, sudo hmailserver --set-setting SRSSecret, and type the value at a prompt that does not echo it, or pipe it in. Which sections and keys stay in the file rather than the database, and the file's [SettingsOverride] section, which forces a value over the stored one (HM5807, HM5808), are in Settings: what is not stock, when a save takes effect. The stored-secrets key on Linux (since 6.3.4) A Linux server has two secret stores. The machine's is the key file <DataFolder>/.hmailserver-secret-key, mode 0600, made by the server on first use: the database password in the configuration and the route and fetch-account passwords are kept under it as LINUX1: envelopes. It travels with the data directory. A secret moved to a machine without it cannot be opened (HM6414), and there is no re-keying command: replacing the file means re-entering every stored password. The installation's is the stored-secrets key, [Security] StoredSecretsKey, under which each account's TOTP second factor and the [Settings] secrets are sealed in the database as HMK1: envelopes, so that every node sharing the database can open them. The service never makes it. On a package installation --create-database runs as the service user, cannot write the root-owned file, makes no key and says what to run instead. An upgrade makes none. Until a key exists those secrets are kept as written, and the setup checklist's stored_secrets_key item offers the step. Make the key, export it and restart the service; nothing is sealed until that restart: sudo hmailserver --create-stored-secrets-key sudo hmailserver --export-stored-secrets-key /root/hmailserver-stored-secrets.key sudo systemctl restart hmailserver Keep the exported file with the database backups, apart from them: a database dump restored without it brings back every second factor and [Settings] secret unopenable. Carrying the key to a standby with --import-stored-secrets-key, what a node without it says (HM7495) and --forget-sealed-secrets for a key lost for good are in Hardening a public mail server. The unit, and what it lets the service touch The packaged unit runs the server with --foreground as hmailserver, and before every start: ExecStartPre=-/usr/bin/hmailserver --apply-new-install-defaults --if-marked TimeoutStartSec=300 The unit's hardening: ProtectSystem=strict, which leaves /etc read-only to the service; /usr read-only, which with the single capability below is why the service can neither run the package manager nor stop itself; ProtectHome=yes, which hides /home; ReadWritePaths of /var/lib/hmailserver and /var/log/hmailserver; RuntimeDirectory=hmailserver, which is /run/hmailserver; and one capability, CAP_NET_BIND_SERVICE, which binds the mail ports. CAP_NET_ADMIN is deliberately not granted, so the firewall hook behind AutoBanFirewall cannot act until a drop-in (systemctl edit hmailserver) adds it to AmbientCapabilities and CapabilityBoundingSet; sudo -u hmailserver /usr/lib/hmailserver/autoban-hook check says whether it can. A DataFolder outside ReadWritePaths needs a drop-in too, before the first start: sudo systemctl edit hmailserver # [Service] # ReadWritePaths=/srv/mail A memory-safety fault is recorded in one line of /var/log/hmailserver/crash-oracle.log; core dumps are off by default. The first start secures a new installation. On a database --create-database made, the ExecStartPre line gives the server a self-signed certificate, STARTTLS on 25, 587, 110 and 143, and TLS required for authentication on the Internet IP range; mail clients warn about the certificate until ACME (Let's Encrypt certificates) or your own replaces it. It acts only on a database marked within a day of its clock and still as the create script left it; an older mark is kept for a run by hand, sudo -u hmailserver hmailserver --apply-new-install-defaults with the server stopped, and an upgraded or attached database is never changed. The mark, the certificate and the checklist item that follows up are in Hardening a public mail server. The root helper hmailserver-update (since 6.3.4) A live update is applied outside the server's unit. The server writes a one-file request into /run/hmailserver; hmailserver-update.path, a root path unit the package enables, takes it and runs /usr/lib/hmailserver/hmailserver-update, a shell script readable end to end. The script: checks every line of the request against root's own configuration; verifies the package, and the rollback image, again as root against their Sigstore bundles, with hmailserver --verify-update: the check the server made, made by a process the server could not have influenced; refuses anything that is not a genuine upgrade: the package's own version is compared with what dpkg or rpm says is installed, so a signed but superseded release cannot be put back; stops the unit, installs with apt-get/dpkg or dnf/rpm, starts the unit, and waits for the server to stay up, and for /readyz when the metrics listener is on; reinstalls the previous version if it does not, and writes one line to <DataFolder>/Updates/last-apply.txt, which the server reports at its next check and the Control Deck shows. A refused request is explained in journalctl -u hmailserver-update and nowhere else, on purpose. sudo /usr/lib/hmailserver/hmailserver-update check names the installer it would use, whether the path unit is active and whether the running unit has the runtime directory the request needs. The Debian and RPM packages enable and start the path unit; Arch does not enable units from a package, so there sudo systemctl enable --now hmailserver-update.path is yours to run. Nothing happens unless the update check is on and the install is asked for, from the Control Deck or over the API, and only for a release signed by a signer the server trusts, which 6.3.4 is not. hmctl and the PowerShell module The other command line is hmctl (/usr/bin/hmctl, one Python 3 file), with the HmailServer PowerShell module beside it in the repository. Both speak the REST API, so both administer a Windows or a Linux server, here or on another machine. They cannot turn the REST listener on, set the administrator password, or make or carry the stored-secrets key. They have a topic of their own: hmctl and the PowerShell module. Logs and the journal The server's own logs are in /var/log/hmailserver. The journal (journalctl -u hmailserver -f) carries the startup line, standard error and the operational events: WindowsEventLogEnabled=1 is a Windows inheritance in name only, and on Linux means syslog. The logrotate rule uses copytruncate, because the server does not reopen its log on a signal. Where to read more On the wiki: Installing on Linux, and Command Line for hmctl. CommandLine.md, SecretsAtRest.md, SecureDefaults.md, Profiles.md and MessageStore.md. The documentation page: 35.2 Supported platforms and packages, 35.7 Command-line reference, which lists none of the verbs new in 6.3.4, and 35.8 The systemd service. On this forum: hMailServer on Linux, First install on Linux, Upgrading to 6.3.4 and The Control Deck; Databases on Linux for --create-database and the schema upgrade; the update check and the live update for what the root helper is asked to apply; and message storage for what the store verbs change.
  • 0 Votes
    1 Posts
    129 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.8 (2 November 2026), a security release, is the release to install: as 6.3.7 (17 October 2026) did, beside the generic .deb and .rpm for x86-64 and AArch64 it carries a package for each of Debian 12 and 13, Ubuntu 24.04, Fedora 43 and 44, Enterprise Linux 9 and 10 and openSUSE Leap 16.0 and Tumbleweed, each installed in a clean container of its release by that release's own package manager before publication, so step 1 is apt, dnf or zypper with your release's own package; the AArch64 files are cross-compiled and tested under emulation. Check a download with sha256sum -c --ignore-missing hmailserver-linux-6.3.8-SHA256SUMS.txt first. See the 6.3.8 announcement. The order matters more than the commands. The package installs the server enabled and stopped, on purpose: it has no database yet, and nothing in a package can know which backend you have or its credentials. The full sequence, with the output each step prints, is §35.3. Take the exact file names from the downloads page, and note the RPM carries a -1 release field in its name. 1. Install the package. sudo apt install ./<file>.deb on Ubuntu 26.04 or 24.04, sudo dnf install ./<file>.rpm on Fedora 43 or 44 or Enterprise Linux 10. 6.3.4's .deb needs glibc 2.38 or later, so it does not install on Debian 12; its .rpm does not install on Enterprise Linux 9, and nothing has been tried with zypper on openSUSE. The maintainer script creates the hmailserver system user and group, creates the directories, and enables the unit without starting it. It deliberately does not walk or chown an existing store. 2. If your backend is MySQL or MariaDB, install the client library now. The server opens it at run time and no package manager will pull it in: libmariadb3 on Debian and Ubuntu, mariadb-connector-c on Fedora and RHEL, mariadb-libs on Arch. PostgreSQL needs nothing extra. 3. Create the database role. Either let the role create its own database, or create an empty one yourself and leave the role without CREATEDB. §35.4 has both, and the SSL keys for a database on another host. 4. Edit /etc/hmailserver/hMailServer.ini. Fill in [Database]. Two rules catch people. Only ; starts a comment, and only at the start of a line. Text after a value on the same line is part of the value, so Type=PostgreSQL ; the backend matches no backend name. Write the port out: PostgreSQL wants 5432, MySQL and MariaDB want 3306. Leave AdministratorPassword alone. 5. Set the administrator password. sudo hmailserver --set-admin-password reads it from standard input with echo off and writes the hash into the file. This is the one step that runs as root, because the file is 0640 root:hmailserver. 6. Create the schema. sudo -u hmailserver hmailserver --create-database. Then, as root, make the stored-secrets key that second factors and the [Settings] secrets are sealed under, and export it at once: sudo hmailserver --create-stored-secrets-key, then sudo hmailserver --export-stored-secrets-key /root/hmailserver-stored-secrets.key. Keep that file with the database backups, stored apart from them. Without a key those secrets are stored as written, as before 6.3.4. 7. Check it before starting anything. sudo -u hmailserver hmailserver --check-config, as the service user and without --config, so the server has to find the configuration on its own. Database type: 0 means the Type key did not take. It is a report and not a validator: it returns 0 unconditionally, so read the output rather than its exit status. 8. Start it. sudo systemctl start hmailserver, then journalctl -u hmailserver -f. A freshly created database already holds four listeners: SMTP on 25 and 587, POP3 on 110 and IMAP on 143, bound to every address. Since 6.3.4 the unit's first start, if it comes within a day of --create-database, gives them a self-signed certificate and optional STARTTLS, and makes the Internet IP range require TLS for authentication (SecureDefaults.md). Mail clients warn about a self-signed certificate, so replace it with ACME or your own before this faces the internet. The listeners and the certificate are database rows, not INI keys, so you change them over the API or the Control Deck's Ports and Certificates views. 9. Turn on administration. sudo hmailserver --set-setting RestApiPort=8080 (or another free port), leave RestApiBindAddress on loopback, and sudo systemctl restart hmailserver. Not by editing the file: since 6.3.4 the database is the settings store, and a [Settings] line edited after the first start is reported as HM5804 and not applied: the service cannot write the packaged file, so the line stays as you left it and the stored value is used. TLS is required unless the bind address is exactly 127.0.0.1, localhost or ::1, so tunnel over SSH rather than exposing it. §35.9 and §35.10 are the rest. Post what --check-config printed and the last lines of journalctl -u hmailserver if a step stops. §35.16 lists the Linux-specific failures by symptom.