SysadminNews

systemd 262 Learns Live Update Orchestrator Sessions

On this page
  1. systemd can now hold a live update session
  2. Two settings that solve real production problems
  3. A PID 1 that fits in a very small container
  4. Smaller items worth knowing
  5. The breaking changes, in the order they will bite you
  6. Sources and further reading

systemd 262-rc1 landed on September 1, 2026, and the change worth reading first is LUOSession=. Service units can now ask systemd to create a Live Update Orchestrator session, the kernel mechanism merged in Linux 6.19 that carries file descriptors across a kexec so a workload survives a kernel swap. systemd holds the session in the file descriptor store, which is exactly where it belongs. Around that sit two smaller items that solve problems most of us have hit in production: RestartRandomizedDelaySec= to spread restart storms, and ActivatingConcurrencyMax= to stop a slice from starting everything at once.

The short answer

systemd 262-rc1 wires the init system into the kernel Live Update Orchestrator. A service unit can set LUOSession= and systemd creates the LUO session, then passes it through the file descriptor store, so a workload can keep its preserved descriptors across a kexec based kernel swap. The manager also exposes KExecsCount and shutdown timestamps over D-Bus and Varlink, and systemd-analyze time reports live update timing. Elsewhere in the release, service units gain RestartRandomizedDelaySec= to add jitter to automatic restarts, slice units gain ActivatingConcurrencyMax= to throttle simultaneous activations, and systemd can now be built as a single statically linked PID 1 for very small containers.

262-rc1released September 1, 2026
LUOSession=new service unit setting
6.19kernel release where LUO landed
Answer card explaining LUOSession= in systemd 262-rc1: a service unit asks systemd to create a kernel Live Update Orchestrator session, which is kept in the file descriptor store so the workload survives a kexec.
The file descriptor store was already the right place for this. Now it holds LUO sessions too. PNG

There is a particular kind of release note that tells you where the platform is going, and this is one of them. systemd 262-rc1, tagged on September 1, 2026, contains a lot of the usual: new unit settings, deprecations, option renames. Underneath it, one line connects the init system to a kernel feature that changes what a kernel upgrade costs.

systemd can now hold a live update session

The kernel side of this shipped in Linux 6.19. The Live Update Orchestrator lets specific stateful file descriptors survive a kexec. A driver registers a handler for its file type, the handler saves the file state before the transition and restores it afterwards, and the descriptor comes out the other side pointing at the same thing. Memory backed descriptors are the headline case: a memfd holding guest RAM stays in RAM across the kernel replacement rather than being torn down and rebuilt.

The framework groups preserved resources into named sessions, and each session is itself a file descriptor obtained through /dev/liveupdate. That design is deliberate. Everything inside a session is tied to the lifetime of the session descriptor, so if whatever is holding it crashes or exits, the kernel reclaims the lot instead of leaking preserved state. Until now, holding that descriptor was the job of a dedicated userspace agent.

systemd 262 adds LUOSession= to service units. Set it and systemd creates the session and passes it to the service through the file descriptor store, the same mechanism systemd already uses to hand descriptors back to a service after a restart. That is a good match. The file descriptor store exists precisely to be the thing that outlives the process it belongs to, supervised by PID 1, which is the one process on the system with a plausible claim to still be around on the other side of a kexec.

The manager also grew observability for this. On kernels that support LUO, systemd exposes KExecsCount plus current and previous shutdown timestamps over both D-Bus and Varlink, and systemd-analyze time reads them to report kexec and live update timing. If you are going to update kernels without rebooting, you need a number that tells you how long the workload was actually paused, and now there is one.

Two settings that solve real production problems

The rest of the release is smaller, but two service and slice settings are the kind of thing you adopt the week you read about them.

RestartRandomizedDelaySec= adds a uniformly distributed extra delay to automatic restarts, layered on top of RestartSec= and any exponential backoff already configured. The failure mode it addresses is familiar to anyone who has watched a database blip take out a fleet: every dependent service fails within the same second, waits exactly the same interval, and retries in unison. The dependency comes back, absorbs the entire fleet at once, and dies again. Jitter is the standard fix and it now costs one line in a unit file.

