NixOS 25.11 adds bashless initialization with explicit prerequisites
Xantusia introduces nixos-init and FirewallD integration while making the Python rebuild tool the default. The new initialization path has specific module prerequisites and does not run activationScripts.
An interpreter-free initialization path requires more than replacing a shell script with a compiled executable. The surrounding system configuration has to supply compatible mechanisms for filesystem setup and users. NixOS 25.11 introduces nixos-init, a Rust-based initialization implementation, with module assertions that make those requirements explicit.
The Xantusia release notes date the release to November 30. They also introduce FirewallD integration, update GNOME to 49, and make the Python implementation of nixos-rebuild the default. The release-source notes distinguish the new initialization option from the changes selected automatically.
NixOS appeals to me because system behavior can be expressed as declared state. Initialization is where that description meets a running machine. A compiled implementation can be useful, but its compatibility with existing module contributions matters as much as the language it is written in.
The module states what the new initialization path needs
The 25.11 nixos-init module declares an opt-in system.nixos-init.enable setting. It also requires systemd in the initrd, the /etc overlay, and either Userborn or systemd-sysusers for user management. These are evaluated assertions, rather than unstated assumptions left for the boot process to discover.
The option documentation adds a major compatibility detail. This path does not use activationScripts, and values configured there become no-ops. An installation with custom activation scripts therefore needs to account for their intended effects before selecting the new path. The release-note headline alone does not describe that migration.
This is more precise than claiming a Rust replacement executes every existing activation fragment without a shell. Arbitrary shell contributions cannot keep the same execution mechanism in an interpreter-free path. The module explicitly tells users which interface stops taking effect, making that boundary visible before activation.
The prerequisite assertions also explain why a one-line option is not a complete configuration for every existing machine. Enabling it expresses the selection, while other module choices establish whether the machine satisfies its requirements. An evaluation failure from a missing prerequisite is therefore part of the declared compatibility behavior. The module separately exposes a package option for the nixos-init executable, keeping selection of its implementation package distinct from enabling the supported initialization path.
The release notes describe the capability as allowing systems to be built without an interpreter. That is a capability of an appropriately configured system, not evidence that the release removes interpreters from every installation. Packages and custom services can still have their own runtime dependencies, independently of which initialization mechanism the system selects.
A module assertion catches configuration mismatch early
Requiring systemd initrd establishes the boot arrangement that nixos-init supports. The /etc overlay requirement establishes another part of the system setup. Userborn or systemd-sysusers supplies a supported mechanism for managing users instead of relying on arbitrary activation-script contributions.
Those checks connect the initialization implementation to the module graph. A declarative system can reject an unsupported combination during evaluation, before producing a generation whose boot behavior depends on the missing mechanism. That is a concrete advantage of expressing prerequisites as assertions.
The no-op behavior for activationScripts still needs attention because it is different from an assertion. The module documentation says those contributions do not run on this path. It does not claim that every custom script produces an automatic error explaining how to replace it. Users need to distinguish a checked prerequisite from an interface the implementation intentionally does not consume.
I prefer that explicit description to a broad compatibility claim. The useful question is which part of the desired state each initialization mechanism realizes. A change in implementation becomes reviewable when the prerequisites and ignored inputs are both documented.
Firewall policy gains a selectable backend
Xantusia adds FirewallD support through services.firewalld. It also adds a backend choice for the existing networking.firewall interface. The release notes distinguish configuring FirewallD as a standalone service from using it behind the established firewall options.
That creates two different integration routes. A standalone service exposes the new service's own configuration model. A backend selection preserves the role of the existing NixOS firewall declaration while changing which implementation realizes it. Users should identify which route a configuration intends before combining options from both.
The release describes FirewallD as a firewall daemon with a D-Bus interface providing a dynamic firewall. This adds an operational model beyond generating a fixed collection of rules once. The NixOS integration determines how that service participates in the system's declared configuration.
A backend abstraction is useful when callers can continue to express policy through familiar options. It does not establish that every backend has identical implementation details. The release provides the selection point, while service-specific behavior remains relevant when a configuration needs capabilities beyond the shared option model.
The presence of both interfaces is therefore substantive. It gives the distribution a way to integrate another firewall implementation without requiring all users to describe their policy through a completely separate module. At the same time, the standalone service remains available for configurations that intentionally choose that route.
The rebuild rewrite is now the default
The Python nixos-rebuild-ng implementation was available for testing in 25.05. Today's release selects it by default. The notes retain system.rebuild.enableNg = false as an opt-out and ask users to report issues requiring it.
This is a different adoption status from nixos-init. The new rebuild implementation becomes the normal tool, while the initialization implementation remains selected through its own option and prerequisites. Calling the release a general switch to new system tooling would obscure which decisions actually change automatically.
The release notes express an expectation that the following major release will remove the opt-out option. That is a forward plan stated today. It should not be read as a completed removal or as a reason to describe the old implementation as already unavailable in 25.11.
For system management, command identity matters because wrappers and automation invoke nixos-rebuild. A default implementation change affects that operational dependency even when the NixOS configuration being built is the same. The explicit opt-out provides a defined temporary route while reports establish any remaining incompatibilities.
GNOME's session change preserves X11 application compatibility
GNOME 49 removes its X11 session in this release, while XWayland remains available for running X11 applications. Session type and application protocol compatibility are separate issues. Losing the X11 session does not mean every application using X11 becomes impossible to run under GNOME.
The notes also say GNOME's custom session service manager has been removed in favor of systemd. That is another implementation boundary around how session components are managed. It sits alongside the display-session change without being the same compatibility event.
Default applications change too. Papers replaces Evince as the default document viewer, and Showtime replaces Totem for video. The release provides configuration routes to exclude or restore applications. This preserves the distribution's distinction between a changed default and a package that has disappeared entirely.
COSMIC advances to its beta version, following the initial integration in 25.05. The release notes describe progress toward its first stable release, rather than declaring that milestone completed. Its status remains independent of GNOME's session decisions and of the operating system's stable release designation.
Defaults can depend on installation history
The release sets PostgreSQL 17 as the default for new installations with system.stateVersion >= 25.11. That qualification is part of the behavior. Updating the Nixpkgs release used by an existing configuration is not the same as changing its state-version declaration.
NetworkManager also stops shipping a default collection of VPN plugins. Required plugins must now be configured explicitly through networking.networkmanager.plugins. This change removes an implicit package selection, so the configuration needs to state which integrations it actually requires.
These examples show why release communication needs to name the controlling option. Some changes follow the new release, some depend on state version, and others require an explicit opt-in. The module system can express those policies, but a reader needs their conditions to understand the resulting machine.
For my infrastructure use, nixos-init is the most interesting new choice because it exposes those dependencies near the initialization boundary. Its prerequisites are checked, while custom activationScripts deliberately do not run. Selecting that path means accounting for the system's activation behavior through the mechanisms it supports, rather than assuming an implementation change preserves every existing shell contribution.