Skip to content

Upgrading hMailServer

Christopher Holloway edited this page Oct 26, 2026 · 34 revisions

Upgrading hMailServer

This page began as a chapter of the 6.2.10 manual and has been corrected for 6.2.28. The Control Panel's pages are grouped differently now, so the paths below use today's groups; the TLS ports 465, 993 and 995 exist only after you create them on the TCP/IP ports page (a fresh install seeds 25, 587, 110 and 143); and everything added since 6.2.10 is in Changes-Since-6210. Where a value here disagrees with the Settings Reference, which is generated from the code, the reference is right. The schema figures, the upgrade without a window (§18.4b) and the settings notes were brought up to master 540347500 on 24 September 2026, and the whole page to 6.3.4 (27 September 2026, schema 6104), from master 92b8f9f63 and, for what the release's last batch changed - step 6104, the installer's HM_ADMIN_PASSWORD, the download links and the checks of a download - from 8adce850a, the commit the tag v6.3.4 was first made on, with its hmailserver/docs/Upgrading.md and UpdateFeed.md, all of it the same at the tag, 51e2dbda0; §18.4c is what is particular to upgrading to 6.3.4. Brought up on 5 October 2026 to 6.3.6 (6 October 2026, schema 6108), for what 6.3.6 changes on this page, and on 18 October 2026 to 6.3.7, the latest release (17 October 2026, schema 6108), from the tag v6.3.7 (31d656c0c) and its release: §18.4f is what is particular to upgrading to 6.3.7, and §18.1, §18.2, §18.4 and §18.7 say what is current.

18.1 How upgrades work

hMailServer upgrades in place. The installer stops the service, replaces the program files, upgrades the database schema if needed, and restarts. Your mail, accounts, domains and settings are preserved.

Two things change during an upgrade:

  1. Program files in C:\Program Files\hMailServer\Bin are replaced.
  2. The database schema is upgraded by DBUpdater, which the installer runs automatically. Schema version of 6.3.7, the current release: 6108, as 6.3.6 has it (6.3.4's was 6104 and 6.3.3's 6040, so an upgrade from 6.3.3 applies 68 steps).

Your data directory (the messages) and hMailServer.INI are left alone by the installer. Since 6.3.4, the first start of a server at schema 6042 or later does one thing to the INI's [Settings] section: the database becomes the settings store - keys the 6.3.3 mirror already held keep their stored values, any key the database never held is taken in from the file - and the section becomes a copy the server rewrites. Nothing has to be run by hand for that - but see 18.3 step 6.

What the installer actually does, in order

flowchart TD
    A["Setup starts"] --> B{"Is the hMailServer<br/>service running?"}
    B -- yes --> C["Stop it and wait<br/>ShutdownDrainSeconds plus 30 s"]
    B -- no --> D
    C --> E{"Reached STOPPED<br/>inside the budget?"}
    E -- no --> F["Refuse to go past the Ready page.<br/>A file copy over a running service leaves<br/>the OLD binary against the NEW schema"]
    E -- yes --> D["Move a pre-5.x hMailServer.ini<br/>out of the Windows directory into Bin"]
    D --> G["Copy the program files"]
    G --> H["Install the bundled .NET 10 Desktop Runtime,<br/>but only if it is missing"]
    H --> I["Register the service"]
    I --> J["Run DBSetupQuick, which drives DBUpdater"]
    J --> K{"Exit code 0?"}
    K -- no --> L["Dialog naming the exit code,<br/>then the install FAILS with a non-zero exit code"]
    K -- yes --> M["net start hMailServer"]
    M --> N{"Is the service running?"}
    N -- no --> O["Dialog: check hMailServer.log<br/>and the ERROR log"]
    N -- yes --> P["Completed"]
Loading

Two of those boxes are worth reading properly.

The service stop is bounded, and a failure to stop aborts the upgrade. The budget is ShutdownDrainSeconds plus 30 seconds, derived from your own setting rather than guessed, because that setting deliberately holds the stop open while sessions finish. If the service will not stop, setup refuses to go past the Ready page - because a file copy over a running service offers Retry/Ignore, and choosing Ignore leaves the old binary in place while the schema moves to the new version, after which the server refuses to start at all.

A failed database upgrade now fails the installation. The exit code of DBSetupQuick is checked, a dialog names it, the service start is still attempted, and then setup raises and finishes with a non-zero exit code. A scripted deployment can tell. (This is a change: in earlier releases the dialog was the only signal and the wizard still reported success.)

18.2 The upgrade path

The database upgrade chain is continuous from every earlier release of this fork, and of the original hMailServer up to 5.6, on MySQL, MS SQL, PostgreSQL and SQL CE. You do not need to step through intermediate versions:

flowchart LR
    A["Schema 0<br/>hMailServer 1.0"] --> B["1100 - 3402<br/>hMailServer 1.x to 3.x"]
    B --> C["4000 - 4402<br/>hMailServer 4.x"]
    C --> D["5000 - 5708<br/>hMailServer 5.x<br/>PostgreSQL and SQL CE<br/>join at 5001"]
    D --> E["6001 - 6005<br/>hMailServer 6.0 to 6.2.18"]
    E --> F["6006 - 6011<br/>6.2.19"]
    F --> G["6012 - 6022<br/>the 6.2.22 pre-releases"]
    G --> H["6023 - 6025<br/>6.2.23-alpha1, 6.2.24"]
    H --> I["6026 - 6030<br/>6.2.25"]
    I --> J["6031<br/>6.2.27 to 6.3.1"]
    J --> K["6032 - 6038<br/>6.3.2"]
    K --> L["6039 - 6040<br/>6.3.3"]
    L --> M["6041 - 6104<br/>6.3.4"]
    M --> N["6105<br/>6.3.5"]
    N --> O["6106 - 6108<br/>6.3.6, and 6.3.7, the current release"]
Loading

Not the original's 5.7 builds of August and September 2026 (5.7.0, 5.7.1 and 5.7.2). The original went on from schema 5708 by steps of its own - 5709, 5710 and 5711 - which this fork's chain does not have. From the release after 6.3.7 the installer refuses such an installation before it changes anything (Upgrading Guide, Where you can upgrade from); the installers of 6.3.7 and earlier do not look, and must not be run over one. Over an installation whose settings are in its Bin folder their DBUpdater stops before it changes anything, but setup has replaced the program files by then and the server refuses the database as too old; over one that the original's 5.7.0 build 3013 or later made new, with its settings and data in C:\ProgramData\hMailServer, setup takes it for a new installation and sets up a new, empty database beside the original's. Install this server on another machine instead, make the domains and accounts there first, and move each mailbox's mail with a migration of kind imap from the original server (Migration) - a Windows machine holds one of the two, since both installers register as hMailServer_is1. ForkAndOriginal.md says what else differs between the two projects.

DBUpdater walks every intermediate step for you, one at a time, in one transaction. It builds the whole path first and checks that every script file on it exists before it runs the first one, so a missing dialect script is named and refused rather than discovered halfway.

