Skip to content

Frostmail maild

The maild extension adds maild, the Frostmail mail engine, to a Frostyard bootc image. It runs as lodge’s Mail app, with the app id mail. A server that runs the suite’s services runs Floe. Frostmail’s own app page is Frostmail. Every image carries the extension’s definition, switched off. The extension itself is not in the image: Updex turns it on and downloads it.

You need:

  • A Frostyard bootc image built on Base.
  • Root access to enable, update, and remove the extension.
  • Network access to https://repository.frostyard.org/ext/maild/.
  • Free space in /var for the image.

maild has no published test results on Frostyard images yet. Try it on a machine you can spare first.

You reach Mail in a browser through lodge. Enable lodge first. The maild extension declares no dependency on lodge, so enabling maild does not enable lodge.

See what Updex found, then enable the feature and download it now:

Terminal window
updex features list --component=maild
sudo updex features enable maild --now

maild appears as a component, with its feature disabled. The enable command downloads the image, checks its checksum and signature, links it, and refreshes the merge. With Updex v2.1.3 and later, the refresh also creates the users, groups, and directories that the extension declares, before systemd reloads. Upholds= then starts the units, with no reboot. updex --version shows your version.

If updex features list does not show the feature, the image is older than the extension. Update the image first.

With an Updex older than v2.1.3, the units cannot start after the enable: their users and groups do not exist yet. Reboot. At boot the image merges the extensions, runs systemd-sysusers and systemd-tmpfiles --create, reloads systemd, and Upholds= starts the units. To start them without a reboot:

Terminal window
sudo systemd-sysusers
sudo systemd-tmpfiles --create
sudo systemctl reset-failed frostmail.socket frostmail.service
sudo systemctl start frostmail.socket frostmail.service

Check the merge and the units:

Terminal window
systemd-sysext status
systemctl status frostmail.socket frostmail.service
sudo -u frostmail mailctl --socket /run/frostmail/maild.sock hello

systemd-sysext status lists maild, systemctl status shows both units active, and hello prints maild <version> (protocol N). hello exits non-zero when maild does not answer.

If a unit shows failed with start-limit-hit, systemd stopped starting it. Run sudo systemctl reset-failed frostmail.socket frostmail.service, then sudo systemctl start frostmail.socket frostmail.service, or reboot. Then run the checks again.

If enable --now exits with a refresh error (refresh_error in JSON), the download worked, but the activation did not. If the output says activation did not finish, the extension is merged, but its users and groups can be missing: reboot. Otherwise the merge state is unknown: reboot. A sudo systemd-sysext refresh by hand does not create the extension’s users and groups. Then run the checks above again.

frostmail.socket and frostmail.service run on the host. maild’s rules and Send Later run without a client, so the service is upheld too.

The drop-in /usr/lib/systemd/system/multi-user.target.d/10-maild.conf has Upholds= for both units, and the preset /usr/lib/systemd/system-preset/40-maild.preset enables both. Both start at boot. If one stops, even with exit status 0, systemd starts it again. To stop it for longer, remove the extension or mask the unit.

extension-release.maild sets EXTENSION_RELOAD_MANAGER=1, so a merge or refresh reloads systemd’s unit files.

frostmail.service runs /usr/bin/maild --lodge as the system user frostmail. Its data is in /var/lib/frostmail, its cache in /var/cache/frostmail, and mailctl reaches it on the private socket /run/frostmail/maild.sock.

frostmail.socket listens on /run/lodge/apps/mail.sock as owner frostmail, group lodge, mode 0660.

At first start, frostmail-secrets-key.service makes the key that seals maild’s secrets, /etc/credstore.encrypted/frostmail-secrets-key, with systemd-creds encrypt --with-key=auto. It skips when the key exists, and never replaces a key. frostmail.service pulls it with Requires= and After=. The unit is not upheld and not in the preset. maild refuses a missing or wrong key. The key is in /etc; the extension never ships it.

The sysusers file /usr/lib/sysusers.d/frostmail.conf creates the user frostmail and the groups lodge and lodge-apps, with frostmail in lodge-apps. The user is never in group lodge. The file also creates the user lodge, with the same line as lodge’s own sysusers file. The daemon looks up the user lodge when it starts, so it starts without lodge’s extension.

The files are /usr/bin/maild, /usr/bin/mailctl, /usr/libexec/frostmail/make-secrets-key, and frostmail-secrets-key.service. The lodge app files are in /usr/share/lodge/apps/mail/: app.json and web/.

Back up maild’s state before you update. maild migrates its database when it starts, and an older maild refuses a database with a newer schema. A rollback does not undo a migration.

Stop maild for the backup. Systemd starts upheld units again, so mask them while you copy. While maild is stopped, the Mail tab does not work, rules and Send Later do not run, and lodge shows the app as down. The archive holds the mail database, the messages, and the sealed account secrets, so keep it root-only:

Terminal window
sudo systemctl mask --now frostmail.socket frostmail.service
sudo sh -c 'umask 077; tar -czpf /root/frostmail-state-$(date -u +%Y%m%dT%H%MZ).tar.gz /var/lib/frostmail'
sudo systemctl unmask frostmail.socket frostmail.service
sudo systemctl start frostmail.socket frostmail.service

