<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[In a container or on Kubernetes: the image, Compose, Helm and the environment]]></title><description><![CDATA[<p dir="auto"><em>Written at 6.3.4, when no image was published. hMailServer 6.3.6 (6 October 2026) publishes the first: <code>registry.gitlab.com/hmailserver/hmailserver:6.3.6</code>, also tagged <code>6.3</code> and <code>latest</code>, 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 <a href="/topic/28276">the 6.3.6 announcement</a> and <code>ContainerImage.md</code> at the v6.3.6 tag. hMailServer 6.3.7 (17 October 2026), a security release, is <code>registry.gitlab.com/hmailserver/hmailserver:6.3.7</code>, 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 <a href="/topic/28277">the 6.3.7 announcement</a>. hMailServer 6.3.8 (2 November 2026), a security release, is <code>registry.gitlab.com/hmailserver/hmailserver:6.3.8</code>, and <code>6.3</code> and <code>latest</code> now name it; the schema stays at 6108. Its image carries MariaDB Connector/C, so <code>HM_DB_TYPE=MySQL</code> 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 <code>--create-database</code> 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 <code>database.port</code> to the type's own port. Move a release of the older chart with <code>helm repo update</code>, <code>helm upgrade &lt;release&gt; hmailserver/hmailserver --reset-then-reuse-values</code> (Helm 3.14 or later) and then <code>kubectl delete pod &lt;release&gt;-hmailserver-0</code>, since the StatefulSet never replaces the stuck pod by itself (<code>--reuse-values</code> would keep the old chart's <code>fsGroup</code> 1000 and <code>database.port</code> 5432); where a cluster's policy refuses every sysctl, set <code>unprivilegedPortsSysctl: false</code>. For a Compose deployment made from an earlier Compose file, keep its <code>HM_REST_CERTIFICATE_FILE</code>, <code>HM_REST_PRIVATE_KEY_FILE</code> and <code>./certs</code> mount in the new file, the key readable by 999, or its REST API does not start. See <a href="/topic/28278">the 6.3.8 announcement</a>.</em></p>
<p dir="auto">The Linux server is also a container image: the x86-64 <code>.deb</code> installed on Ubuntu 24.04 and run as the <code>hmailserver</code> 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 <code>HM_*</code> variables the entrypoint reads. The one thing to know first, from the 6.3.4 changelog: <strong>no container image of 6.3.4 is published.</strong> The images published on <code>ghcr.io/progressiverobot/hmailserver</code> before GitHub's suspension answer 404, and <code>registry.gitlab.com/progressiverobot/hmailserver</code>, which the Compose file and the chart now name, holds nothing. Meanwhile, build the image from the release's <code>.deb</code> and use your own tag wherever the files say <code>registry.gitlab.com/...</code>. What a Linux server is and is not is in <a href="/topic/28189">hMailServer on Linux</a>.</p>
<h3><a class="anchor-offset" name="building-the-image-yourself"></a>Building the image yourself</h3>
<p dir="auto">Use the <code>docker</code> directory of the same tag as the package: 6.3.4's entrypoint runs verbs a 6.3.3 binary does not have.</p>
<pre><code class="language-sh">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 .
</code></pre>
<p dir="auto">The Dockerfile copies <code>packages/hmailserver_*_amd64.deb</code> (the <code>HM_DEB</code> 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.</p>
<h3><a class="anchor-offset" name="what-is-in-the-image-and-what-is-not"></a>What is in the image, and what is not</h3>
<p dir="auto"><strong>In:</strong> <code>/usr/bin/hmailserver</code>, the packaged <code>hMailServer.ini</code> at <code>/etc/hmailserver/</code>, the database scripts, the Control Deck, the fail2ban filter and jail the package carries, <code>ca-certificates</code> for the TLS the server does as a client, and an entrypoint. About 40 MB. The process runs as <code>hmailserver</code>; ports below 1024 are bound through <code>cap_net_bind_service</code> on the binary, not through root. Since 6.3.4 the Dockerfile installs the package's two Recommends, <code>7zip</code> and <code>python3</code>: without them a backup failed with "Could not add the backup settings to the archive", and <code>hmctl</code> had no Python 3.</p>
<p dir="auto"><strong>Not in: the database</strong>, 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: <code>curl</code>, an editor, or a shell of any consequence beyond <code>bash</code>. There is no Control Panel and no COM; the REST API is the administration.</p>
<h3><a class="anchor-offset" name="the-variables-the-entrypoint-reads-itself"></a>The variables the entrypoint reads itself</h3>
<p dir="auto">Sixteen of these are older than 6.3.4 and keep their names and meanings.</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Variable</th>
<th>Writes</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>HM_DB_TYPE</code></td>
<td><code>[Database] Type</code></td>
<td><code>PostgreSQL</code> or <code>MySQL</code> (MariaDB is <code>MySQL</code>). Setting this is what makes the section be written at all</td>
</tr>
<tr>
<td><code>HM_DB_HOST</code>, <code>HM_DB_PORT</code></td>
<td><code>Server</code>, <code>Port</code></td>
<td>Defaults <code>db</code>, and the type's port (5432 or 3306)</td>
</tr>
<tr>
<td><code>HM_DB_NAME</code>, <code>HM_DB_USER</code>, <code>HM_DB_PASSWORD</code></td>
<td><code>Database</code>, <code>Username</code>, <code>Password</code></td>
<td>Defaults <code>hmailserver</code>, <code>hmailserver</code>, empty. The user needs the right to create the database on first start</td>
</tr>
<tr>
<td><code>HM_DB_TLS_MODE</code>, <code>HM_DB_TLS_CA</code></td>
<td><code>TlsMode</code>, <code>TlsCaFile</code></td>
<td>Since 6.3.4. <code>disable</code>, <code>prefer</code> (empty, the default), <code>require</code> or <code>verify-full</code>, and the path of a PEM file of CA certificates mounted into the container</td>
</tr>
<tr>
<td><code>HM_ADMIN_PASSWORD</code></td>
<td><code>[Security] AdministratorPassword</code></td>
<td>Through the server's own <code>--set-admin-password</code>: the file holds a PBKDF2 hash, never the password. Without one the REST API refuses to start</td>
</tr>
<tr>
<td><code>HM_REST_PORT</code></td>
<td><code>RestApiPort</code></td>
<td>The packaged file has 0, which is off; 8045 is the conventional port</td>
</tr>
<tr>
<td><code>HM_REST_BIND_ADDRESS</code></td>
<td><code>RestApiBindAddress</code></td>
<td>The packaged file has <code>127.0.0.1</code>, which inside a container is the container's own loopback</td>
</tr>
<tr>
<td><code>HM_REST_CERTIFICATE_FILE</code>, <code>HM_REST_PRIVATE_KEY_FILE</code></td>
<td><code>RestApiCertificateFile</code>, <code>RestApiPrivateKeyFile</code></td>
<td>Paths inside the container, readable by the <code>hmailserver</code> user. Paths the server reads, not <code>_FILE</code> forms</td>
</tr>
<tr>
<td><code>HM_DB_PASSWORD_FILE</code>, <code>HM_ADMIN_PASSWORD_FILE</code></td>
<td>the same two secrets</td>
<td>Read from a file: a Docker or Compose secret. Setting both a variable and its <code>_FILE</code> is refused</td>
</tr>
<tr>
<td><code>HM_CREATE_DATABASE</code>, <code>HM_UPGRADE_DATABASE</code></td>
<td>nothing</td>
<td><code>0</code> skips <code>--create-database</code> or <code>--upgrade-database</code> at start. Both are no-ops when there is nothing to do</td>
</tr>
<tr>
<td><code>HM_INI</code></td>
<td>which file is written</td>
<td>Default <code>/etc/hmailserver/hMailServer.ini</code></td>
</tr>
<tr>
<td><code>HM_PROFILE</code>, <code>HM_PROFILE_CONFIRM</code></td>
<td>nothing</td>
<td>Since 6.3.4: the setting profile applied once, and its confirmation</td>
</tr>
</tbody>
</table>
<p dir="auto"><strong>On 6.3.3 and earlier</strong> the entrypoint writes these into <code>hMailServer.ini</code> on every start, and every other setting is the packaged file in the <code>/etc/hmailserver</code> volume. <strong>Since 6.3.4</strong> the database is the settings store, so the four <code>HM_REST_*</code> variables are aliases of <code>HM_SETTING_RestApiPort</code> and its three neighbours, stored like any other setting; <code>[Database]</code> is still written into the file at every start, because it says where the database is. The image's <code>/etc/hmailserver</code> is owned by the <code>hmailserver</code> user, so there, unlike on a package installation, the server keeps its copy of each stored value in the file, and a <code>--create-database</code> that creates the schema also makes the stored-secrets key.</p>
<h3><a class="anchor-offset" name="every-setting-from-the-environment-since-6.3.4"></a>Every setting from the environment (since 6.3.4)</h3>
<p dir="auto">After <code>--create-database</code> and <code>--upgrade-database</code>, and before the server, the entrypoint runs the server's own verb, <code>hmailserver --apply-environment</code>, whenever there is anything for it to do. <code>HM_SETTING_&lt;key&gt;</code> sets a <code>[Settings]</code> key and <code>HM_PROPERTY_&lt;name&gt;</code> a database setting (<code>HM_SETTING_MaxSubmissionsPerIPPerMinute=30</code>, <code>HM_PROPERTY_maxmessagesize=20480</code>), and either with <code>_FILE</code> reads the value from a file, a Compose secret or a Kubernetes Secret mounted as one.</p>
<ul>
<li><strong>The environment wins, at every start.</strong> 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.</li>
<li><strong>Everything is checked before anything is stored.</strong> When anything is refused, nothing is stored and <strong>the container does not start</strong>; <code>docker compose logs</code> or <code>kubectl logs</code> says which variable and why.</li>
</ul>
<p dir="auto">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 <a href="/topic/28231">configuration from the environment</a>.</p>
<p dir="auto"><strong><code>HM_PROFILE</code></strong> names one of the four setting profiles, <code>office</code>, <code>gateway</code>, <code>relay</code> or <code>sender</code>, which the entrypoint applies once, with <code>--apply-profile NAME --once</code>, after the environment's settings and before the server starts. <code>HM_PROFILE=none</code> 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 <code>HM_PROFILE_CONFIRM=yes</code>, and without it the container does not start. A profile skips a setting the environment manages, naming its variable.</p>
<p dir="auto"><strong>Not yet run in a container.</strong> The verb has been run as <code>hMailServer.exe --apply-environment</code> 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. <code>HM_PROFILE</code> is in the same state. Try it on a scratch database first.</p>
<h3><a class="anchor-offset" name="reaching-the-api"></a>Reaching the API</h3>
<p dir="auto">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:</p>
<ul>
<li><strong>Bind it and give it a certificate.</strong> <code>HM_REST_BIND_ADDRESS=0.0.0.0</code>, with <code>HM_REST_CERTIFICATE_FILE</code> and <code>HM_REST_PRIVATE_KEY_FILE</code> pointing at a certificate and key mounted into the container, and the port published. Publish it on the host's loopback (<code>127.0.0.1:8045:8045</code>) unless the certificate is one clients will trust.</li>
<li><strong>Host networking</strong> (<code>--network host</code>). The container's <code>127.0.0.1</code> is the host's, the packaged bind address is right as it is, no certificate is needed, and the API is at <code>http://127.0.0.1:8045</code> from the host and through an SSH tunnel from elsewhere. The mail ports are then the host's ports directly.</li>
</ul>
<h3><a class="anchor-offset" name="first-start-with-compose"></a>First start, with Compose</h3>
<p dir="auto">The file to use is in the repository, at <code>hmailserver/source/Server/platform/packaging/docker/docker-compose.yml</code>. It reads the two passwords as Compose secrets from files, mounts the REST certificate and key in, and carries the <code>HM_SETTING_</code> and <code>HM_PROPERTY_</code> examples commented out. A workflow resolved it with <code>docker compose config</code> on every change until 18 September 2026, so it is known to parse. Below is the <code>mail</code> service of the wiki's shorter example, which puts it under <code>services:</code> beside a <code>postgres:16</code> service named <code>db</code> and declares the named volumes. It names the image built above, not the empty registry:</p>
<pre><code class="language-yaml">  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
</code></pre>
<p dir="auto"><code>restart: unless-stopped</code> matters on the first start: <code>depends_on</code> starts the database container first but does not wait for it to accept connections, so the entrypoint's <code>--create-database</code> fails and the container exits if the database is not there yet; the restart tries again a moment later. The first start logs the <code>[Database]</code> section written, the administrator password hashed and the database created at schema 6104, and also what <code>--create-database</code> did about the stored-secrets key and one line per <code>HM_SETTING_</code> or <code>HM_PROPERTY_</code> variable stored.</p>
<p dir="auto">The first domain is then one request away (<a href="/topic/28190">First install on Linux</a>). Passwords in a Compose file are passwords in a file: for anything beyond a first look, use <code>HM_DB_PASSWORD_FILE</code> and <code>HM_ADMIN_PASSWORD_FILE</code> with Compose secrets.</p>
<p dir="auto"><strong>The secure defaults of a new installation are not applied in a container by themselves.</strong> On a package installation the systemd unit's first start gives a database <code>--create-database</code> made a self-signed certificate, STARTTLS and TLS required for authentication on the <em>Internet</em> IP range. The image has no unit, so that step is run by hand, as the service's user, with the server stopped. <a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/SecureDefaults.md" rel="nofollow ugc">SecureDefaults.md</a> gives the verb and no container command for it, and lists its refusal of a running service (exit 4) for Windows only:</p>
<pre><code class="language-sh">hmailserver --apply-new-install-defaults
</code></pre>
<p dir="auto">A run by hand has no one-day deadline, so an image first booted days after its <code>--create-database</code> is secured this way. What it changes, its exit codes and the checklist item that says what is left are in <a href="/topic/28241">Hardening a public mail server</a>.</p>
<h3><a class="anchor-offset" name="kubernetes-with-the-chart"></a>Kubernetes, with the chart</h3>
<p dir="auto">Since 6.3.4 the chart is at <code>hmailserver/source/Server/platform/packaging/helm/hmailserver</code>. It is not published to a chart repository; install it from a checkout. It renders a <strong>StatefulSet of one</strong>, 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 <code>database.host</code>. Its <code>image.repository</code> names the empty registry, so set it to the image you built, pushed to a registry the cluster's nodes can pull from:</p>
<pre><code>helm install mail ./hmailserver \
  --set image.repository=&lt;your registry&gt;/hmailserver \
  --set database.host=postgres.databases.svc.cluster.local \
  --set database.password=... \
  --set administrator.password=... \
  --set rest.tlsSecretName=mail-tls
</code></pre>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Value</th>
<th>What it is</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>image.repository</code></td>
<td><code>registry.gitlab.com/progressiverobot/hmailserver</code> as shipped, which holds no image: set it to where you pushed the image you built</td>
</tr>
<tr>
<td><code>database.host</code>, <code>.type</code>, <code>.name</code>, <code>.user</code></td>
<td>The PostgreSQL or MariaDB the server uses; required. <code>database.create</code> and <code>database.upgrade</code> make the schema on first start and move it forward on an upgrade</td>
</tr>
<tr>
<td><code>database.password</code>, <code>administrator.password</code></td>
<td>Go into a Secret the chart makes. Point <code>existingSecret</code> at a Secret you made and the chart writes none; its two keys are named by <code>existingSecretKeys</code></td>
</tr>
<tr>
<td><code>rest.enabled</code>, <code>.port</code>, <code>.tlsSecretName</code></td>
<td>The one listener that serves the REST API, the Control Deck and the webmail. Off loopback the server requires TLS, so without a <code>kubernetes.io/tls</code> Secret named here the listener will not start; the chart's notes say so on install</td>
</tr>
<tr>
<td><code>settings</code>, <code>properties</code>, <code>settingsFrom</code></td>
<td>Since 6.3.4: any <code>[Settings]</code> key (rendered as <code>HM_SETTING_&lt;key&gt;</code>), any database setting (<code>HM_PROPERTY_&lt;name&gt;</code>), and a key of a Secret you made, projected into <code>/run/secrets/hmailserver-settings/&lt;n&gt;</code> and named through <code>_FILE</code>, so a secret value is in no environment variable, rendered manifest or log line</td>
</tr>
<tr>
<td><code>service.type</code>, <code>.ports</code>, <code>.externalTrafficPolicy</code></td>
<td>A LoadBalancer with the mail ports, and the REST port when the listener is on. <code>Local</code> keeps the client's address, which auto-ban, greylisting and the <code>Received</code> header all need</td>
</tr>
<tr>
<td><code>persistence.*</code></td>
<td>The message store, the logs and the configuration, one claim each</td>
</tr>
<tr>
<td>Probes</td>
<td>Port 25, as the image's own health check is, or the metrics listener's <code>/livez</code> and <code>/readyz</code> when that listener is on</td>
</tr>
</tbody>
</table>
<p dir="auto"><code>settings</code>, <code>properties</code> and <code>settingsFrom</code> look like this in a values file; a <code>settingsFrom</code> entry says <code>setting:</code> in place of <code>property:</code> for a <code>[Settings]</code> key:</p>
<pre><code class="language-yaml">settings:
  MaxSubmissionsPerIPPerMinute: "30"
properties:
  maxmessagesize: "20480"
settingsFrom:
  - secretName: mail-relay
    key: password
    property: smtprelayerpassword
</code></pre>
<p dir="auto">A switch is <code>1</code> or <code>0</code> for a <code>[Settings]</code> key; <code>true</code>, <code>false</code>, yes, no, on and off are taken as well for a setting whose REST settings row is a switch, and only there: <code>maxmessagesize=off</code> is refused rather than stored as 0, which is "no limit".</p>
<h3><a class="anchor-offset" name="volumes-ports-and-the-health-check"></a>Volumes, ports and the health check</h3>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Path</th>
<th>What lives there</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/var/lib/hmailserver</code></td>
<td>The mail store, and <code>.hmailserver-secret-key</code>, 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</td>
</tr>
<tr>
<td><code>/var/log/hmailserver</code></td>
<td>The logs. Bind-mount it to a host path if fail2ban on the host is to read it</td>
</tr>
<tr>
<td><code>/etc/hmailserver</code></td>
<td><code>hMailServer.ini</code>; since 6.3.4 the only home of <code>PasswordPepper</code> and <code>[Security] StoredSecretsKey</code>, which no database and no backup holds</td>
</tr>
</tbody>
</table>
<p dir="auto">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.</p>
<h3><a class="anchor-offset" name="upgrades-backups-and-auto-ban"></a>Upgrades, backups and auto-ban</h3>
<p dir="auto"><strong>Upgrading</strong> 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 <code>--upgrade-database</code> 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 <code>utf8mb4</code>, which takes time on a large database and needs the database server version that <a href="/topic/28247">Databases on Linux</a> gives. An upgraded database gets no stored-secrets key until one is made with <code>hmailserver --create-stored-secrets-key</code>, and never the new installation's defaults. Take the backup first (<a href="/topic/28193">Upgrading to 6.3.4</a>).</p>
<p dir="auto"><strong>Backups</strong> are three things together: the database, the <code>/var/lib/hmailserver</code> volume, and <code>/etc/hmailserver</code>. Two things are only in <code>/etc/hmailserver/hMailServer.ini</code>: a <code>PasswordPepper</code>, 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 <code>[Settings]</code> secret. Export the key once, and keep the file with the database backups, apart from them:</p>
<pre><code class="language-sh">docker exec &lt;container&gt; hmailserver --export-stored-secrets-key /var/lib/hmailserver/stored-secrets.key
docker cp &lt;container&gt;:/var/lib/hmailserver/stored-secrets.key ./stored-secrets.key
</code></pre>
<p dir="auto">A replacement container takes it back with <code>--import-stored-secrets-key FILE</code>.</p>
<p dir="auto"><strong>Auto-ban</strong> 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 <code>/var/log/hmailserver</code>, copy the filter and jail out of the image, point the jail's <code>logpath</code> at the mounted directory, and enable it.</p>
<h3><a class="anchor-offset" name="what-was-proven-and-what-has-not-been"></a>What was proven, and what has not been</h3>
<p dir="auto">Until 18 September 2026 the <code>image</code> 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 <code>linux-image</code> job is written to build the image from the <code>.deb</code> with kaniko and inspect it; it neither pushes nor runs the image, so the smoke test has no replacement yet. The <code>chart-and-compose</code> job is written to lint and render the chart and resolve the Compose file. Neither had run when <a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/ContinuousIntegration.md" rel="nofollow ugc">ContinuousIntegration.md</a> 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.</p>
<h3><a class="anchor-offset" name="where-to-read-more"></a>Where to read more</h3>
<ul>
<li><a href="https://gitlab.com/hmailserver/hmailserver/-/wikis/Running-in-a-Container" rel="nofollow ugc">Running in a Container</a> and <a href="https://gitlab.com/hmailserver/hmailserver/-/wikis/Profiles-and-Environment" rel="nofollow ugc">Profiles and Environment</a> on the wiki.</li>
<li><a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/EnvironmentConfiguration.md" rel="nofollow ugc">EnvironmentConfiguration.md</a> for the naming rule and every refusal, <a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/Profiles.md" rel="nofollow ugc">Profiles.md</a> for every entry of the four profiles, <a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/SecureDefaults.md" rel="nofollow ugc">SecureDefaults.md</a> for the verb run by hand, and <a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/DatabaseTls.md" rel="nofollow ugc">DatabaseTls.md</a> for <code>HM_DB_TLS_MODE</code>.</li>
<li>The 6.3.4 section of <a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/CHANGELOG.md" rel="nofollow ugc">CHANGELOG.md</a> for the state of the image.</li>
<li>On this forum: <a href="/topic/28218">the 6.3.4 announcement</a>, <a href="/topic/28189">hMailServer on Linux</a>, <a href="/topic/28190">First install on Linux</a> and <a href="/topic/28193">Upgrading to 6.3.4</a>; <a href="/topic/28231">configuration from the environment</a> for <code>HM_SETTING_</code> and <code>HM_PROPERTY_</code> in full, and <a href="/topic/28245">the hmailserver command line on Linux</a> for the verbs the entrypoint runs.</li>
</ul>
]]></description><link>https://www.hmailserver.co.uk/topic/28246/in-a-container-or-on-kubernetes-the-image-compose-helm-and-the-environment</link><generator>RSS for Node</generator><lastBuildDate>Fri, 06 Nov 2026 09:05:51 GMT</lastBuildDate><atom:link href="https://www.hmailserver.co.uk/topic/28246.rss" rel="self" type="application/rss+xml"/><pubDate>Sun, 27 Sep 2026 19:28:13 GMT</pubDate><ttl>60</ttl></channel></rss>