September 29, 2017·6 min read·engineeringnixconfigurationdatabasesrelease

NixOS 17.09 separates software updates from state defaults

The 17.09 tag contains stateVersion-gated database defaults alongside changes that require direct configuration edits. PostgreSQL, MySQL and nginx illustrate different meanings of declarative state.

Updating the system's software and changing assumptions about its persistent data are different operations. NixOS 17.09 makes that distinction visible through database defaults gated by system.stateVersion. Its release tag, created by Franz Pletz on September 29, 2017, identifies the Hummingbird release. The notes in that tag describe both preserved state defaults and compatibility changes that still require configuration edits.

The distinction is central to operating a declarative system with existing data. A new declaration can select a different database package or directory. That does not mean the existing database files have undergone the corresponding migration. The release's compatibility conditions make some of those choices explicit instead of treating every new default as appropriate for every upgraded machine.

I am interested in NixOS because desired-state descriptions make system changes inspectable. The useful part of this release is how clearly some options state their relationship with existing state. It also shows why a declaration needs a precise contract about what it preserves, creates or leaves for an administrator to migrate.

PostgreSQL defaults depend on stateVersion

The PostgreSQL module in the release chooses its default package based on system.stateVersion. At 17.09 or higher it selects PostgreSQL 9.6. For state versions beginning at 16.03 and below the new threshold, it retains PostgreSQL 9.5. Still older state versions retain the earlier branch named in the module.

This is more precise than saying that installing 17.09 automatically changes every database to 9.6. The module explicitly preserves earlier defaults for earlier state versions. Its source comment says changes to the package default must be conditional on system.stateVersion to maintain compatibility with existing systems.

The database superuser name changes under the same 17.09 threshold. At or above the threshold, the default is postgres, while older state versions retain root. The module treats this option as internal because changing it can break database setup. That qualification explains why a default name is persistent operational information, not merely cosmetic configuration.

The data-directory default is conditional too. The implementation uses /var/lib/postgresql/ followed by the selected package's schema for the newer state version, and retains /var/db/postgresql for older state versions. Selecting another path changes where the service expects its data. The condition itself does not copy or convert the old files.

These defaults define a compatibility policy around an enabled service. They do not establish that every setting in the operating system is frozen by the same option. Each affected module still needs to implement its own relationship with the state version, and the release notes identify which database changes use that mechanism.

MySQL declarations ensure a minimum state

The release adds services.mysql.ensureDatabases and services.mysql.ensureUsers. The MySQL module's option descriptions say these ensure that named databases and users exist. They also state that changing the options never deletes existing databases or users.

For users, the guarantee is similarly additive for permissions. The option ensures at least the listed permissions and does not remove permissions when the declaration changes. This is an important limit on what desired state means for this interface. The declaration expresses required presence and grants, rather than an exact replacement for all database state.

Removing an entry from the configuration therefore does not revoke the access it previously established. The documentation says those users and permissions need manual removal. That behavior protects persistent state from automatic deletion, while leaving an administrator responsible for changes that require absence.

The declared users use Unix socket authentication. A Unix user authenticates as the database user with the same name without a password. The release notes identify an existing password-authenticated root account as a compatibility case that needs attention before using the new options. The mechanism is not a general instruction to distribute database passwords through the configuration.

I regard the explicit additive contract as useful even though it is less comprehensive than a complete account of database state. It avoids guessing whether removing a declaration should destroy data or revoke permissions. The operator can then distinguish provisioning from deliberate deletion and revocation.

Network interface names need a direct migration

Some upgrade changes are outside those database compatibility gates. In QEMU-based virtual machines, network interface names can change, with enp0s3 becoming ens3 in the example. The release attributes this to a kernel configuration change and identifies machines using interface names in their NixOS configuration as affected.

Static configuration through networking.interfaces is an important case. The notes specify the first interface as ens3, the second as ens8 and subsequent names increasing by one. An expression that still configures an old name does not automatically identify the intended interface after the naming change.

The documented activation sequence uses nixos-rebuild boot after editing the names, then reboots into the new configuration. Switching immediately can lose connectivity because the running kernel and the configuration can disagree about the names. For NixOps, the notes specify deployment with --force-reboot.

That sequence illustrates why activation is part of the configuration contract. A valid expression for the next boot may not describe the current machine's interface names. Choosing when to activate it keeps the configuration and the environment that interprets it aligned.

nginx gives distinct HTTPS policies distinct names

The nginx module also changes its option interface. enableSSL becomes onlySSL to reflect its intended meaning. forceSSL remains the option for creating a separate plain-HTTP host that redirects to HTTPS. The notes say combining the former options had worked by accident through implementation details.

The virtual-host option definitions distinguish three policies. onlySSL configures HTTPS without ordinary HTTP service. forceSSL supplies a permanent redirect from HTTP. The new addSSL provides HTTP and HTTPS with the same configuration.

Those are operationally different choices. An HTTP client can receive a redirect only when something accepts its HTTP request. A host offering HTTPS alone and a host redirecting HTTP are therefore not equivalent, even though both aim to direct normal use toward encrypted connections.

The new listen option replaces the simpler port option with a list of addresses, ports and SSL settings. Its description says it overrides the defaults supplied by addSSL and onlySSL. An explicit listener list therefore becomes the authoritative statement for those listening choices instead of another independent switch layered on top.

A module can be replaced while the system stays stable

The release introduces disabledModules, allowing one module to be disabled so another can take its place. The notes identify importing selected modules from another channel while keeping the rest of the system on a stable release as a use case. That gives a more focused customization route than treating the entire channel as the only unit of change.

The replacement still has to satisfy the configuration's needs. Disabling a module removes its participation, while importing another supplies a different implementation. The mechanism makes that choice explicit without promising that every module from a different channel is compatible with every stable package set.

Other additions extend declarative configuration in smaller areas. networking.hosts can describe /etc/hosts entries. GitLab Runner gains configOptions, and Postfix gains a structured config option for its main configuration. These changes move more service inputs into the system expression, each with its own documented behavior.

Backup scheduling changes its operational interface

The MySQL backup service now uses a mysqlbackup system account by default instead of the mysql account. Its scheduling moves from cron to systemd timers. The old services.mysqlBackup.period option is replaced by services.mysqlBackup.calendar in systemd calendar format.

That is more than a renamed string. The scheduler and execution account both change, so an existing setup needs to interpret the new option according to the timer's contract. The release notes also say there is no built-in email-on-failure function at present and identify journal monitoring as a separate way to provide notification.

The backup entry gives concrete service commands for starting a backup and inspecting its status. It keeps the claim about easier default setup separate from the question of failure notification. A configured timer, a successful backup and an alert reaching an operator remain distinct parts of running the service.