systemd started as an init system experiment announced on 30 April 2010 and, as of 25 October 2026, its newest release is v262 (22 September 2026), with v263 in development. Every release is a single integer. Most sandboxing directives arrived between v209 and v250, and the version your distribution ships decides which ones you can actually use.
That last point is the reason for this post. A hardening line copied from a blog or a vendor unit file is only as good as the systemd that reads it, and the systemd you run is your distribution's build, not upstream's tip. Every version number and date below comes from the project's own NEWS file, the "Added in version" notes in the official man pages, the project's release and security documents, or the distributions' own release notes.
This is a history and reference post. If you want to know how to write a hardened unit, read our starter guide to systemd service sandboxing directives; for the scoring tool, read how to read the systemd-analyze security exposure score; for dynamic users, what DynamicUser really implies; and for the boot chain, unified kernel images, systemd-boot and measured Linux boot.
Key takeaways
- Lennart Poettering announced systemd in the "Rethinking PID 1" post on 30 April 2010, crediting the design to himself and Kay Sievers. The v1 tag in the project's repository is dated 7 July 2010.
- Fedora 15 was the first major distribution to make it the default (2011). RHEL 7 followed in June 2014, Ubuntu 15.04 in April 2015 and Debian 8 in April 2015.
- Versions are plain integers. Release candidates use a tilde in the source tree (
256~rc1) and a hyphen in the git tag (v256-rc1). Point releases such as 262.1 come from per-release stable branches. - Since v256 the stable branches live in the main repository; older ones live in the separate systemd-stable repository.
- Cadence has varied: ten releases a year in 2014 and 2015, four a year from 2016 to 2019, two or three a year since. 2026 has had three so far (v260, v261, v262).
- The core namespace and seccomp directives arrived in three waves: v187 to v214 (seccomp filters, private devices, read-only system), v229 to v235 (DynamicUser, kernel protections, namespace restrictions) and v238 to v250 (hostname, clock, kernel logs, proc, IPC, filesystem and interface restrictions). Newer releases added PrivatePIDs (v257) and BPF delegation controls (v258).
systemd-analyze securityarrived in v240 (December 2018). Service credentials arrived in v247, encrypted and TPM2 bound credentials in v250.- The project asks for private reports through GitHub security advisories or a distribution-facing mailing list and coordinates disclosure with distributions. Distributions then backport fixes, so a Debian 12 system can carry a v252 based package with many later fixes inside.
How did systemd begin?
"Rethinking PID 1" (30 April 2010)
The founding document is a long blog post by Lennart Poettering titled "Rethinking PID 1", posted on 30 April 2010. Its one sentence summary was that the authors were "experimenting with a new init system". The post names the people behind the design as Lennart Poettering (Red Hat) and Kay Sievers (Novell).
The argument was about boot speed and process supervision. Process identifier 1 is the first userspace process the kernel starts and the parent of last resort for every orphan. On Linux that job had long belonged to sysvinit, and the post noted that Upstart had by then found its way into the major distributions. The proposal was to start less, start it in parallel, and use socket and bus activation so that services could start in any order and still find each other. The post also argued for tracking processes with Linux control groups rather than PID files, which turned out to matter for security as much as for reliability: a service cannot escape supervision by double forking if its whole cgroup is tracked.
The repository's v1 tag carries a date of 7 July 2010. The NEWS file, the project's changelog, only begins at v38, released on 11 January 2012, which it describes as the "First release with the journal". Anything before v38 has to be read from the git history rather than from release notes.
How the scope grew
systemd.io now describes the project as "a suite of basic building blocks for a Linux system": a system and service manager running as PID 1, plus a logging daemon, utilities for basic system configuration such as hostname, date and locale, user login management, network configuration, time synchronisation and more. That breadth did not exist in 2010. It grew by absorbing or creating components one release at a time, and the most consequential merges are datable:
- v38 (January 2012): the journal, replacing two older helper daemons.
- v183 (May 2012): udev's sources were merged into the systemd tree. NEWS for v183 says all future udev development would happen there, while udev could still run without systemd.
- v209 (February 2014): systemd-networkd.
- v213 (May 2014): a minimal systemd-resolved.
- v220 (May 2015): gummiboot was merged and renamed systemd-boot, alongside an EFI kernel stub.
The full component timeline is further down.
When did the major distributions switch?
Distribution adoption turned systemd from a Fedora project into the default Linux service manager. Each date below comes from the distribution's own release notes or announcement.
| Distribution | First release with systemd as default | Date | What the release material says |
|---|---|---|---|
| Fedora | Fedora 15 | 2011 | The Fedora feature page states that Fedora 15 replaced Upstart with systemd |
| Red Hat Enterprise Linux | RHEL 7 | 10 June 2014 (announcement) | Red Hat's launch press release lists systemd among the new infrastructure components |
| Ubuntu | 15.04 "Vivid Vervet" | 23 April 2015 | The release announcement calls the switch from Upstart to systemd one of the larger changes of the cycle |
| Debian | Debian 8 "jessie" | 25 April 2015 | The release notes: systemd, introduced in Debian 7, "is now the default init system" |
Two details from those sources are worth keeping. Debian shipped systemd as an option in Debian 7 before making it the default in Debian 8, and its release notes from 2015 spend a whole section on upgrade issues such as stricter handling of failing mounts and locally modified init scripts that needed porting. Ubuntu made the switch in an interim release rather than a long term support one.
How are systemd releases numbered and shipped?
One integer per release
systemd does not use semantic versioning. Each feature release gets the next integer: 255, 256, 257 and so on. There is no major or minor split, and the number says nothing about compatibility on its own. Incompatible changes are announced in NEWS, usually under a heading such as "Feature Removals and Incompatible Changes", and often one or two releases ahead in an "Announcements of Future Feature Removals" section.
Release candidates and the tilde
The project's release checklist (docs/RELEASE.md in the repository) spells out the mechanics. During development the version in meson.version reads like 256~devel; release candidates become 256~rc1, 256~rc2 and so on; the final release is plain 256. The document explains that a tilde sorts lower than nothing at all under the UAPI.10 version format specification, so 255~rc1 correctly sorts before 255. Git does not accept tildes in tag names, so the tag for that candidate is v255-rc1.
The checklist also shows what a release involves beyond tagging: updating NEWS and the contributors list, the hardware database and the syscall tables, building the man pages, and announcing to the systemd-devel mailing list with text copied from NEWS. Each NEWS section ends with a place and a date, which is why NEWS is a reliable source for release dates.
Stable branches and point releases
Point releases such as v262.1 come from stable branches. The checklist's final steps create an empty v<version>-stable branch at release time. Fixes are then backported "at least" from every pull request tagged needs-stable-backport, using git cherry-pick -x so each backport records the original commit.
Where those branches live changed in 2024. The release document says: since 256 the stable branches live in the main systemd repository, and stable branches for older releases are in the separate systemd-stable repository. As of 25 October 2026 the main repository has stable branches for v256 through v262, and its releases page lists recent point releases including v262.1, v261.4, v260.6, v259.10 and v258.12. From v256 onward the point version is written into meson.version (for example 256.3), so systemctl --version reports it directly.
How often has systemd released?
Counting release dates in NEWS gives this picture.
| Period | Releases | Range | Pattern |
|---|---|---|---|
| 2014 | 10 | v209 to v218 | Rapid releases, often weeks apart |
| 2015 | 10 | v219 to v228 | Rapid releases; v223 and v224 two days apart |
| 2016 to 2019 | 4 per year | v229 to v244 | Roughly quarterly |
| 2020 to 2021 | 3 per year | v245 to v250 | Roughly every four months |
| 2022 | 2 | v251, v252 | v252 shipped on 31 October 2022 |
| 2023 | 3 | v253 to v255 | |
| 2024 | 2 | v256, v257 | June and December |
| 2025 | 2 | v258, v259 | v258 took nine months, v259 three |
| 2026 to date | 3 | v260 to v262 | March, June and September |
The practical lesson is that "the current systemd" is a moving target, often three releases ahead of what a long term distribution ships. Plan around your distribution's version, not upstream's.
Which version added each component?
This is the component timeline. Each entry is checked against the NEWS section for that version; dates are the NEWS sign-off dates (for v38 to v189, which have no dated sign-off, the git tag date).
| Version | Date | Component or change | Security relevance |
|---|---|---|---|
| v1 | July 2010 (tag) | First tagged release | Cgroup based process tracking from the start |
| v38 | 11 Jan 2012 | First release with the journal | Structured, metadata rich logging that services cannot forge as easily as plain syslog lines |
| v183 | 25 May 2012 | udev merged into the systemd tree | Device handling and its rules became part of the same release train |
| v189 | 23 Aug 2012 | Forward Secure Sealing for journal files | Tampering with sealed log history becomes detectable |
| v209 | 20 Feb 2014 | systemd-networkd | Network configuration in a small daemon that later ran as its own user |
| v213 | 28 May 2014 | Minimal systemd-resolved | NEWS already stated the goal of a local DNSSEC enabled resolver |
| v216 | 19 Aug 2014 | networkctl; resolved gains a caching stub resolver | |
| v220 | 22 May 2015 | gummiboot merged as systemd-boot; EFI kernel stub | Kernel, initrd and command line in one signable EFI binary |
| v229 | 11 Feb 2016 | resolved becomes a DNSSEC validating stub resolver | Validation of DNS answers on the host |
| v239 | 22 Jun 2018 | resolved gains DNS over TLS (opportunistic) | Encrypted DNS to upstream servers |
| v240 | 21 Dec 2018 | systemd-analyze security | First built-in exposure score for service sandboxing |
| v243 | 3 Sep 2019 | Strict DNS over TLS mode | Fails closed instead of falling back to plain DNS |
| v245 | 6 Mar 2020 | systemd-homed | Encrypted, portable home directories |
| v247 | 26 Nov 2020 | Service credentials (LoadCredential, SetCredential) | Secrets passed to services without environment variables |
| v248 | 30 Mar 2021 | systemd-cryptenroll; systemd-sysext | TPM2, FIDO2 and PKCS#11 unlocking for LUKS; system extension images that overlay /usr |
| v250 | 23 Dec 2021 | Encrypted and authenticated credentials, systemd-creds | Secrets at rest bound to the host's TPM2 and/or a key on /var |
| v251 | 21 May 2022 | systemd-sysupdate | Image based A/B updates |
| v252 | 31 Oct 2022 | systemd-measure | Signed expected TPM2 PCR values for a unified kernel image |
| v253 | 15 Feb 2023 | ukify | Build, measure and sign unified kernel images |
| v254 | 28 Jul 2023 | Soft reboot; ImportCredential | Userspace only restart; easier credential import |
| v255 | 6 Dec 2023 | systemd-pcrlock (experimental) | Local TPM2 PCR policies for firmware measurements |
| v256 | 11 Jun 2024 | run0 | Privilege elevation without a setuid binary |
| v257 | 10 Dec 2024 | PrivatePIDs | Per-service PID namespace |
| v258 | 17 Sep 2025 | cgroup v1 support removed; System V state control removed; minimum kernel 5.4 | Smaller legacy surface; older kernels unsupported |
| v259 | 17 Dec 2025 | Journal default storage becomes persistent | Logs survive reboot by default |
| v260 | 17 Mar 2026 | System V service script support removed | Native unit files required |
| v261 | 19 Jun 2026 | Feature release | |
| v262 | 22 Sep 2026 | Current release as of this writing |
Two of those rows deserve more detail because they changed how people run systems, not just what they can configure.
The journal and Forward Secure Sealing. v38 brought the journal; v189 added Forward Secure Sealing. NEWS for v189 describes it as cryptographic sealing of journal files "so that attackers cannot alter log history anymore without this being detectable". The journald.conf man page explains the mechanism: it is based on Seekable Sequential Key Generators and is enabled for persistent journal files when a sealing key exists, which you create with journalctl --setup-keys. Sealing detects alteration; it does not stop deletion and it does not ship logs anywhere. That is a property of the design, not a weakness of any one version.
The boot chain. v220's EFI stub could already combine kernel, initrd, splash, command line and OS release information into one binary signed as a single image. v252's systemd-measure calculates and signs the TPM2 PCR values that such a unified kernel image should produce. v253's ukify builds, measures and signs those images, replacing earlier tooling. v255 added systemd-pcrlock, marked experimental in NEWS, for the harder problem of predicting firmware PCR values. Our UKI explainer covers how these parts fit together at boot.
Sandboxing directives by version
This is the reference most hardening guides leave out. The table lists the version that added each directive, taken from the "Added in version" note in the current systemd.exec and systemd.resource-control man pages, with the NEWS wording where it adds something. A few directives (PrivateTmp, PrivateNetwork, CapabilityBoundingSet, User and Group) carry no version note in the man page because they predate the oldest annotated release; NEWS shows them in use before v183.
| Directive | Added in | What it does (summarised from the man page) |
|---|---|---|
| PrivateTmp | before v183 | Private /tmp and /var/tmp; v183 NEWS extended it to /var/tmp |
| PrivateNetwork | before v183 | New network namespace with only loopback |
| CapabilityBoundingSet | before v183 | Limits the capabilities a service can ever hold |
| NoNewPrivileges | v187 | Process and children can never gain privileges (for example via setuid) |
| SystemCallFilter | v187 | seccomp allow or deny list of system calls |
| PrivateDevices | v209 | Minimal /dev with API nodes such as /dev/null, no physical devices |
| SystemCallArchitectures | v209 | Blocks system calls from secondary architectures |
| RestrictAddressFamilies | v211 | Limits socket address families (AF_INET, AF_UNIX and so on) |
| ProtectSystem, ProtectHome | v214 | Read-only /usr and /boot (and more with higher levels); hidden or read-only home directories |
| AmbientCapabilities | v229 | Grants specific capabilities to a service that runs as an unprivileged user |
| MemoryDenyWriteExecute | v231 | No memory mappings both writable and executable |
| RestrictRealtime | v231 | No realtime scheduling |
| ReadWritePaths, ReadOnlyPaths, InaccessiblePaths | v231 | Path based access in the service's mount namespace (replacing the older ReadWriteDirectories family) |
| DynamicUser | v232 | Transient user and group allocated for the service's lifetime |
| ProtectKernelTunables | v232 | /proc and /sys tunables read-only |
| ProtectKernelModules | v232 | No module loading or unloading |
| ProtectControlGroups | v232 | /sys/fs/cgroup read-only |
| PrivateUsers | v232 | User namespace mapping only the service's user and root |
| RemoveIPC | v232 | Removes the service user's System V and POSIX IPC objects on stop |
| RestrictNamespaces | v233 | Limits which namespace types the service can create |
| LockPersonality | v235 | Locks the execution domain (personality) |
| KeyringMode | v235 | Controls the kernel session keyring the service gets |
| IPAddressAllow, IPAddressDeny | v235 | Per-unit IP allow and deny lists enforced with BPF (systemd.resource-control) |
| TemporaryFileSystem | v238 | tmpfs over chosen paths |
| PrivateMounts | v239 | Private mount namespace without other restrictions |
| RestrictSUIDSGID | v242 | No creation of setuid or setgid files |
| ProtectHostname | v242 | No hostname changes |
| ProtectKernelLogs | v244 | No access to the kernel log buffer |
| ProtectClock | v245 | No changes to the system or hardware clock |
| ProtectProc, ProcSubset | v247 | hidepid and subset mount options for the service's /proc |
| SystemCallLog | v247 | Logs listed system calls instead of blocking them |
| LoadCredential, SetCredential | v247 | Pass secrets as files in a credentials directory |
| PrivateIPC | v248 | Private IPC namespace |
| SocketBindAllow, SocketBindDeny | v249 | Restrict which ports and families the service may bind (systemd.resource-control) |
| RestrictFileSystems | v250 | Deny access by filesystem type using the BPF LSM |
| RestrictNetworkInterfaces | v250 | Allow or deny named network interfaces (systemd.resource-control) |
| LoadCredentialEncrypted, SetCredentialEncrypted | v250 | Credentials encrypted at rest, decrypted at service start |
| ImportCredential | v254 | Import credentials by glob from the system credential store |
| PrivatePIDs | v257 | New PID namespace; the service is PID 1 inside it |
| PrivateBPF, BPF delegation settings | v258 | Private BPF filesystem with controlled delegation |
| DelegateNamespaces | v258 | Which namespace types the service may create and own |
| UserNamespacePath | v259 | Join an existing user namespace |
Wave one: v187 to v214, the first real sandbox
The early releases gave service units seccomp and a handful of namespace switches. NEWS for v187 introduced system call filtering "based on SECCOMP Mode 2 of Linux 3.5", and the man page dates NoNewPrivileges to the same release. v209 added PrivateDevices, described in NEWS as a namespaced /dev "that does not contain any device nodes for physical devices", plus SystemCallArchitectures to switch off secondary architectures. v211 added RestrictAddressFamilies, and v214 added ProtectSystem and ProtectHome, which NEWS called "very lightweight per-service sandboxing".
By the end of this wave a careful administrator could already give a network daemon its own /tmp, no physical devices, no setuid escalation and a read-only /usr. The distributions that switched in 2014 and 2015 shipped versions from this era.
Wave two: v229 to v235, kernel protections and dynamic users
The second wave tightened what a compromised service could do to the kernel and to the rest of the host.
- v231 (July 2016) added MemoryDenyWriteExecute and RestrictRealtime. NEWS said the first makes it "harder to dynamically write and then execute memory in exploited service processes", and that systemd's own long-running services had it enabled.
- v232 (November 2016) is the busiest single release for sandboxing: ProtectKernelTunables, ProtectKernelModules, ProtectControlGroups, PrivateUsers, RemoveIPC and DynamicUser. NEWS describes DynamicUser allocating user and group IDs from the range 61184 to 65519 for the service's lifetime, with PrivateTmp and RemoveIPC switched on.
- v233 (March 2017) added RestrictNamespaces. NEWS tied it to "the high amount of recently discovered namespacing" related kernel issues.
- v235 (October 2017) added LockPersonality, KeyringMode and per-unit IP filtering through IPAddressAllow and IPAddressDeny. It also added StateDirectory, CacheDirectory, LogsDirectory and ConfigurationDirectory, which NEWS says are "particularly useful in combination with DynamicUser=yes".
A note on DynamicUser's version, because it is often misquoted. The man page and NEWS both put DynamicUser itself in v232. v235 is the release that made it practical for stateful services, by adding the managed directories. v242 then changed its semantics: NEWS for v242 says NoNewPrivileges and the new RestrictSUIDSGID are now implied when DynamicUser is on, and calls that "a minor compatibility breakage". Our DynamicUser post covers what it implies in practice.
Wave three: v238 to v250, closing the remaining doors
The third wave filled gaps one resource at a time. TemporaryFileSystem (v238) and PrivateMounts (v239) gave finer mount control. v242 added ProtectHostname and RestrictSUIDSGID. v244 added ProtectKernelLogs, which NEWS says removes the syslog() system call, /proc/kmsg, /dev/kmsg and CAP_SYSLOG from the service. v245 added ProtectClock. v247 added ProtectProc and ProcSubset, which NEWS calls "an important new sandboxing option that is recommended to be set on all system services". v248 added PrivateIPC, v249 SocketBindAllow and SocketBindDeny, and v250 RestrictFileSystems and RestrictNetworkInterfaces. RestrictFileSystems is notable because NEWS says it is built on the BPF LSM of the Linux kernel, which makes it depend on kernel configuration as well as on the systemd version.
This wave also brought the measuring tool. v240 added systemd-analyze security, which NEWS describes as analyzing services "in order to determine an 'exposure level' for them". The man page explains that it estimates exposure on a 0.0 to 10.0 scale from systemd's own settings only, and warns that a high number "neither means that there is no effective sandboxing applied by the service code itself, nor that the service is actually vulnerable". v250 added --security-policy= so you can supply your own weighting. Our exposure score guide explains how to read the output.
Wave four: v254 to today, namespaces and BPF
Recent releases have extended the model rather than filled gaps. ImportCredential (v254) simplified credential delivery. PrivatePIDs (v257) gives a service its own PID namespace in which it runs as PID 1 and sees only its own processes in /proc; the man page notes it is supported for service units only and not with Type=forking. v258 added PrivateBPF and a family of BPF delegation settings, plus DelegateNamespaces. v259 added UserNamespacePath. None of these are drop-in for old services; they are building blocks for running untrusted or semi-trusted workloads under the service manager.
Which components carry security features worth knowing?
systemd-resolved: DNSSEC, DNS over TLS and the stub listener
resolved has three settings that matter for security, and each has a history.
- DNSSEC. NEWS for v229 announced resolved as "a DNSSEC validating stub resolver", off by default at the time and to be tested with
DNSSEC=allow-downgrade. The current resolved.conf man page lists the values as a boolean orallow-downgrade, documents the downgrade risk ofallow-downgradeplainly, and records the setting as added in v229. Distributions choose the compiled default, so check what yours uses rather than assuming. - DNS over TLS. Added in v239 in opportunistic mode, with NEWS saying it was "still turned off by default". v243 added a strict mode. The current man page says that with the setting true every connection to the server is encrypted and lookups fail if the server cannot do TLS, while
opportunisticcan be downgraded and cannot authenticate the server. The documented default isno. - The stub listener. The man page documents a stub resolver on 127.0.0.53 with the full feature set and a second one on 127.0.0.54 in proxy only mode, both on port 53. Extra listen addresses can be added with
DNSStubListenerExtra=. Those extra addresses are where exposure mistakes come from (see Common mistakes).
systemd-journald: sealing and storage defaults
Forward Secure Sealing has been in journald since v189 and is on by default when a sealing key exists. The storage default changed much later. NEWS for v259 says "The default storage mode for the journal is now 'persistent'"; before that the default was auto, which meant logs were only kept across reboots if /var/log/journal existed. The current journald.conf man page also notes that journald starts in volatile storage at boot and switches to persistent storage when systemd-journal-flush.service runs. A distribution can still change the compiled default, so an older or customised system may keep journals in memory only.
Credentials and the TPM
Service credentials arrived in v247 with SetCredential (a literal value) and LoadCredential (the contents of a file or a socket). NEWS for v250 extended the design with encrypted and authenticated credentials, using a key on /var, the TPM2 chip, or both ("by default if a TPM2 chip exists the combination is used"). The new systemd-creds tool encrypts them, and LoadCredentialEncrypted and SetCredentialEncrypted deliver them, decrypted, at the moment the service starts. NEWS gives SSL certificates and passwords as the intended use. The systemd-creds man page lists the settings that consume credentials, including ImportCredential from v254.
Credentials matter for supply chain reasons too: they keep secrets out of unit files that get copied between machines, committed to configuration repositories or baked into images.
homed, cryptenroll and run0
v245's systemd-homed manages home directories "with built-in encryption", primarily on LUKS volumes. v248's systemd-cryptenroll enrols TPM2, FIDO2 and PKCS#11 tokens, recovery keys and passphrases into LUKS2 volumes. v256's run0 is the newest of the three: the man page describes it as serving a similar purpose to sudo while inheriting no execution or security context from the caller, authenticating through polkit, allocating its own pseudo terminal and using no setuid or setgid bits. NEWS for v256 adds that the command runs as a transient unit started by the service manager.
How does systemd handle security reports and backports?
Reporting
The project's security policy (docs/SECURITY.md, also shown as the repository's security policy on GitHub) asks for non-public disclosure through a new GitHub security advisory, which it marks as preferred, or through the systemd-security mailing list. It says disclosure "will be coordinated with distributions", and that the advisory and mailing list audience is regular contributors and members of distribution security teams. The policy also lists what a valid report needs: reproducible on the main branch, a full end to end reproducer that is real world rather than simulated, and a demonstrated security boundary violation. It is candid that most maintainers are volunteers and that no fixed timeline is guaranteed, and it asks reporters not to disclose before an agreed date.
Upstream backports
Fixes land on main first. The release document's stable release steps then cherry-pick commits from pull requests tagged for stable backport into each active stable branch, bump the point version and tag a stable release. That is why you see point releases from several branches on the same day: on GitHub, v262.1 and v261.4 were tagged within a day of each other in October 2026.
What distributions do on top
Most people never run an upstream tarball. They run their distribution's package, which starts from an upstream release or stable point release and adds distribution patches and backports. Debian's security tracker makes this visible. On 25 October 2026 it listed these systemd versions:
| Debian release | systemd package version | Upstream lineage |
|---|---|---|
| Debian 12 "bookworm" | 252.39-1~deb12u2 | v252 stable branch, point 39 |
| Debian 13 "trixie" | 257.13-1~deb13u1 | v257 stable branch, point 13 |
| forky (testing) | 262-1 | v262 |
| sid (unstable) | 262.1-1 | v262.1 |
Read the first row carefully. The package carries v252, released in October 2022, but it is the 39th point release of that branch plus a Debian revision. The tracker lists each issue per release with statuses such as fixed, vulnerable or "no DSA" (meaning Debian chose not to issue a security advisory and may fix it in a point release instead). Comparing "252" with an upstream advisory's affected range tells you very little. The distribution's tracker is the source of truth for your package.
Every enterprise distribution works the same way, with its own tracker and errata. The rule is simple: for patch status, ask your distribution; for feature availability, use the upstream version number your package is based on.
The supply chain angle: libsystemd and libudev
systemd is not only PID 1. It ships shared libraries that other software links against: libsystemd (journal logging, sd-bus, sd-daemon notifications, login and device APIs) and libudev (device enumeration). Daemons link libsystemd to report readiness or log to the journal; hardware tools and storage stacks link libudev. That means systemd code is present in places where systemd is not running at all, such as container images.
What this looks like in practice:
- In a container image. Debian based images can contain
libsystemd0andlibudev1even though the container's PID 1 is not systemd, because packages installed into the image depend on them. An image SBOM will list them as operating system packages with the distribution's version string, for example a Debian package URL in the formpkg:deb/debian/libsystemd0@<version>. - On a host. The SBOM for a host, or an inventory from the package manager, shows the systemd source package and its binary packages, again with the distribution's version.
- In your own builds. If you build software that links libsystemd, the library comes from the build environment's package, so the build image's distribution decides which version ends up in your artifact's dependency graph.
Two consequences follow. First, a vulnerability in a systemd component may or may not matter for an image that only carries the library; the answer depends on which component the issue is in. Journal or udev library code linked into a daemon is different from a bug in PID 1 or logind, which a container does not run. Second, the version string in the SBOM is the distribution's, so match it against the distribution's tracker, not the upstream release list. Our post on using SBOMs to find affected products fast covers that matching workflow in general.
Step by step: check what you run before you harden
These steps are read-only except where noted. Run them on a test machine first if you are not sure.
1. Find the version and compiled features
systemctl --version
The first line shows the version (on v256 and later builds the full point version appears in parentheses after the release number), followed by a line of compiled features with + or - in front: +SECCOMP, +TPM2, +SELINUX, +APPARMOR and so on. A directive that relies on a feature compiled out will not work no matter how new the version number is. If -SECCOMP appears, SystemCallFilter and several other filters cannot be enforced.
Also record which package the binary came from:
# Debian and Ubuntu
dpkg -S /usr/lib/systemd/systemd
dpkg -l systemd libsystemd0 libudev1
# Fedora, RHEL and derivatives
rpm -qf /usr/lib/systemd/systemd
rpm -q systemd
2. Score your services
# Summary table of all loaded long-running services
systemd-analyze security
# Detailed breakdown for one service
systemd-analyze security nginx.service
This needs v240 or later. The summary table is a good triage list; the per-service view shows each check, whether it passed and its weight. Treat the number as a guide to which sandboxing options are not in use, which is what the man page says it measures.
3. Check which directives your version supports before you use them
The local man page matches the installed version, and modern man pages carry "Added in version" notes:
man systemd.exec | grep -n -B3 "Added in version 25"
man systemd.exec | grep -n -A2 "^ *PrivatePIDs="
If the local man pages are not installed, use the versioned online copies. The freedesktop.org site keeps them under a version path, for example https://www.freedesktop.org/software/systemd/man/252/systemd.exec.html for a v252 system. On that page, a directive that does not exist (PrivatePIDs, for instance, does not appear in the v252 page) is a directive your system will not enforce.
Then let systemd check your unit:
systemd-analyze verify /etc/systemd/system/myapp.service
Unknown settings are reported as warnings. The service manager behaves the same way at load time: it logs the unknown key and carries on. It does not refuse to start the service. That is the root of the most common hardening mistake below.
4. Review drop-ins and overrides
Hardening often lives in drop-in files, and so do changes that undo it.
# Show the unit plus every drop-in in the order applied
systemctl cat myapp.service
# List every overridden or extended unit on the system
systemd-delta --type=extended,overridden
# Find local drop-in directories
find /etc/systemd/system -type d -name '*.d'
systemctl cat prints each fragment with its path as a comment, so you can see which file set which directive. systemd-delta lists units that local files extend or override, which is the quickest way to catch an old override that silently removed a vendor's hardening.
5. Confirm the effective settings
systemctl show myapp.service -p ProtectSystem -p ProtectHome -p PrivateTmp -p NoNewPrivileges -p DynamicUser
systemctl show prints the values the manager actually applied after merging every file. If a value differs from what you wrote, a later drop-in or a typo is the cause.
6. Check logging and DNS posture
# Is the journal persistent, and is sealing set up?
ls -ld /var/log/journal
journalctl --disk-usage
journalctl --verify
# What is resolved doing?
resolvectl status
ss -lntup | grep ':53 '
journalctl --verify checks journal file consistency and, if sealing keys are in use, the seals. The ss output should show the resolved stub only on 127.0.0.53 and 127.0.0.54 unless you deliberately added listeners.
7. Apply a change safely (changes the system)
When you add a directive, put it in a drop-in rather than editing the vendor unit, then reload and restart in a maintenance window:
sudo systemctl edit myapp.service
# add, for example:
# [Service]
# ProtectKernelLogs=yes
sudo systemctl daemon-reload
sudo systemctl restart myapp.service
systemctl status myapp.service
journalctl -u myapp.service -b --no-pager | tail -n 50
Watch the service's own logs and its health checks after the restart. Sandboxing failures often surface as permission errors inside the application, not as unit failures.
Decision table: what your systemd version lets you use
Use the upstream version your distribution package is based on.
| If your base version is | You can rely on | You cannot rely on |
|---|---|---|
| 209 to 228 | PrivateTmp, PrivateDevices, PrivateNetwork, NoNewPrivileges, SystemCallFilter, ProtectSystem, ProtectHome, RestrictAddressFamilies | DynamicUser, kernel protections, systemd-analyze security |
| 232 to 239 | Wave two: DynamicUser, ProtectKernelTunables, ProtectKernelModules, ProtectControlGroups, RestrictNamespaces (233), IPAddressDeny (235) | systemd-analyze security, ProtectHostname, ProtectClock, ProtectProc |
| 240 to 246 | systemd-analyze security, RestrictSUIDSGID and ProtectHostname (242), ProtectKernelLogs (244), ProtectClock (245) | Credentials, ProtectProc |
| 247 to 249 | Credentials, ProtectProc, ProcSubset, PrivateIPC (248), SocketBindDeny (249) | Encrypted credentials, RestrictFileSystems |
| 250 to 256 (for example Debian 12 at 252) | Encrypted and TPM2 bound credentials, RestrictFileSystems, RestrictNetworkInterfaces, --security-policy | PrivatePIDs |
| 257 and later (for example Debian 13 at 257) | PrivatePIDs; at 258 and later, PrivateBPF and DelegateNamespaces | Anything announced only for a newer release |
Kernel support still applies on top of this table. RestrictFileSystems needs the BPF LSM; PrivateUsers needs user namespaces; and v258 raised the minimum kernel to 5.4.
Common mistakes
Using a directive the installed version silently ignores. Copying a hardened unit from a newer system onto an older one is the classic case. systemd logs a warning about the unknown setting and starts the service anyway, so the sandbox you think you added does not exist. Check the version, run systemd-analyze verify, and confirm with systemctl show.
Assuming a compiled-out feature works. A recent version without +SECCOMP or without BPF support cannot enforce the filters that depend on them. Read the feature line from systemctl --version.
Hardening that breaks a service with no alert. A ProtectSystem=strict without the right ReadWritePaths, or a SystemCallFilter that blocks a call the application needs only at month end, can turn into an application level error that the unit status never shows. Roll hardening out one directive group at a time, watch application logs and health checks, and keep the change in a drop-in that is easy to revert.
Trusting an old override. Local drop-ins written years ago may reset settings the vendor later hardened. systemd-delta and systemctl cat find them.
Volatile journals on servers that need logs. Before v259 the default was auto, so a system without /var/log/journal kept logs only in memory and lost them on reboot. Distributions and images also set volatile storage deliberately to save disk. On servers where logs matter for incident response, set persistent storage explicitly, size it with the journald limits, and forward logs off the host.
Exposing resolved's stub listener beyond localhost. The stub is designed for 127.0.0.53 and 127.0.0.54. Adding a DNSStubListenerExtra= address on a public interface, or forwarding port 53 to the host in a container setup, turns a local resolver into a network service. Check with ss after any DNS change.
Treating opportunistic DNS over TLS as protection against interception. The man page states that opportunistic mode cannot authenticate the server. Use the strict setting with a named server if authentication is the goal.
Comparing the distribution's version with upstream advisories. A v252 based package with many backports is not "v252". Use the distribution's tracker for patch status.
Reading the exposure score as a vulnerability score. The man page says plainly that it measures systemd's own sandboxing settings only. A score of 9 is a to-do list, not proof of a vulnerability, and a score of 2 is not proof of safety.
Ignoring the libraries. Images that never run systemd still carry libsystemd and libudev. They belong in your SBOM and your patch planning.
FAQ
When was systemd first released?
Lennart Poettering announced it in the "Rethinking PID 1" blog post on 30 April 2010, and the v1 tag in the repository is dated 7 July 2010. The NEWS changelog starts at v38, released on 11 January 2012.
Who created systemd?
The announcement credits the design to Lennart Poettering, then at Red Hat, and Kay Sievers, then at Novell. Today it has a large contributor base; each NEWS section lists that release's contributors.
What is the latest version of systemd?
As of 25 October 2026 the latest feature release is v262, released on 22 September 2026, and the latest point release on that branch is v262.1. v263 is in development.
How often does systemd release a new version?
Recently two or three feature releases a year: v258 and v259 in 2025, and v260, v261 and v262 in 2026. In 2014 and 2015 the project shipped ten releases a year; from 2016 to 2019 it shipped four a year.
Which distributions use systemd by default, and since when?
Fedora since Fedora 15 (2011), RHEL since RHEL 7 (2014), Ubuntu since 15.04 (April 2015) and Debian since Debian 8 (April 2015), according to each distribution's release material.
Which version of systemd added DynamicUser?
v232, released on 3 November 2016. v235 added StateDirectory and related settings that made it practical for services with state, and v242 made it imply NoNewPrivileges and RestrictSUIDSGID.
Which version added systemd-analyze security?
v240, released on 21 December 2018. v250 added the --security-policy= option for custom weightings.
How do I know if my systemd supports a directive?
Check systemctl --version, then look the directive up in the local man systemd.exec (which matches the installed version) or the versioned man page online. Run systemd-analyze verify on the unit: unknown settings appear as warnings.
Does systemd have a security policy?
Yes. It asks for private reports through GitHub security advisories (preferred) or the systemd-security mailing list, coordinates disclosure with distributions and sets requirements for a valid report, including a real world reproducer on the main branch.
Why does my distribution's systemd version look old?
Stable distributions freeze on one upstream release and backport fixes into it. Debian 12, for example, ships a package based on the v252 stable branch at point release 39 plus Debian's own revision. The feature set is v252's; the fixes are much newer.
Where Safeguard fits
Safeguard does not configure systemd, score unit files or manage hosts, and nothing in it replaces systemd-analyze security or your distribution's security tracker. Where it helps is the supply chain side of this story. The Safeguard CLI can scan a repository (safeguard scan --dir .), a container image (safeguard scan --image <name>) or an existing SBOM (safeguard scan --sbom <file>) and writes results as JSON under ./output. Operating system packages such as libsystemd0 and libudev1 in an image show up as components with their distribution version, and the platform shows which projects contain a given component and version and matches them against vulnerability data. Policies and gates in CI can then stop an image from shipping on a base you have decided is too old. Host operating systems outside your images, and the unit files on them, remain a job for your configuration management and your distribution.
The fit here is indirect, and that is worth saying plainly: a systemd fix almost always comes from your distribution, not from a dependency bump you make. What you control is how quickly rebuilt images and hosts pick that fix up, and how well you know which images carry systemd libraries at all.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation categories include dependencies, licenses, security posture and findings, and each one is set separately. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds.
3. Gold Registry: start from components that are already fixed
Gold Registry provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs and a completed license check, with signed build provenance (signed with Safeguard's key and verifiable with cosign, SLSA Build Level 3 as the design target) and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. Ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | Safeguard policies and gates in CI |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
The first demo is a real Safeguard CLI session. The assistant reads a finding and its advisory in a repository, asks before any command that changes something, edits a dependency version and runs the install and tests with approval. The same flow applies when the fix for a systemd related finding is a change you own, such as moving a Dockerfile to a newer base image tag so rebuilt images pick up the distribution's patched libsystemd.
Click through it at your own pace, or open it full screen.
The second demo sets up the Safeguard runner on your own machine, approves it with its device code and then scans from the terminal. That is where you would scan the images you build, so the systemd libraries and their distribution versions appear in the results before an image reaches a registry.
Click through it at your own pace, or open it full screen.
The third demo connects Claude to the Safeguard MCP server with OAuth and chooses which tools Claude can use. Once connected, a question such as "which of our projects contain libudev1, and at which versions" can be answered from your own Safeguard data while you work through a distribution advisory.
Click through it at your own pace, or open it full screen.
To try it on your own images and repositories, download Safeguard or read the CLI documentation.
Quick check
You copy ProtectProc=invisible into a unit on a host whose systemctl --version reports 245. What happens?
The service still starts. ProtectProc arrived in v247, so a v245 manager logs a warning about the unknown setting and ignores it. The service has no /proc restriction. Check the version first and confirm with systemctl show.
Debian 12 ships a systemd package versioned 252.39-1~deb12u2. An upstream advisory says versions before 256 are affected. Is the host vulnerable?
You cannot tell from the version string. The package is the v252 stable branch at point 39 plus Debian's changes, and Debian may have backported the fix. Check the Debian security tracker entry for that issue and release.
Your container image does not run systemd. Why does its SBOM list libsystemd0 and libudev1?
Other packages in the image link against those libraries, so they are installed as dependencies. They belong in your inventory and patch planning, matched against your base distribution's tracker.
Sources
- systemd NEWS (full changelog)
- Lennart Poettering: Rethinking PID 1 (30 April 2010)
- systemd.io project home
- systemd: Steps to a Successful Release (docs/RELEASE.md)
- systemd security policy (docs/SECURITY.md)
- systemd releases on GitHub
- systemd v1 tag
- systemd v38 tag
- systemd v262 release
- systemd-stable repository (stable branches before v256)
- systemd.exec man page (latest)
- systemd.exec man page (v252)
- systemd.resource-control man page
- systemd.unit man page
- systemctl man page
- systemd-analyze man page
- resolved.conf man page
- journald.conf man page
- systemd-creds man page
- run0 man page
- systemd.io: System and Service Credentials
- Fedora Project wiki: Features/systemd (Fedora 15)
- Red Hat press release: Red Hat Enterprise Linux 7 (10 June 2014)
- Ubuntu 15.04 (Vivid Vervet) release announcement
- Debian 8 "jessie" release notes (text)
- Debian security tracker: systemd source package
Last reviewed: 25 October 2026. systemd releases several times a year; check NEWS and your distribution's tracker for the latest versions.