Skip to content

Porter porterd

The porterd extension adds porterd, the Porter assistant daemon, to a Frostyard bootc image. It runs as lodge’s Porter app, with the app id porter. A server that runs the suite’s services runs Floe. Porter’s own page is Porter. 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/porterd/.
  • Free space in /var for the image.

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

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

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

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

porterd 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.

The lab has not yet proven this for porterd: no lab run has enabled it live yet. If a unit fails, follow the start-limit-hit steps below, or reboot.

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 frostporter.socket frostporter.service
sudo systemctl start frostporter.socket frostporter.service

Check the units:

Terminal window
systemctl status frostporter.socket frostporter.service

systemctl status shows both units active.

If a unit shows failed with start-limit-hit, systemd stopped starting it. Run sudo systemctl reset-failed frostporter.socket frostporter.service, then sudo systemctl start frostporter.socket frostporter.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.

frostporter.socket and frostporter.service run on the host. The service is upheld because its routines run without a client.

The drop-in /usr/lib/systemd/system/multi-user.target.d/10-porterd.conf has Upholds= for both units, and the preset /usr/lib/systemd/system-preset/40-porterd.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.porterd sets EXTENSION_RELOAD_MANAGER=1, so a merge or refresh reloads systemd’s unit files.

frostporter.service runs /usr/bin/porterd as the user frostporter, with the groups lodge-apps and lodge-agents. It listens on no network, only on Unix sockets. Its state is in /var/lib/frostporter, mode 0700, and each app’s token is in /var/lib/frostporter/tokens.

Porter needs a token for each app it reaches. Issue one with sudo lodgectl token issue NAME --app APP --scope SCOPE --agent frostporter, and save it in a file. Set it as the user frostporter, then restart Porter: porterd reads the tokens only when it starts.

Terminal window
sudo -u frostporter porterd token set <app> < <token-file>
sudo systemctl restart frostporter.service

porterd prints that the app’s token is set.

The configuration belongs to you, and the extension ships no file in /etc: /etc/porter/porter.json.

frostporter.socket listens on /run/lodge/apps/porter.sock as owner frostporter, group lodge, mode 0660.

The sysusers file /usr/lib/sysusers.d/frostporter.conf creates the user frostporter and the group lodge, with frostporter in lodge-apps and lodge-agents. 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. Porter reaches the apps through lodge’s agent socket, so lodge’s configuration must name it in agents.

The command is /usr/bin/porterd, with no CLI. The lodge app files are in /usr/share/lodge/apps/porter/.

Back up Porter’s state before you update. The thread database has no schema version, and an older porterd opens a newer one, so a rollback needs no restore today. A later version can change this.

Stop Porter for the backup. Systemd starts upheld units again, so mask them while you copy. While Porter is stopped, lodge shows porter down and routines do not run. /var/lib/frostporter/tokens/ holds the app tokens, so keep the archive root-only:

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

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

Check for an update, then install it:

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

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 porterd 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 Porter, then let lodge read the new manifest:

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

Check the version that runs:

Terminal window
sudo lodgectl apps
journalctl -u frostporter.service -n 20

lodgectl apps lists porter up, with the new version. lodge polls each app every 15 seconds, so if porter shows down at once, run lodgectl apps again after 15 seconds. The journal since the start shows no warning and no error.

Roll back to the previous version:

Terminal window
sudo updex features rollback porterd

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

Restore the state only if journalctl -u frostporter.service shows a thread database error at start. These errors follow porterd: and begin with open thread database:, thread schema:, routines columns:, add routines.last_success:, fill routines.last_success:, or thread recovery:. A start can fail for other reasons, such as a bad /etc/porter/porter.json, and a restore does not help then. To restore, keep the current state aside and extract the backup. Replace ARCHIVE with the path of the archive:

Terminal window
sudo systemctl mask --now frostporter.socket frostporter.service
sudo mv /var/lib/frostporter /var/lib/frostporter.new
sudo tar -xzpf ARCHIVE -C / var/lib/frostporter
sudo systemctl unmask frostporter.socket frostporter.service
sudo systemctl start frostporter.socket frostporter.service

The restore loses the thread turns, Activity, approvals, routine history, and token changes made after the backup. It does not undo what Porter already did in other apps, such as drafts and notes.

Remove porterd:

Terminal window
sudo updex features disable porterd --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-porterd, 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 Porter (Porter assistant daemon and Lodge porter app)
Command /usr/bin/porterd, with no CLI
Service frostporter.service, which runs /usr/bin/porterd as the user frostporter
Socket frostporter.socket on /run/lodge/apps/porter.sock, owner frostporter, group lodge, mode 0660
State /var/lib/frostporter, with app tokens in /var/lib/frostporter/tokens
User and group frostporter, group lodge, frostporter in lodge-apps and lodge-agents, and the user lodge, from /usr/lib/sysusers.d/frostporter.conf
Lodge app /usr/share/lodge/apps/porter/
Activation /usr/lib/systemd/system/multi-user.target.d/10-porterd.conf with Upholds=, and /usr/lib/systemd/system-preset/40-porterd.preset
Version The image file name carries the frostyard-porterd package version, which is the app’s release version
Image file porterd_<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/porterd/, signature required (Verify=true), into /var/lib/extensions.d/

Each build checks the command, the units, 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; porter v0.1.1 commit d1c8631fd953dcbb25f17ad1004d1edd075dadb6; Updex v2.1.3 commit 2661e987faac986f2594d1d9bbbadf1bf3053f5b.