ActivatingConcurrencyMax= goes on slice units and caps how many units in that slice hierarchy may be in the activating state simultaneously. Everything above the cap is queued and started automatically as slots free. If you have ever watched a boot where thirty services all decided to warm their caches at the same moment and every one of them crept toward its start timeout, this is the knob.

Alongside it, the manager gained a D-Bus method EnqueueUnitJobMany() that queues start, stop, restart or reload jobs for several units in a single transaction. That matters because it lets ordering dependencies between the named units be resolved properly rather than being decided by the order you happened to type them. systemctl and portablectl use it when the manager supports it and fall back to per unit calls otherwise. Our notes on systemd-oomd and zram defaults in Fedora CoreOS cover another case where a systemd default quietly changed what a node does under pressure.

Checklist of systemd 262-rc1 changes: LUOSession and kexec timing, embedded fallback unit files, a statically linked PID 1 for small containers, RestartRandomizedDelaySec, ActivatingConcurrencyMax, EnqueueUnitJobMany, NUMAPolicy additions, and the notify-reload and journalctl breaking changes.
What to read before the upgrade, in the order you are likely to be bitten. PNG

A PID 1 that fits in a very small container

Two changes make systemd viable in places it was awkward before.

The manager now embeds a basic set of unit files, basic.target, sysinit.target, multi-user.target, reboot.target, shutdown.target, systemd-poweroff.service and friends, and falls back to them when it cannot load anything from disk. Files and masks found on disk still win. The practical consequence is that a container can boot with systemd as PID 1 without any unit files installed at all, and a host with a damaged /usr has a better chance of reaching a state where you can fix it.

Separately, systemd can now be built as a single statically linked PID 1 and executor binary, by configuring Meson with --default-library=static --prefer-static -Dbuild-static=true -Dsystemd-multicall-binary=true. Such builds skip dlopen() for optional libraries entirely and use simplified passwd and group file lookups instead of NSS. That is a meaningfully different deployment target: an init that is one file with no runtime library discovery.

Note that the Meson options changed shape here. -Dbuild-executor-shared=single is replaced by -Dsystemd-multicall-binary=true, and -Dstandalone-binaries= now takes a comma separated list of patterns naming which binaries get a .standalone variant instead of a boolean. Old values are translated but deprecated, and existing build directories may need to be recreated.

Smaller items worth knowing

NUMAPolicy= accepts two new values. preferred-many needs Linux 5.15 or newer, and weighted-interleave needs Linux 6.9 or newer and takes its weights from the kernel weighted_interleave sysfs files. If you have been tuning NUMA placement by hand for memory bandwidth bound services, weighted interleave is now expressible in the unit.

ConditionCPUFeature= understands arm64 features reported through ELF hwcaps, and feature names can be written as ARCH.FEATURE, for example arm64.bti, to disambiguate on mixed architecture deployments.

systemd-coredump speaks the kernel coredump socket protocol introduced in Linux 6.17. systemd-firstboot accepts systemd.firstboot=headless to suppress every interactive prompt, which is the missing piece for genuinely unattended first boots. systemd-vmspawn extends --coco= to Intel TDX alongside AMD SEV-SNP. New FSCRYPT backed home directories default to FSCRYPT v2 policies. Socket units accept IEC suffixes on MessageQueueMessageSize=.

systemd-repart stopped forcibly disabling copy on write when it creates image files. There is a new --cow= option taking yes, no or auto, defaulting to auto, which leaves the filesystem or parent directory policy alone. If you were relying on the old NOCOW behaviour on Btrfs, pass --cow=no explicitly.

The breaking changes, in the order they will bite you

Type=notify-reload. Services of this type must now catch or block ReloadSignal= at the point they send READY=1. If they do neither, the service fails to start with a protocol error. Previously they were allowed to start, and a later reload could invoke the default action for the signal and terminate the service. The new behaviour is correct, but it turns a latent bug into a startup failure.

journalctl field listing. -F and --field are now rejected when combined with a unit, boot, time, cursor or grep filter. The old implementation used sd_journal_query_unique(), which cannot express those filters, so it ignored them silently and returned field values from the whole journal. Any script that looked like it was listing fields for one unit was giving you something else, and now it says so.

UnsetEnvironment=. It is applied after environment variable expansion of ExecStart= and related lines. Variables listed there can still be referenced in the command line while staying out of the executed process environment. Units that depended on whole word $VAR references being dropped need updating.