tar exits 0, and systemctl status frostmail.socket frostmail.service shows both units active again.

The key that opens the sealed secrets, /etc/credstore.encrypted/frostmail-secrets-key, is not in the archive. It is bound to the host, so a restore of the archive works on this host only.

Check for an update, then install it:

Terminal window
updex features check --component=maild
sudo updex features update --component=maild

By default, the update merges the new files at once, and systemd reloads its unit files. Updex v2.1.3 and later also create new users, groups, and directories at once. An older Updex leaves them for the next boot. The running maild keeps its old binary until it restarts. A reboot applies everything.

The new files wait for the next boot when you pass --no-refresh, when the automatic update timer installs the update, or when the output says refresh skipped: Updex skips the refresh when it cannot refresh safely. Then reboot. See Update.

With an Updex older than v2.1.3, run these commands first. updex --version shows your version.

Terminal window
sudo systemd-sysusers
sudo systemd-tmpfiles --create
sudo systemctl daemon-reload

To run the new version now, restart maild, then let lodge read the new manifest:

Terminal window
sudo systemctl restart frostmail.socket frostmail.service
sudo lodgectl reload

Check the version that runs:

Terminal window
sudo -u frostmail mailctl --socket /run/frostmail/maild.sock hello
sudo lodgectl apps

hello prints maild <version> (protocol N) with the new version. lodgectl apps lists mail up, with the new version. lodge polls each app every 15 seconds, so if mail shows down at once, run lodgectl apps again after 15 seconds.

Roll back to the previous version:

Terminal window
sudo updex features rollback maild

The rollback takes effect at the next boot. To apply it now, add --now, then restart maild as above. See Roll back.

When the older version runs, read the journal:

Terminal window
journalctl -u frostmail.service -n 50

maild 0.3.0 migrates the database of 0.2.0, so a rollback from 0.3.0 to 0.2.0 needs a restore. If the journal shows database schema N is newer than this maild, restore the state from your backup. Replace ARCHIVE with the path of the archive:

Terminal window
sudo systemctl mask --now frostmail.socket frostmail.service
sudo rm -rf /var/lib/frostmail
sudo tar -xzpf ARCHIVE -C / var/lib/frostmail
sudo systemctl unmask frostmail.socket frostmail.service
sudo systemctl start frostmail.socket frostmail.service

The restore loses what changed after the backup.

Remove maild:

Terminal window
sudo updex features disable maild --now --force

Updex unmerges all extensions on the host, deletes the image, and merges the rest again. Reboot when the command finishes. Without --force, Updex refuses while the image is merged.

Item What you get
Package frostyard-maild, the .deb attached to the app’s GitHub release, installed into the image with dpkg. The build checks the download against a pinned SHA-256, and checks that its dependencies are met before it installs. Its GitHub build-provenance attestation is checked when the pin is set and in every CI build, before the image is built
Feature Mail (Frostmail mail engine and Lodge mail app)
Commands /usr/bin/maild and /usr/bin/mailctl
Service frostmail.service, which runs /usr/bin/maild --lodge as the system user frostmail
Socket frostmail.socket on /run/lodge/apps/mail.sock, owner frostmail, group lodge, mode 0660
Private socket /run/frostmail/maild.sock, for mailctl
Data and cache /var/lib/frostmail and /var/cache/frostmail
Secrets key frostmail-secrets-key.service and /usr/libexec/frostmail/make-secrets-key, which make /etc/credstore.encrypted/frostmail-secrets-key at first start
User and group frostmail, groups lodge and lodge-apps, frostmail in lodge-apps, and the user lodge, from /usr/lib/sysusers.d/frostmail.conf
Lodge app /usr/share/lodge/apps/mail/, with app.json and web/
Activation /usr/lib/systemd/system/multi-user.target.d/10-maild.conf with Upholds=, and /usr/lib/systemd/system-preset/40-maild.preset
Version The image file name carries the frostyard-maild package version, which is the app’s release version
Image file maild_<version>_13_x86-64.raw.zst, where 13 is the OS version ID; .raw.xz, .raw.gz, and .raw also work
Download From https://repository.frostyard.org/ext/maild/, signature required (Verify=true), into /var/lib/extensions.d/

Each build checks the commands, the units, the secrets-key helper /usr/libexec/frostmail/make-secrets-key, the sysusers file, the lodge app manifest and web page, and the preset, activation drop-in and extension-release files. A build that is missing one fails. Each build also checks that every upheld unit has no Condition*= line, or is a oneshot that stays active.

Reviewed 2026-10-29. snosi commit e423112908540aa3a8045fa9d8022a3742e3cd17; core commit cd2720e0c55d08f3ba8f28a3b55dd5796a5c425a; Frostmail v0.3.0 commit 89243ae6753d6da11947c6fd7023c84ad0ce59d7; Updex v2.1.3 commit 2661e987faac986f2594d1d9bbbadf1bf3053f5b.