Coming from Its schema Path Notes
6.3.7 6108 Nothing to do: it is the current release
6.3.6 6108 Its live update installs 6.3.7, or run the 6.3.7 installer; one installed from a distribution release's own package is upgraded with its package manager - see 18.4f No step: 6.3.7 adds none
6.3.5 6105 Run the current installer - see 18.4e and 18.4f Three steps, to 6108, one-way
6.3.4 6104 Run the current installer - see 18.4e and 18.4f Four steps, to 6108
6.3.3 6040 Run the current installer, by hand - see 18.4c Sixty-eight steps, to 6108. 6101 adds two columns to hm_messages, and on MySQL and MariaDB 6102 rebuilds every table - see 18.2a
6.3.2 6038 Run the current installer Seventy steps
6.2.27 - 6.3.1 6031 Run the current installer Seventy-seven steps. Two of them add columns to hm_messages (6036, 6101) - see 18.2a
6.2.25 / 6.2.26 6030 Run the current installer Seventy-eight steps
6.2.24 6025 Run the current installer Eighty-three steps. One of them reads every child table - see 18.2a
6.2.19 - 6.2.21 6011 Run the current installer Ninety-seven steps
6.2.10 - 6.2.18 6005 Run the current installer A hundred and three steps
6.0 / 6.1 read hm_dbversion Run the current installer Schema upgraded automatically
5.6 read hm_dbversion Run the current installer Schema upgraded automatically. Read 18.5 - the GUI has changed
5.3 - 5.5 read hm_dbversion Run the current installer As above. Very old installs: back up first and test the restore
The original's 5.7 development builds up to 5708 Run the current installer Upgraded as 5.6 is: this fork extended the original's schema from 5708 by steps of its own
The original's 5.7 builds of August and September 2026 (5.7.0, 5.7.1, 5.7.2) 5709 - 5711 Not the installer: install this server on another machine, make the domains and accounts there first, and move each mailbox's mail with a migration of kind imap from the original server (Migration) - a Windows machine holds one of the two, since both installers register as hMailServer_is1 The original went on from 5708 by steps of its own, which this fork's chain does not have. From the release after 6.3.7 the installer refuses such an installation before it changes anything. The installers of 6.3.7 and earlier do not look, and must not be run over one: they replace the program files, and the server then refuses the database as too old or, over an installation that the original's 5.7.0 build 3013 or later made new, a new, empty database is set up beside the original's

How far back the chain reaches depends on your backend. The steps are registered once for all four, but the SQL files are per-dialect and they do not all exist: MySQL and MS SQL ship every step back to schema 0; PostgreSQL and SQL Server Compact ship every step from 5001 onwards. In practice that is not a gap, because neither was a supported backend before hMailServer 5, so no older database exists on either.

18.2a The steps in this fork, and which of them cost time

Everything from 6005 onwards, with the operational note that matters. The full "what changed" table is in Changes-Since-6210; this one answers "how long will it take and what should I watch".