Rate limit clocks. sd-event rate limit timers, and therefore manager timers like StartLimitIntervalSec=, moved from CLOCK_MONOTONIC to CLOCK_BOOTTIME. Time spent suspended now counts toward expiry. After an upgrade or reexecution, serialized rate limit state from an older manager may expire once earlier than it would have.

udevd control socket. The legacy socket is gone and udevadm uses Varlink IPC unconditionally. /run/udev/control is now a symlink to the Varlink socket, kept only so that existing checks for udevd being alive still find something bound at that path. Do not read that as the legacy protocol still working there.

systemd-sysupdate. The units are renamed to systemd-sysupdate-update.service and systemd-sysupdate-update.timer, with compatibility symlinks, clearing the way for a systemd-sysupdate@.service for Varlink activation. The binary itself moved back from bindir to libexecdir and is marked experimental again, with more breaking changes expected.

Removed Meson options. -Dlibiptc=, -Dlibidn=, -Drc-local=, -Dsysvinit-path= and -Dsysvrcnd-path= are gone.

This is a release candidate, so the final 262 may still shift. The direction will not. PID 1 is becoming the thing that holds the state your workload needs in order to survive a kernel being replaced underneath it.

Sources and further reading

Frequently asked questions

What does LUOSession= actually do in a unit file?

It tells systemd to create a Live Update Orchestrator session on behalf of the service and hand it over through the file descriptor store interface. LUO is the kernel framework merged in Linux 6.19 that lets specific stateful file descriptors, memfd and vfio and iommufd among them, survive a kexec based kernel replacement. The session is represented by a file descriptor and the lifetime of everything preserved inside it is tied to that descriptor, so the kernel cleans up automatically if the holder disappears. Previously a userspace agent had to own that descriptor and keep it alive across the transition. Now systemd can be the thing that holds it, which is a much better fit, because the file descriptor store is already the mechanism systemd uses to carry descriptors across a service restart.

Who needs this, realistically?

Anyone running long lived workloads on hosts that need kernel updates more often than they can tolerate a full reboot. The canonical case is a hypervisor with virtual machines whose guest RAM lives in a memfd. With LUO the guest memory stays in place across the kexec and the machines come back on the new kernel without a migration. The same shape applies to any service holding a large in memory dataset that is expensive to rebuild. If you reboot fleets on a maintenance window and nobody notices, you do not need this. If a reboot means draining and refilling a node for twenty minutes, this is the direction the stack is moving.

What is RestartRandomizedDelaySec= good for?

It adds a uniformly distributed extra delay on top of RestartSec= and any exponential backoff when a unit restarts automatically. The problem it solves is synchronization. When a shared dependency fails, every service that depends on it fails at roughly the same instant, then every one of them waits exactly RestartSec= and retries at roughly the same instant. The dependency comes back, gets hit by the whole fleet simultaneously, and falls over again. Randomizing the delay breaks that lockstep. It is the same reasoning behind jitter in any retry policy, now available as one line in a unit file instead of something you build into each service.

What does ActivatingConcurrencyMax= change?

It caps how many units inside a slice hierarchy may sit in the activating state at the same time. Anything over the cap is queued and started automatically as slots free up. This is a throttle for boot and for bulk starts. If you have a slice full of services that each hammer the disk or a database during startup, starting them all at once makes every one of them slower and can push the whole group past its start timeouts. Setting a concurrency limit turns that stampede into a queue. It is worth pairing with the new EnqueueUnitJobMany() method, which lets systemctl submit start or stop jobs for many units as a single transaction so ordering dependencies are honoured regardless of the order you named them on the command line.

What should I check before upgrading?

Four items. Services of Type=notify-reload must now catch or block ReloadSignal= by the time they send READY=1, or they fail to start with a protocol error, so audit anything using that type. Scripts calling journalctl -F or --field alongside a unit, boot, time, cursor or grep filter will now be rejected instead of silently ignoring the filter. UnsetEnvironment= is applied after expansion of ExecStart=, so variables you unset can still be referenced in the command line, which is usually what you wanted but changes behaviour for units that relied on the old coupling. And rate limit timers such as StartLimitIntervalSec= moved from CLOCK_MONOTONIC to CLOCK_BOOTTIME, meaning suspended time now counts toward expiry.