1 { config, lib, pkgs, ... }:
2 let cfg = config.system.autoUpgrade;
10 enable = lib.mkOption {
11 type = lib.types.bool;
14 Whether to periodically upgrade NixOS to the latest
15 version. If enabled, a systemd timer will run
16 `nixos-rebuild switch --upgrade` once a
21 operation = lib.mkOption {
22 type = lib.types.enum ["switch" "boot"];
27 `nixos-rebuild switch --upgrade` or run
28 `nixos-rebuild boot --upgrade`
32 flake = lib.mkOption {
33 type = lib.types.nullOr lib.types.str;
35 example = "github:kloenk/nix";
37 The Flake URI of the NixOS configuration to build.
38 Disables the option {option}`system.autoUpgrade.channel`.
42 channel = lib.mkOption {
43 type = lib.types.nullOr lib.types.str;
45 example = "https://nixos.org/channels/nixos-14.12-small";
47 The URI of the NixOS channel to use for automatic
48 upgrades. By default, this is the channel set using
49 {command}`nix-channel` (run `nix-channel --list`
50 to see the current value).
54 flags = lib.mkOption {
55 type = lib.types.listOf lib.types.str;
59 "stuff=/home/alice/nixos-stuff"
62 "http://my-cache.example.org/"
65 Any additional flags passed to {command}`nixos-rebuild`.
67 If you are using flakes and use a local repo you can add
68 {command}`[ "--update-input" "nixpkgs" "--commit-lock-file" ]`
73 dates = lib.mkOption {
78 How often or when upgrade occurs. For most desktop and server systems
79 a sufficient upgrade frequency is once a day.
81 The format is described in
82 {manpage}`systemd.time(7)`.
86 allowReboot = lib.mkOption {
88 type = lib.types.bool;
90 Reboot the system into the new generation instead of a switch
91 if the new generation uses a different kernel, kernel modules
92 or initrd than the booted system.
93 See {option}`rebootWindow` for configuring the times at which a reboot is allowed.
97 randomizedDelaySec = lib.mkOption {
102 Add a randomized delay before each automatic upgrade.
103 The delay will be chosen between zero and this value.
104 This value must be a time span in the format specified by
105 {manpage}`systemd.time(7)`
109 fixedRandomDelay = lib.mkOption {
111 type = lib.types.bool;
114 Make the randomized delay consistent between runs.
115 This reduces the jitter between automatic upgrades.
116 See {option}`randomizedDelaySec` for configuring the randomized delay.
120 rebootWindow = lib.mkOption {
122 Define a lower and upper time value (in HH:MM format) which
123 constitute a time window during which reboots are allowed after an upgrade.
124 This option only has an effect when {option}`allowReboot` is enabled.
125 The default value of `null` means that reboots are allowed at any time.
128 example = { lower = "01:00"; upper = "05:00"; };
129 type = with lib.types; nullOr (submodule {
131 lower = lib.mkOption {
132 description = "Lower limit of the reboot window";
133 type = lib.types.strMatching "[[:digit:]]{2}:[[:digit:]]{2}";
137 upper = lib.mkOption {
138 description = "Upper limit of the reboot window";
139 type = lib.types.strMatching "[[:digit:]]{2}:[[:digit:]]{2}";
146 persistent = lib.mkOption {
148 type = lib.types.bool;
151 Takes a boolean argument. If true, the time when the service
152 unit was last triggered is stored on disk. When the timer is
153 activated, the service unit is triggered immediately if it
154 would have been triggered at least once during the time when
155 the timer was inactive. Such triggering is nonetheless
156 subject to the delay imposed by RandomizedDelaySec=. This is
157 useful to catch up on missed runs of the service when the
158 system was powered down.
166 config = lib.mkIf cfg.enable {
169 assertion = !((cfg.channel != null) && (cfg.flake != null));
171 The options 'system.autoUpgrade.channel' and 'system.autoUpgrade.flake' cannot both be set.
175 system.autoUpgrade.flags = (if cfg.flake == null then
176 [ "--no-build-output" ] ++ lib.optionals (cfg.channel != null) [
178 "nixpkgs=${cfg.channel}/nixexprs.tar.xz"
181 [ "--refresh" "--flake ${cfg.flake}" ]);
183 systemd.services.nixos-upgrade = {
184 description = "NixOS Upgrade";
186 restartIfChanged = false;
187 unitConfig.X-StopOnRemoval = false;
189 serviceConfig.Type = "oneshot";
191 environment = config.nix.envVars // {
192 inherit (config.environment.sessionVariables) NIX_PATH;
194 } // config.networking.proxy.envVars;
202 config.nix.package.out
203 config.programs.ssh.package
207 nixos-rebuild = "${config.system.build.nixos-rebuild}/bin/nixos-rebuild";
208 date = "${pkgs.coreutils}/bin/date";
209 readlink = "${pkgs.coreutils}/bin/readlink";
210 shutdown = "${config.systemd.package}/bin/shutdown";
211 upgradeFlag = lib.optional (cfg.channel == null) "--upgrade";
212 in if cfg.allowReboot then ''
213 ${nixos-rebuild} boot ${toString (cfg.flags ++ upgradeFlag)}
214 booted="$(${readlink} /run/booted-system/{initrd,kernel,kernel-modules})"
215 built="$(${readlink} /nix/var/nix/profiles/system/{initrd,kernel,kernel-modules})"
217 ${lib.optionalString (cfg.rebootWindow != null) ''
218 current_time="$(${date} +%H:%M)"
220 lower="${cfg.rebootWindow.lower}"
221 upper="${cfg.rebootWindow.upper}"
223 if [[ "''${lower}" < "''${upper}" ]]; then
224 if [[ "''${current_time}" > "''${lower}" ]] && \
225 [[ "''${current_time}" < "''${upper}" ]]; then
231 # lower > upper, so we are crossing midnight (e.g. lower=23h, upper=6h)
232 # we want to reboot if cur > 23h or cur < 6h
233 if [[ "''${current_time}" < "''${upper}" ]] || \
234 [[ "''${current_time}" > "''${lower}" ]]; then
242 if [ "''${booted}" = "''${built}" ]; then
243 ${nixos-rebuild} ${cfg.operation} ${toString cfg.flags}
244 ${lib.optionalString (cfg.rebootWindow != null) ''
245 elif [ "''${do_reboot}" != true ]; then
246 echo "Outside of configured reboot window, skipping."
252 ${nixos-rebuild} ${cfg.operation} ${toString (cfg.flags ++ upgradeFlag)}
257 after = [ "network-online.target" ];
258 wants = [ "network-online.target" ];
261 systemd.timers.nixos-upgrade = {
263 RandomizedDelaySec = cfg.randomizedDelaySec;
264 FixedRandomDelay = cfg.fixedRandomDelay;
265 Persistent = cfg.persistent;