Step Shipped in What it does Plan for it
6005 to 6006 6.2.19 hm_imapfolders.folderspecialuse Instant
6006 to 6007 6.2.19 Two hm_domains columns for DKIM key rotation Instant
6007 to 6008 6.2.19 Two hm_tcpipports columns for client certificates Instant
6008 to 6009 6.2.19 hm_sslcertificates.sslprivatekeypassword Instant
6009 to 6010 6.2.19 Two hm_settings rows for ARC Instant
6010 to 6011 6.2.19 New table hm_inisettings Instant
6011 to 6012 6.2.22-pre1 Widens hm_rule_criterias.criteriamatchvalue to 2000; adds hm_accounts.accountvacationbegindate Fast; rule criteria are few
6012 to 6013 pre1 hm_messages.messagesavedate, then UPDATE every message row to seed it Scales with mailbox size. On a store of millions of messages this is the first slow step
6013 to 6014 pre1 New table hm_imap_metadata Instant
6014 to 6015 pre2 hm_messages.messageemailid, then UPDATE every message row Scales with mailbox size, same as 6013
6015 to 6016 pre3 New table hm_apppasswords Instant
6016 to 6017 pre3 hm_accounts.accounttotpsecret Instant
6017 to 6018 pre3 New table hm_quarantine Instant
6018 to 6019 pre3 hm_accounts.accountpasswordchanged plus an update of every account; new table hm_passwordhistory Fast; accounts are few
6019 to 6020 pre6 New table hm_messagetrace Instant
6020 to 6021 pre6 Six hm_domains.domainrelay* columns Instant
6021 to 6022 pre6 Two hm_servermessages rows for quota warnings Instant
6022 to 6023 6.2.23-alpha1 New tables hm_messageindexterms, hm_messageindexstate Instant; they stay empty until full-text indexing is switched on
6023 to 6024 alpha1 Six hm_domains.domainvacation* columns; new table hm_blocked_senders Instant
6024 to 6025 alpha1 hm_messages.messageflags tinyint to smallint, plus three hm_accounts and two hm_distributionlists columns A table rewrite of hm_messages on MS SQL, SQL CE and MySQL. PostgreSQL was already smallint. This is the single most expensive step in the chain on a large store
6025 to 6026 6.2.25 One hm_settings row Instant
6026 to 6027 6.2.25 Retention-day columns on domains and accounts Instant
6027 to 6028 6.2.25 New table hm_metricsamples Instant
6028 to 6029 6.2.25 New table hm_archiveindex Instant; only copies made from then on are indexed
6029 to 6030 6.2.25 Seventeen FOREIGN KEY ... ON DELETE CASCADE constraints, with orphan rows deleted first, children before parents; MySQL also gets ENGINE=InnoDB on every table involved Reads every child table once. Plan for it the way you would plan an index build
6030 to 6031 6.2.27 hm_fetchaccounts.famirrorfolders Instant
6031 to 6032 6.3.2 New table hm_contacts (the address book), cascading from hm_accounts Instant
6032 to 6033 6.3.2 New table hm_accountprefs (the webmail's remembered choices) Instant
6033 to 6034 6.3.2 New table hm_scheduled (messages put off to later) Instant
6034 to 6035 6.3.2 New table hm_files (files sent as links) Instant
6035 to 6036 6.3.2 hm_messages.messagekeywords, a NOT NULL DEFAULT '' column added to the message table on all four backends The one step here that touches hm_messages. How long an added column with a default takes depends on the backend and its version; on a large store, time it on a copy first
6036 to 6037 6.3.2 New table hm_smimekeys (S/MIME certificates and keys) Instant
6037 to 6038 6.3.2 hm_surblservers.surblresult Instant
6038 to 6039 6.3.3 Widens hm_domains.domainrelaypassword to 1024 Fast; domains are few
6039 to 6040 6.3.3 hm_contacts.contacturi, contactuid, contactvcard (CardDAV) Fast; scales with the address books, not the message store
6040 to 6041 6.3.4 New tables hm_calendars, hm_calendarobjects (CalDAV) Instant
6041 to 6042 6.3.4 update hm_dbversion and nothing else: the version is the fence that keeps a 6.3.3 server, which lets hMailServer.INI win, away from a database that is now the settings store Instant. The move of the [Settings] keys into the database happens at the server's first start afterwards, not in DBUpdater - see Settings Overview
6042 to 6043 6.3.4 New tables hm_audit, hm_alertrules, hm_alertevents; nine rows in hm_settings for alerts and the audit trail, each inserted only if absent Instant
6043 to 6044 6.3.4 Seven hm_domains columns for the external-sender tag, the first-contact note and the disclaimer; new table hm_knownsenders Fast; domains are few
6044 to 6045 6.3.4 New table hm_remotedomainpolicies Instant
6045 to 6096 6.3.4 Fifty-one more steps, one per feature: new tables and columns for sender lists, roles, DKIM rotation, bounces, OpenPGP, webhooks, country blocking, devices, Web Push, complaints, mailbox moves, reputation, spam traps, out-of-office, quota policy, mail flow rules, calendars and their sharing, send-as, masked addresses, recoverable items, expiring messages, DMARC reports, compromised accounts, greylisting, the domain policy list, lists, journaling, recovery, passkeys, the directory, address books, zero-access, restricted delivery, recall, impersonation, setting levels, the send API, migrations, the sandbox, IP warm-up, gateway encryption, the S/MIME authority and SCIM. Each is listed in section 2 of Changes-Since-6210 None touches hm_messages. 6071 to 6072 widens the greylisting triplet table's two address columns and indexes it, which scales with that table; every other step adds tables, or columns to small tables
6096 to 6097 6.3.4 New tables hm_discoverycases, hm_discoverysearches, hm_discoveryholds, hm_discoveryexports (eDiscovery); the view_discovery and manage_discovery permissions added to the built-in serveradmin role, view_discovery to auditor Instant
6097 to 6098 6.3.4 New table hm_storeblobs (de-duplicated attachment parts); hm_settings rows MessageStoreCompression 0 and MessageStoreDedupMinKB 256, each inserted only if absent Instant. Nothing already stored is rewritten: compression is off, and --compress-store is a separate, deliberate step
6098 to 6099 6.3.4 New tables hm_accountkeys and hm_domainrecovery (mailbox encryption at rest); hm_settings row MailboxKeyIdleMinutes 15 Instant. Off on every account
6099 to 6100 6.3.4 New tables hm_inboundhooks and hm_inboundhookqueue (mail in to a webhook); the manage_inbound_webhooks permission added to serveradmin Instant
6100 to 6101 6.3.4 hm_accounts.accountkind (0, a person); new tables hm_bookingpolicies, hm_bookinganswers, hm_bookingqueue, hm_bookingdecisions (rooms and equipment); two columns on hm_messages, messagesubmitterid (0) and messageauthdomains ('') The step that touches hm_messages, which has a row for every stored message. As with 6036, how long an added column with a default takes depends on the backend and its version: on a large store, time it on a copy first
6101 to 6102 6.3.4 MySQL and MariaDB: every table converted to utf8mb4 under utf8mb4_general_ci, InnoDB and ROW_FORMAT=DYNAMIC, and the database's default with them. SQL Server, SQL Server Compact and PostgreSQL only move the version On MySQL and MariaDB, the slowest step since 6025: every table is rebuilt, the message tables taking the time, with free disk of the order of the largest table. It needs MySQL 5.7.9 or MariaDB 10.2.2 and the ALTER privilege on the database, and it can stop on error 1062 - see 18.4c
6102 to 6103 6.3.4 hm_accounts.accounttotpsecret widened to 1024 characters, to hold a sealed second factor (secrets at rest) Fast; accounts are few. Nothing is sealed by the step - see 18.4c
6103 to 6104 6.3.4 hm_fetchaccounts.faorigin, 0 on every row: who set a fetch account's server and user name, so that one a person makes in the webmail is fetched only from a public address (unless ReaderFetchPrivateHosts is on) and one made without the server's authority is never given the server's own OAuth2 token Fast; fetch accounts are few. An existing fetch account behaves as it did
6104 to 6105 6.3.5 hm_quarantine.quarantinehelo, the HELO name of a held message Instant; messages held before have no HELO name recorded
6105 to 6106 6.3.6 No column changes: every IMAP external account's days to keep messages is translated to the one meaning both fetchers read from 6.3.6 (18.4e) Instant; no account starts deleting
6106 to 6107 6.3.6 The greylisting key finished: every row keyed, the key unique, one row per triplet; rows whose address had been cut to 200 characters are deleted On a very large hm_greylisting_triplets, the keying runs before the transaction and commits as it goes, so a run stopped part-way continues where it stopped; a sender whose row was deleted is greylisted once more
6107 to 6108 6.3.6 New table hm_neverbannames; hm_tcpipports.portsmtpauth (On, Off or Honeypot), On for every listener an upgrade finds Instant

So: from 6.2.24 or later, the upgrade is quick whatever the size of your store, with the caveats of 6036 and 6101 on a very large hm_messages, 6072 on a very large greylisting table, and - on MySQL and MariaDB only - 6102, which rebuilds every table. From 6.2.21 or earlier it crosses 6012, 6014 and 6024, all three of which touch every message row - which is the reason to do it in a maintenance window rather than at lunchtime.

18.2b What DBUpdater does with each step

flowchart TD
    A["Read hm_dbversion"] --> B{"Newer than<br/>this build needs?"}
    B -- yes --> C["Refuse: downgrading a database<br/>is not supported"]
    B -- no --> D{"Equal?"}
    D -- yes --> E["Nothing to do; exit code 0"]
    D -- no --> F["Build the whole path,<br/>checking every script file exists"]
    F --> G["BEGIN TRANSACTION"]
    G --> H["Run the prerequisites script<br/>MS SQL and PostgreSQL only"]
    H --> I["For each step: EnsurePrerequisites,<br/>then run Upgrade&lt;from&gt;to&lt;to&gt;&lt;dialect&gt;.sql"]
    I --> J["Run the schema PROBES<br/>on the same connection,<br/>so they see the uncommitted DDL"]
    J --> K{"Every probe satisfied?"}
    K -- no --> L["ROLLBACK where the backend can,<br/>and report which step"]
    K -- yes --> M["COMMIT"]
    M --> N["Read hm_dbversion back"]
    N --> O{"Equals the required version?"}
    O -- no --> P["Report: the scripts ran without error<br/>but the database is at the wrong version"]
    O -- yes --> Q["Success; exit code 0"]
Loading

The probe step exists because of a specific trap. Three of the four dialects mark their ALTER TABLE statements [IGNORE-ERRORS] so that re-running a step is harmless - but the update hm_dbversion statement in the same script carries no marker and always runs. A marked ALTER that failed for a real reason would therefore stamp the new version onto a database that does not have the column. Each such step now has a probe that must be satisfied before the commit, and build/check-schema-versions.ps1 proves that every step has one.

DBUpdater's exit codes, which is what a scripted install reads:

Code Meaning
0 Success, or nothing to do
1 The upgrade failed
2 A configuration error - unknown database type, missing script directory, or no path to the required version
3 Authentication was cancelled, or under /silent could not be satisfied

18.3 Before you upgrade

  1. Back up. Database, data directory and hMailServer.INI. See chapter 15. The database half is the one that makes rollback possible; the data directory is the half that cannot be reconstructed from anything else.

  2. Write down two numbers. The server version (Help → About, or the Control Panel's About page) and the schema version. The Control Panel shows the schema on Server status; when the server will not start, read it directly:

    select value from hm_dbversion;

    hm_dbversion is a single-column, single-row table of its own - not a row in the settings table. If you ever open an issue, those two numbers are the first two questions.

  3. Estimate the outage from 18.2a. From 6.2.24 or later, a minute or two. From 6.2.21 or earlier on a large store, longer, because three steps touch every message row.

  4. Pick a quiet time. Senders retry, so nothing is lost, but users will notice.

  5. Know your administrator password. A silent upgrade needs it on the command line - see 18.4a.

  6. (Upgrading 6.3.3 or earlier to 6.3.4.) Restart the service once after your last change to a [Settings] key, before you upgrade. The new build's first start adopts the file's values only for keys the database has never held; for the rest it keeps the stored value, and a file line that differs from what the old server last recorded is treated as an edit - reported as HM5804 and not used, and put back where the service may write the file (under NT SERVICE\hMailServer, to which the file is read-only, it stays as edited - The Service Account). A 6.3.3 server records a change at its next start, and a 6.3.3 Control Panel running on the server writes the file directly, so a change made in the file or on that Control Panel and never followed by a restart would be lost. If HM5804 appears after the upgrade, set the named keys again in the Control Panel.

18.4 Doing it

  1. Download the installer: https://updates.progressiverobot.com/download/hMailServer-x64.exe always fetches the newest stable release's installer, never a pre-release's, and https://updates.progressiverobot.com/download/ leads to that release's page; for 6.3.7 the file is hMailServer-6.3.7-x64.exe. GitLab's own latest release follows the release published last, a pre-release included. Check the download: for 6.3.7, against the SHA-256 the Download table of the release's notes gives it (dcae96bd75d6cae8e5a910d15940567067539ed99db5aeed40a815added4d85f for the x64 installer), or against its Sigstore bundle (Security Policy, Verifying a release) - 6.3.4's against its hmailserver-6.3.4-SHA256SUMS.txt and that file's SSH signature - and Get-AuthenticodeSignature on the installer should answer Valid, signed by Progressive Robot Ltd.
  2. Run it as administrator. A server on 6.3.6 can let its live update install 6.3.7 instead, and a 6.3.4 or 6.3.5 one under LocalSystem too (18.4f); 6.3.4 itself no server's live update installs (18.4c).
  3. The installer detects the existing installation and offers to upgrade. Accept.
  4. Keep your existing database settings when asked.
  5. Let it finish, then confirm the service is running.
  6. Open the Control Panel and check About shows the version you just installed (6.3.7 today).
  7. Send a test message in and out, and read it back over IMAP or POP3. A service that starts is not the same as a server that delivers.
  8. Read the ERROR log for the window around the upgrade. An empty ERROR log is the signal you want.

18.4a Unattended

$p = Start-Process -Wait -PassThru -FilePath .\hMailServer-6.3.4-x64.exe `
     -ArgumentList '/silent', '/SUPPRESSMSGBOXES', '/adminpassword=<password>'
if ($p.ExitCode -ne 0) { throw "hMailServer upgrade failed with exit code $($p.ExitCode)" }
  • With an administrator password set, the installer needs it - /adminpassword= or, from 6.3.4, the environment variable HM_ADMIN_PASSWORD, set in an elevated prompt, because it does not cross the UAC prompt. A password with a double quote goes in the variable: Inno Setup drops every double quote from its command line, and /adminpassword= is readable by anybody listing processes while setup runs. From 6.3.4 the installer hands the password to DBSetupQuick.exe, and DBSetupQuick to DBUpdater.exe, in HM_ADMIN_PASSWORD; until then it went on their command lines unquoted, and a password with a space, a double quote or backslashes before one failed the database step with exit code 3. DBUpdater has to authenticate before it can move the schema. Without it the database upgrade fails and the installer reports the non-zero exit code rather than waiting - which is the fix for a real defect: until 6.2.23 a silent upgrade of an installation with a password set hung, on a modal password dialog nobody was there to answer.
  • With an empty administrator password, a silent upgrade works with no extra argument.
  • The password must be at least five characters, or setup refuses it up front.
  • (New in 6.2.28: the live update's helper passes a single-use /upgradetoken=<hex> in place of the password, so an unattended self-update never handles the administrator password at all.)
  • (New in 6.3.3: the first live update started from an open Control Panel failed with Inno's exit code 5, and so did its rollback - the installer replaces the Control Panel's own files and, silent, aborts at the first one in use. The helper now ends whatever runs from under the installation before each installer run, the Control Panel closes itself once the helper has started, and a failed run's outcome quotes the installer's own log. Because the helper that runs an update is the one already installed, an installation on 6.3.1 or 6.3.2 has to be updated by hand once - close the Control Panel, run the installer - and live updates work from then on.)

18.4b An upgrade without a window (6.3.4)

New in 6.3.4. Three things change how an upgrade feels. The drain is done by the server being stopped, so it first serves the upgrade from 6.3.4 to a later release; the upgrade to 6.3.4 stops a 6.3.3 or older service the way it always did. UpgradeOnStart is read by the binary that starts, so it can serve the upgrade to 6.3.4 itself.

  • The server drains before it stops. The SMTP, IMAP, POP3 and ManageSieve listeners are closed at the socket - a connection attempted then is refused, which every sending server treats as try again later - and the delivery queue is paused, so what is in flight finishes and nothing new is accepted only to be cut. The webmail, CalDAV and CardDAV, the REST API and the metrics listener stay up; /readyz answers 503 not ready: draining. The live update drains first by itself; for maintenance of your own, POST /api/v1/server/drain enters the state, GET reports it and DELETE leaves it. Set ShutdownDrainSeconds (30 is a reasonable figure) so that the stop waits for what is still in flight.
  • The service can bring its own schema forward. With the new bootstrap key [Database] UpgradeOnStart=1 in hMailServer.ini, a service that finds the database older than its binary applies the upgrade chain itself as it starts - the same code as hmailserver --upgrade-database, one log line per step - and then carries on starting. It is for binaries replaced by something other than this installer (a deployment tool, a container, the second node of a pair). It is off by default, it backs nothing up first, and it belongs on one node at a time: set it on the node you start first. It is in the file, not the settings store, because it has to be read before the database is at a version the binary can open.
  • A rolled-back update says so. When the live update's helper had to reinstall the previous version, the outcome is reported when the service comes back - in the log, on the status page and in GET /api/v1/update - rather than only in the helper's file. On Linux the live update now applies too, through a root path unit that verifies the package again before installing (Installing on Linux).

What a single node still cannot promise is written in the repository's Upgrading.md: a restart is still a restart, and the listeners are closed while the binary is replaced.

18.4c Upgrading to 6.3.4

6.3.4 (27 September 2026, build 43, schema 6104) is the first release built after GitHub suspended the project's account, and it is upgraded to differently from every release since 6.2.28.

Every server installs it by hand. 6.3.4 is Authenticode-signed but carries no Sigstore bundle: it was built and signed by hand, before the project's GitLab CI signing job existed. A server's live update verifies every installer against the Sigstore bundle published beside it, so a server of any version that is offered 6.3.4 reports Release 6.3.4 carries no Sigstore bundle for its installer, so it cannot be verified. and downloads nothing. So:

  • On Windows, run hMailServer-6.3.4-x64.exe over the existing installation (18.4), or silently (18.4a). Check it first against the release's SHA-256 sums file, which is signed with the release tags' SSH key: ssh-keygen -Y verify and sha256sum -c, as Security Policy Verifying a release gives them.
  • On Linux, install hmailserver_6.3.4_amd64.deb with apt or hmailserver-6.3.4-1.x86_64.rpm with dnf; the package runs hmailserver --upgrade-database itself. 6.3.4's Linux packages are x86-64 only: it has no AArch64 package and no AppImage (the project has no ARM machine, and the AppImage tool is published only on GitHub). An AArch64 or AppImage installation stays on 6.3.3's packages or builds 6.3.4 from source (Installing on Linux).

A server from 6.2.28 to 6.3.3 still looks at GitHub for updates, at the Releases API for the suspended account's repository, github.com/Progressiverobot/hmailserver, which has answered 404 since 18 September 2026 and still does - the releases published on GitHub again since 18 October 2026 are under another account, where such a server does not look. To have its update check work again - and see the newest release - put exactly this line in the [Settings] section of its hMailServer.ini, then restart the service:

[Settings]
UpdateFeedUrl=https://updates.progressiverobot.com/releases.json

The address without .json is a directory, and the server, which follows no redirect, reports The update feed answered HTTP 302. Its live update still installs nothing: the feed offers it only the newest release, which it cannot install (6.3.4 carries no Sigstore bundle, and every release after it is signed on GitLab, which it does not trust), and it downloads only the installer its check found, so even the re-hosted releases up to 6.3.3, which carry the bundles GitHub Actions made and verify against its signer, are installed by hand (The Update Feed, What those servers can install through it). Keep UpdateAutoDownload off on such a server until it is upgraded by hand, or it will download a GitLab-signed installer at every check and refuse it. 6.3.4 reads the new feed without being told and trusts both signers, so after the upgrade the line can go: clear it in the Control Panel, with DELETE /api/v1/settings/ini/UpdateFeedUrl, or on Linux with hmailserver --set-setting UpdateFeedUrl= - not in the file, where a changed line is not used (HM5804). The whole account is the repository's UpdateFeed.md. This wiki's copy of it is The Update Feed.

The releases after 6.3.4 are signed by the project's own GitLab CI, for their v* tag, and a 6.3.4 server's live update trusts them; every release from 6.3.5 has been signed there. A Windows service that runs as the virtual account NT SERVICE\hMailServer checks for updates but cannot apply one: an administrator downloads the installer from the release page, checks its Authenticode signature and runs it (the copy the server downloaded is not offered).

Take the backup first (18.3), and plan for the two steps that cost time (18.2a):

  • 6101 adds two columns to hm_messages, the table with a row for every stored message. On a large store it is not instant on every backend.
  • 6102, on MySQL and MariaDB, converts every table to utf8mb4 under utf8mb4_general_ci, InnoDB and ROW_FORMAT=DYNAMIC, so that an emoji or any character outside the Basic Multilingual Plane is stored instead of refused. It rebuilds every table, so it takes about as long as copying the database, and needs free disk space of the order of the largest table. It needs MySQL 5.7.9 or MariaDB 10.2.2 or later, as 6041 and 6101 before it do: each opens with a probe, so on an older server the upgrade stops at 6041 with error 1071 and nothing has been changed. It needs the ALTER privilege on the database itself, which GRANT ALL PRIVILEGES ON <database>.* gives (without it, error 1044). It can stop on error 1062, naming an index and a value, where two rows the old collation told apart are one to the new - on MariaDB 11.8 straße and strase in one address book, 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 be run twice, so a step stopped part of the way is finished by running it again. Afterwards a text column may read mediumtext; the server reads both alike. SQL Server, SQL Server Compact and PostgreSQL only move the version.
  • 6104 adds hm_fetchaccounts.faorigin, 0 on every existing row, so every fetch account there is behaves as it did; accounts a person makes from then on are fetched only from a public address, unless ReaderFetchPrivateHosts is on, and never with the server's own OAuth2 token.

What stays as it was on an upgrade, where a new installation differs:

  • The service account. A new Windows installation runs the service as NT SERVICE\hMailServer; an upgrade keeps the account it has (LocalSystem, for an installation from before 6.3.4) unless the installer's Service account page, or /serviceaccount=virtual|localsystem|keep, says otherwise (The Service Account).
  • The secure defaults. Only a database the installation created gets the self-signed certificate, STARTTLS on 25, 587, 110 and 143 and TLS required for authentication on the Internet range; an upgraded database is left as it was, and the setup checklist's Passwords only over TLS item judges it (Secure Defaults).
  • Sealed secrets are off until an administrator makes the installation's stored-secrets key: hMailServer.exe --create-stored-secrets-key from an elevated prompt (sudo hmailserver --create-stored-secrets-key on Linux), then --export-stored-secrets-key <file> and keep the file apart from the backups, and --import-stored-secrets-key on every other server that shares the database. Until then second factors and the [Settings] secrets are stored as written, as before, one application-log line at each start says so, and the setup checklist offers the step. The upgrade's own 6103 step only widens the column (Secrets at Rest).

What changes on upgrade without a switch - the settings store, SPF per RFC 7208, the audit trail and alerts, the IMAP answers, the database connection encrypted where the database server offers TLS, the administrator password re-hashed at sign-in, a COM change to a shared configuration object taking effect at Save(), and more - is listed in Changes Since 6.2.10 section 5 and in the repository's CHANGELOG.md. Read it before you upgrade a server that scripts itself over COM or relies on how IMAP answered.

18.4d Upgrading to 6.3.5

6.3.5 (29 September 2026, build 44, schema 6105) is the first release made by the tag's GitLab pipeline: Authenticode-signed through Azure Artifact Signing, and signed with Sigstore as the pipeline at the path the project began at, which a 6.3.4 server trusts - so a 6.3.4 server's live update installs it, on Windows and on Linux. A server from 6.2.28 to 6.3.3 is upgraded by hand (18.4c). One schema step, 6104 to 6105, adds hm_quarantine.quarantinehelo. What changes without a switch is the Control Panel's sidebar order and two group names, the quarantine's HELO column and grouping, the webmail's keyboard, an Outlook-style invitation shown as one, and the fields and COM members a script may meet (Changes-Since-6210 4f).

18.4e Upgrading to 6.3.6

6.3.6 (6 October 2026, build 45, schema 6108) is the first release signed at the group's path, gitlab.com/hmailserver/hmailserver, the first that carries every file its pipeline makes (the ARM64 Windows installer, a preview, among them), and the last release for the time being: after it the project makes security releases only (Security Policy, While releases pause). Upgrade every server running 6.3.5 or earlier: it fixes the flaws the project's own reviews of the code found (Changes-Since-6210 4g has the list) and takes OpenSSL 4.0.3 into the Windows installers. In the order that matters:

  • Back up the database and the data directory first. The three schema steps are one-way: 6106 changes no column and gives every external account's days to keep messages the one meaning both fetchers now read; 6107 finishes the greylisting key, deleting the rows in which an address had been cut to 200 characters (a sender whose row was deleted is greylisted once more; on a large table the keying commits as it goes, so a run stopped part-way continues); 6108 adds the never-ban list by reverse name and, for each SMTP listener, what it does with AUTH, On for every listener an upgrade finds. From 6.3.4 the upgrade runs 6.3.5's step 6105 first; from 6.3.3 or older it runs 6.3.4's steps too, and 18.4c applies.
  • Mail can stop flowing if your resolver strips DNSSEC. With DaneEnforcementEnabled and DnssecValidationEnabled on, as they are by default, an MX or TLSA lookup that cannot tell is no longer read as "unsigned": the host is not used, and a delivery with no host left is deferred with 4.7.0. A server whose resolver cannot answer DNSSEC queries - one that strips signatures, or a firewall that drops DNS answers longer than 512 octets - defers its mail to every domain it cannot prove unsigned, says so in each deferral and in the application log once in ten minutes. Make the resolver answer DNSSEC queries, or set DaneEnforcementEnabled to 0. A private name under a signed public domain, answered by an internal DNS server, is delivered to through a route (Encryption and Certificates, What DANE actually does; DNS for Your Domain).
  • Two more outbound TLS deferrals. A host that publishes only DANE-TA records and whose certificate does not meet them is deferred where it was delivered to as if no record were published; a host that TLSA records or an MTA-STS policy require TLS of, and that offers no STARTTLS, is deferred with 450 4.7.0 where the message bounced at the first attempt. A route's own STARTTLS (required) still refuses permanently (Routes and Relays).
  • External accounts: days to keep messages has one meaning - -1 deletes each message on the remote server once collected, 0 leaves every message there, N deletes it N days after collection, for POP3 and IMAP alike; from 6.2.25 to 6.3.5 the IMAP fetcher read 0 and -1 the other way round. After upgrading, look at your IMAP external accounts - one that was emptying its remote INBOX now shows -1 and goes on doing so until you set it to 0 - and at your POP3 accounts at -1, which go on deleting. Change any program or script that writes the value, and an OnExternalAccountDownload script that answers 1 for an IMAP account: from 6.3.6 the message is deleted, as the event has always said (The COM API and Scripting). A webmail or Deck page left open across the upgrade says the earlier meaning until it is reloaded; a change it sends with a negative number is refused with 409, reload the page.
  • Scripts and programs that use COM (Windows, with an administrator password set): Utilities.BlowfishEncrypt, BlowfishDecrypt, ResolveMXRecords, GetMailServer, IsLocalHost, CheckSieveSyntax, EvaluateSieveScript and CriteriaMatch answer only a caller that has called Authenticate, or an event script in the service. A hMailServer.Message or hMailServer.FetchAccount a program creates itself takes the credential of an Application the same program holds and has signed in on; with none, Message.Save and Copy, FetchAccount.Save and the other writes are refused. The new Application.CreateMessage() gives a message that carries the credential; Attachments.Add and Attachment.SaveAs answer the server administrator alone (The COM API and Scripting, Send a message from a script). Event scripts in the service, the Control Panel and the shipped DecryptBlowfish.vbs need no change.
  • The administrator's commands, the service account and the installer. hMailServer.exe's command lines - every one but RunAsService, /Debug and /Test - write nothing to the error log and say what they did on their own standard output and standard error: a script that read the error log after one of them reads its output instead. Every store command stops (exit 1) on a junction, mount point or symbolic link in the data folder that the administrators do not alone control, and names it (Message Storage). On Linux a store command, a profile or --apply-environment run as root runs as the service's account, or on as root over a data directory root owns and nobody else could have written into (Installing on Linux). The program folder's Data, Logs, Temp, Database and Events and hMailServer.INI become the administrators' and the service's alone: Setup runs hMailServer.exe /ProtectFolders at every installation and upgrade, whatever account the service runs as - on a large message store the first run takes minutes - and a script, backup program or monitoring agent that read the logs or the store without running as an administrator needs an entry of its own (icacls "<folder>" /grant "<account>:(OI)(CI)RX"), which later runs keep; the Control Panel not run as administrator says Could not read the file on its live log page (Security Hardening §17.3, Troubleshooting). An upgrade whose data, log or database folder sits behind a junction no longer fails at the service account's grant; /GrantServiceAccess that cannot read the database ends in 7 unless an administrator confirmed; and the installer exits 10 or 11 when a step after the files did not complete (18.6) - a script that runs the installer should read the two (Installing hMailServer, The Service Account).
  • The live update's trust is read from hMailServer.ini alone - UpdateSigningIdentity, UpdateSigningIssuer, UpdateSourceRepository, UpdateTrustRootsFile and UpdateLogPublicKeyFile, never from the database; COM and the REST API refuse a change (409). Most servers set none of the five. Three settings left from 6.3.5's by-hand route: a server whose three GitLab values still name the path the project began at refuses 6.3.6 - clear them first, as 18.7 says, not in the file. On Windows under NT SERVICE\hMailServer a 6.3.4 or 6.3.5 server still says to install 6.3.6 by hand: download hMailServer-6.3.6-x64.exe, check that Get-AuthenticodeSignature reports it Valid and signed by CN=Progressive Robot Ltd, and run it as an administrator, keeping the service account on the installer's Service account page; from 6.3.6 on, Install update in the Control Panel on the server installs the next release with one Windows prompt. On Linux, install 6.3.6 with the package manager (apt, dnf or zypper): the update to 6.3.6 is applied by the helper of the version it replaces, or turn the apply off until then with systemctl disable --now hmailserver-update.path. A server under a unit of another name needs --unit in a drop-in for hmailserver-update.service; an AppImage under the path unit needs --image there and 6.3.6's script copied to /usr/lib/hmailserver/. Every .rpm requires glibc's extra character-set converters where dnf is installed on Fedora or Enterprise Linux and recommends them elsewhere (Installing on Linux). A server from 6.2.28 to 6.3.3, or one installed from an AArch64 package or an AppImage, is upgraded by hand, once.
  • Behind a reverse proxy. The REST listener's plain loopback port answers only a request whose Host is localhost, an IP address, the server's host name or the host of the webmail's outside address, and refuses any other with 421: a proxy that forwards the browser's own Host needs that name set first. The proxy must pass the Control Deck's page through unchanged. A wrong administrator password costs a growing delay, up to ten seconds, from every source including the loopback address (APIs Reference, the listener).
  • Port 25 still takes AUTH on an upgraded server: every SMTP listener stays On, so nobody is banned on the morning of the upgrade; the setup checklist's new item No password on port 25 says so until you move clients that sign in on 25 to 587 and set port 25 to Honeypot or Off (Ports Reference). The URI blocklists are asked about every domain a message links to, up to 256 queries per active list where it was 16, and a message whose links name more than 200 domains scores SurblTooManyDomainsScore (5 by default) once; a list's error answer is never a listing (Stopping Spam §10.6). ManageSieve has bounds (Rules and Sieve §12.3). An IP address in text is read the same way on Windows as on Linux: a value kept in a form Windows used to take (192.168.1, 3232235777, 0x7f.1, a leading zero, an address with a port) is read the new way from the first start - an API key's source restriction so written makes the key refused from every address, a REST, web-services or SNMP bind address so written leaves that listener stopped, an AutoBanNeverBan entry so written protects nothing (HM6524), and DNSServer=8.8.8 is reported invalid (HM4401). What the auto-ban put below the server is lifted after a stop too (AutoBanCommand is told unban; the Linux hook is told reset).
  • The webmail's S/MIME certificates of correspondents are kept only when the chain is trusted and under the From address; those kept before the upgrade stay - review them on the webmail's Security page (The Webmail, Encryption for People).
  • After upgrading, an account's retention (Windows): from 6.2.25 to 6.3.5 an account saved through the object COM's Authenticate hands out - a webmail's password or out-of-office plugin - could have its MessageRetentionDays overwritten. Check the retention of the accounts whose holders use such a plugin (the account's page in the Control Panel, COM Account.MessageRetentionDays, REST message_retention_days).
  • A full restore keeps this installation's own self-signed pair when the archive's key is not the same (Backup and Restore); a full restore of messages that failed on an installation made new with 6.3.4 or 6.3.5 is made again from the same archive once 6.3.6 runs.
  • Smaller: the packaging Compose file runs PostgreSQL 18 (Running in a Container); a monitor that matches "The response carried no HTTP headers." needs the new sentence, which says who ended the connection, after how many bytes and seconds; tlds.txt is removed and the Public Suffix List compiled in decides, so a sender at a public suffix with no MX now fails the MX-record test.

18.4f Upgrading to 6.3.7

6.3.7 (17 October 2026, build 46, schema 6108; the tag v6.3.7, 31d656c0c) is the first security release since releases paused, cut from master with the finished work master held then (Security Policy, While releases pause). It fixes vulnerabilities in every earlier version: upgrade every server running 6.3.6 or earlier (Changes-Since-6210 4h has the list). It is signed with Sigstore at the group's path, as 6.3.6 was - https://gitlab.com/hmailserver/hmailserver//.gitlab-ci.yml@refs/tags/v6.3.7, issuer https://gitlab.com, with the project's id, 86730559, in the certificate - which every server from 6.3.4 on has compiled in, and the update is applied by the version already installed. In the order that matters:

  • Back up first; there is no schema step. Take the backup Upgrading Guide describes - the database and the data directory, each with its own tools. 6.3.7 requires schema 6108, as 6.3.6 does: an upgrade from 6.3.6 changes no table, and the live update's rollback to 6.3.6, should 6.3.7 not start, meets no newer schema. From 6.3.5 or older the upgrade runs 6.3.6's three one-way steps to 6108 (18.4e), and the backup is the only way back.
  • From 6.3.6 the live update installs 6.3.7, with 6.3.6's own file as its rollback image. A Windows service running as LocalSystem installs it itself; one running as NT SERVICE\hMailServer downloads and verifies it, and an administrator installs it with Install update on the Server status page of the Control Panel on the server - one Windows prompt, and the installer is checked again, elevated, before it runs. On Linux 6.3.6's own update helper applies it, for the generic .deb and .rpm of either architecture and both AppImages, each finding its own file in the feed by name; an ARM64 Windows server finds the ARM64 installer by name. A server installed 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: upgrade it with its package manager and its own release's package of 6.3.7 (Installing on Linux).
  • From 6.3.4 or 6.3.5, what 18.4e says still holds. A Windows service running as LocalSystem installs 6.3.7 by its live update - from 6.3.5 with 6.3.5's file as its rollback image, which is no way back once the schema steps have run, and from 6.3.4 without one. One running as NT SERVICE\hMailServer downloads and verifies it, then leaves the install to an administrator (HM7463), whose Install update on 6.3.4 and 6.3.5 cannot do it: install 6.3.7 by hand, once, as below, and every release after it with one click. On Linux install it with apt, dnf or zypper rather than by the live update, or turn the apply off until then with systemctl disable --now hmailserver-update.path.
  • A server whose UpdateSigningIdentity, UpdateSigningIssuer and UpdateSourceRepository still name the path the project began at trusts that path alone and refuses 6.3.7, as it refused 6.3.6. Clear the three first: on 6.3.4 and 6.3.5 as 18.7 says; on 6.3.6, which reads them from hMailServer.ini alone, by deleting their lines from [Settings] with an editor run as an administrator, or on Linux with sudo hmailserver --set-setting <name>=, and restarting the service (The Update Feed).
  • From 6.2.28 to 6.3.3, and from 6.3.3's AArch64 packages or AppImages: by hand, once (18.4c). Older than 6.2.28: run the installer, as always.
  • By hand on Windows: download hMailServer-6.3.7-x64.exe, check that its SHA-256 is dcae96bd75d6cae8e5a910d15940567067539ed99db5aeed40a815added4d85f, as the release's Download table gives it, and that Get-AuthenticodeSignature reports it Valid and signed by CN=Progressive Robot Ltd, and run it as an administrator, keeping the service account on the installer's Service account page. On a machine that has not seen the file before, SmartScreen may say that it "prevented an unrecognized app from starting": More info names the publisher, Progressive Robot Ltd, and with the signature checked, Run anyway installs it.
  • In a container, registry.gitlab.com/hmailserver/hmailserver:6.3.7, also tagged 6.3 and latest: the update check names the image to pull and installs nothing (Running in a Container).
  • What may need doing before the upgrade (the release notes' Before you upgrade; all thirty-three items are in Changes-Since-6210 4h):
    • an account that sends over authenticated SMTP as an address of another domain hosted here, with no alias or grant that lets it, now has that mail sent unsigned as that domain: give it the alias or the grant first;
    • a Sieve script stored before the upgrade still runs, without an edit of From, Sender, Resent-From, Resent-Sender, DKIM-Signature, Auto-Submitted or the loop count, and its notify goes from the account's own address where its :from does not reach the account; a domain's or an account's rule stored before is taken without a Set header value of one of the first five or of the loop count, and without a Reply from an address its owner may not send as. The application log names each one (Rules and Sieve);
    • RuleLoopLimit 0 now stops out-of-office replies, as it stopped every other generated message;
    • an account given the serveradmin role with a list of domains is, over COM as over REST, confined to those domains: give the role with no list to an account that is to administer the whole server, a script's or a Control Panel's account among them;
    • on Windows a program folder at a drive's root, its Bin and its ControlPanel keep no entry that lets another user change them, a file's own entry aside, which a CHECK line names; under Program Files the first run makes the three folders' inherited entries their own (The Service Account);
    • the MySQL and MariaDB client, MariaDB Connector/C 3.4.11, ships with seven authentication plugins where 6.3.6's had nine: a database account that signs in with MySQL Enterprise's Windows authentication can no longer sign in, and the server then cannot open its database;
    • SMTP, POP3 and ManageSieve refuse a SASL PLAIN sign-in that asks to act as another account; and with SRS on, mail to an SRS address this server handed out is held to the relay rules unless it is a bounce;
    • under Remove all attachments and deliver the text, a message that is one attachment arrives as the note alone, and one the server cannot read is deleted, its recipients told;
    • a forward, a redirect or a rule's forward of mail from outside whose From names the forwarding domain's own organisation, and that did not pass DMARC for it when it arrived, keeps the envelope sender it came with; a copy that would still carry an identity of that organisation is not sent to an outside address, and the application log names each recipient;
    • an event script that goes on in a transaction after Database.ExecuteSQL has failed is refused every statement after it once the database has ended the transaction, and Database.CommitTransaction then fails: call Database.RollbackTransaction and run the whole transaction again, or stop (The COM API and Scripting).
  • The .NET 10 Desktop Runtime. Both installers carry 10.0.13, Microsoft's security release of 13 October 2026, where those of 6.3.4 to 6.3.6 carried 10.0.12; setup installs it only on a machine that has no .NET 10 Desktop Runtime at all, so an upgraded server keeps the runtime it had. Bring it to 10.0.13 or later through Microsoft Update, or with Microsoft's installer (https://dotnet.microsoft.com/download/dotnet/10.0); dotnet --list-runtimes lists the releases a machine has (Upgrading Guide, The .NET 10 runtime).
  • What 6.3.7 does not fix: four narrower cases of the program folder's permissions stay open, none of which reaches an installation in a folder only the administrators may change unless a link another account controls is on its path. To have an installation at a drive's root hardened before the upgrade runs anything from it, follow The Service Account, Before upgrading an installation at a drive's root, first (Changes-Since-6210 4h).
  • The Control Panel's pages moved (item 32): Updates is a page of its own under Maintenance, API & monitoring is Monitoring & alerts, the sidebar has Compliance and Virus scanning groups, and Ctrl+K takes every old title and route to its page (Control Panel Reference).

18.5 Things that changed, and what to do about them

Coming from 5.x or early 6.x, the administration GUI is different.

The classic hMailServer Administrator (hMailAdmin.exe) was retired in 6.2 and its source removed in 6.2.10. It is replaced by the Control Panel (hMailCP.exe), which does everything the old tool did - domains, accounts, aliases, distribution lists, routes, rules, IP ranges, TCP/IP ports and SSL bindings, server settings, status, queue, logs, backup, certificates, scripts and public folders - plus a live dashboard, complete settings coverage and optional two-factor authentication.

  • The navigation tree deliberately mirrors the old Administrator's layout, so muscle memory mostly transfers.
  • Your TOTP secret carries over - the Control Panel uses the same one.
  • The Control Panel needs the .NET 10 Desktop Runtime, which the installer installs silently if missing.

Coming from before hMailServer 5, hMailServer.ini moves. It used to live in the Windows directory; the installer copies it to Bin\hMailServer.ini, leaves a .old copy behind and deletes the original. The server reads only the one in Bin.

The PHP WebAdmin is gone. It stored the administrator password in plaintext in a PHP session and needed DCOM opened up for the web server account. It was 2008-era unmaintained code. If you used it to administer the server remotely, use the Control Panel instead - it connects to a remote host directly, which is both simpler and safer.

"Administrative tools" means something narrower now. With both old front-ends gone, the component's only job is registering the COM type library so scripts on another machine can administer this server. If you do not script remotely, you do not need it.

The .NET runtime moved from 8 to 10 in the first release after 6.2.18. The installer bundles windowsdesktop-runtime-10.0-win-x64.exe and installs it /quiet /norestart, only when it is missing. Nothing about the server itself changed - it is native code with no .NET dependency - so an upgrade that fails to install the runtime still leaves you with a working mail server, just without the Control Panel and the setup tools.

18.6 If it goes wrong

The server does not paper over a half-finished upgrade. On startup it compares the database's version with the version it requires and refuses to run if they differ, in either direction, reporting HM5011 with both numbers appended:

Message in the ERROR log What it means What to do
The database is too old for this version of hMailServer. Please run hMailServer Database updater (DBUpdater.exe) to upgrade it. Database version: N, Required database version: M The schema did not get all the way up Run DBUpdater.exe from the installation's Bin folder. It picks up from whatever version the database actually reports, so a retry after a partial upgrade resumes rather than restarting. If it fails again, the log names the step it stopped on - that number is the useful thing to put in an issue
The database is too new for this version of hMailServer. Please upgrade hMailServer. Database version: N, Required database version: M You are running an older server against a newer schema - usually an old binary left in place, or a reinstall of a previous release Install the matching or newer server. Do not try to downgrade the database
Database version could not be detected. (HM5010) hm_dbversion could not be read at all Check the connection details in [Database] and that the login can read the table

DBSetupQuick.exe and DBUpdater.exe do the same job; the installer drives the first, and the error message names the second because that is the one you run by hand.

The installer's exit codes 10 and 11 (since 6.3.6). Setup ends with one of the two whenever a step after the files did not complete, and which one says whether the database is still the earlier version's: 10, the database is as the earlier version left it - a step before the database step failed, or the database step failed without moving the version - and the earlier version can be put back on it; 11, the database step moved the version, all the way or part-way, or its version could not be read - nothing is put back, and the outcome is upgraded-incomplete, reported at the next start as HM5481 (HM5480 is an update that did not succeed and changed nothing). The live update's helper rolls back on 10 only; on 11, finish the upgrade with DBSetupQuick.exe from Bin once its cause is put right, or restore the backup the update took and reinstall the earlier version. DBSetupQuick.exe itself exits 4 when it cannot reach the service through COM, having changed nothing. On Linux the helper holds to the same rule: it reads the database's version with hmailserver --schema-version (required <n>, then database <n>; exit 1 when the database's cannot be read, 2 with no [Database]) before it installs and again when the new version is not back, and puts the earlier version back only onto a database the step did not move (Upgrading Guide, If it goes wrong).

Rolling back

There is no downgrade path, and this is the one genuine sharp edge. The schema upgrade is one-way: an older server refuses to run against a newer dbversion rather than misinterpret it, which is correct - and means "just reinstall the old version" does not work on its own.

  1. Uninstall the version you just installed.
  2. Install your previous version.
  3. Restore the database from your backup. This is the essential step.
  4. Restore hMailServer.INI if you changed it.

Rolling back from 6.3.4 to 6.3.3 or earlier: the [Settings] section the newer server kept current is a complete copy of its stored settings, and an older server reads that file as the truth, so any setting changed after the upgrade survives the rollback through the file even though the restored database predates it - wherever the server could write that file, which a Linux package installation's unit does not allow (Installing on Linux, step 7), nor the file's permissions under NT SERVICE\hMailServer (The Service Account). The [SettingsStore] section the newer server added is ignored by older builds and can be deleted. A [SettingsOverride] section is ignored by older builds too, so a value only it was supplying is lost - move it into [Settings]. Where the 6.3.4 server had sealed the stored secrets (a stored-secrets key made, 18.4c), restore the pre-upgrade hMailServer.ini with the database: the newer server writes the [Settings] secrets into the file as HMK1: envelopes where it may write the file, which an older server reads as they are - its /Register would hand ServiceAccountPassword's envelope to Windows as the service's password, and the service would not start. Where you have no such copy, set those secrets again with the older version's Control Panel once it runs, and run hMailServer.exe /Register again (Upgrading.md).

The data directory does not normally need restoring - message files are not rewritten by an upgrade - but note the consequence: mail that arrived after the backup exists on disk with no metadata row, and will be invisible. That is what rollback costs, and it is why the cutover is worth doing in a quiet window.

This is why 18.3 step 1 is not optional. Without a database backup there is no rollback.

18.7 Staying up to date

  • Watch the project on GitLab to be notified of releases: the notification bell on https://gitlab.com/hmailserver/hmailserver, Custom, then New release.
  • Every release lists exactly what changed and why, and every release from 6.2.3 carries SPDX and CycloneDX SBOMs; the five earlier releases that carry an installer have a CycloneDX SBOM of it alone, made afterwards (Security Policy, Supply chain).
  • Security fixes are called out explicitly in the release notes.
  • Let the server check for you (new in 6.2.28). With UpdateCheckEnabled=1 in hMailServer.INI the server reads the project's release feed at startup and then every UpdateCheckHours (default 24), compares the newest release on UpdateChannel (stable, the default, or prerelease) with its own version, and reports a newer one in the application log, on the Control Panel's Updates page (Server status before 6.3.7), in Status.UpdateState / Status.AvailableVersion over COM and in GET /api/v1/update. The check is off by default; Status.CheckForUpdate and POST /api/v1/update/check run one on demand whether or not it is on. Every setting the feature reads has an editor on the Control Panel's Updates page under Maintenance from 6.3.7, and in 6.3.4 to 6.3.6 on the Updates card of its API & monitoring page. The feed. Up to 6.3.3 the feed a server read by default was GitHub's API, which has answered 404 since GitHub suspended the project's account on 18 September 2026, so such a server's check reports The update feed answered HTTP 404. until UpdateFeedUrl names https://updates.progressiverobot.com/releases.json (18.4c). Since 6.3.4 the default is https://updates.progressiverobot.com, which GitLab Pages serves from the project's releases (UpdateChecker.cpp's STABLE_FEED and PRERELEASE_FEED), and nothing needs setting.
  • What the live update installs. It verifies every installer against its Sigstore bundle and the signers compiled into the server. 6.3.4 carries no bundle, so no server's live update installs it (18.4c); the releases after it are signed by the project's GitLab CI, which a 6.3.4 server trusts beside the GitHub workflow that signed the releases from 6.2.19 to 6.3.3 - 6.3.5 at the path the project began at, and 6.3.6 onwards at the path of the group hmailserver, which the project moved to on 29 September 2026, where the certificate must carry the project's id. A server that still has the three GitLab values UpdateSigningIdentity, UpdateSigningIssuer and UpdateSourceRepository set to the signer of the project's first path, which let a server from 6.2.28 to 6.3.3 install a GitLab-signed release, trusts that path alone and refuses 6.3.6 and every release after it, 6.3.7 among them, which are signed at the group's path. Clear the three before the update - in the Control Panel, with DELETE /api/v1/settings/ini/<name>, or on Linux with hmailserver --set-setting <name>= - not in the file, whose edit 6.3.4 and 6.3.5 do not use (HM5804). Once 6.3.6 runs, the five trust keys are read from the file alone (18.4e), and the three are cleared by deleting their lines from it (18.4f). No setting relaxes the project's id for the group's path. Never point the three at the group's path on a server from 6.2.28 to 6.3.3, which checks no project id and would install whatever anyone who holds that path signs; once the project has moved, such a server is upgraded by hand (UpdateFeed.md, copied here as The Update Feed).
  • If this machine reaches the web through a forward proxy, set HttpProxy=<host>:<port> (or [ipv6]:<port>) in the [Settings] section of hMailServer.INI - empty, the default, means a direct connection. The update check, the installer download, and the JWKS and token-introspection fetches all go through it; an https target is reached with CONNECT and the TLS handshake runs inside the tunnel, so the certificate is verified exactly as it would be on a direct connection, and plain http is fetched by putting the absolute URL in the request line. A proxy that refuses is reported by name: The proxy <proxy> refused CONNECT to <host:port>: followed by the proxy's own status line. A value with no port is an error, and proxy credentials are not supported. The editor sits beside the feed URL on the Updates card.
  • Since 6.3.4, UpdateCheckEnabled, HttpProxy and the rest are stored in the database rather than read from hMailServer.INI: use the Updates card, PUT /api/v1/settings/ini/{name} or hmctl ini set. Since 23 September 2026 such a save applies when it is saved - the update check reads its hours, window and channel when it runs - and the field says so. A value typed into the file for a key the database already holds is reported as HM5804 at the next start and not used, its line put back where the service may write the file (under NT SERVICE\hMailServer, to which the file is read-only, the edit stays in the file - The Service Account).

For the mechanism behind all of this - what the chain does, why rollback is what it is, and how to move to a different machine or a different backend - see Upgrading Guide and Migrating the Database Backend.


Home — full index

Start here

1. Install and run

2. Secure it

3. Operate it

4. Extend it

5. Contribute to it

Look it up — from any journey


About this wiki

Clone this wiki locally