about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2021-07-23 09:26:00 +0000
committerAlyssa Ross <hi@alyssa.is>2021-07-23 09:26:00 +0000
commitab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d (patch)
tree504b28a058661f6c1cbb7d3f580020e50367ca7f /nixpkgs/nixos/modules
parent55cc63c079f49e81d695a25bc2f5b3902f2bd290 (diff)
parentb09661d41fb93562fd53f31574dbf781b130ac44 (diff)
downloadnixlib-ab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d.tar
nixlib-ab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d.tar.gz
nixlib-ab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d.tar.bz2
nixlib-ab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d.tar.lz
nixlib-ab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d.tar.xz
nixlib-ab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d.tar.zst
nixlib-ab63e0bb8dcf2b1bf8d4a26ed360af777b8f241d.zip
Merge commit 'b09661d41fb93562fd53f31574dbf781b130ac44'
Diffstat (limited to 'nixpkgs/nixos/modules')
-rw-r--r--nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix9
-rw-r--r--nixpkgs/nixos/modules/installer/tools/nixos-option/CMakeLists.txt8
-rw-r--r--nixpkgs/nixos/modules/installer/tools/nixos-option/default.nix12
-rw-r--r--nixpkgs/nixos/modules/installer/tools/nixos-option/libnix-copy-paste.cc83
-rw-r--r--nixpkgs/nixos/modules/installer/tools/nixos-option/libnix-copy-paste.hh9
-rw-r--r--nixpkgs/nixos/modules/installer/tools/nixos-option/nixos-option.cc643
-rw-r--r--nixpkgs/nixos/modules/installer/tools/tools.nix2
-rw-r--r--nixpkgs/nixos/modules/misc/ids.nix2
-rw-r--r--nixpkgs/nixos/modules/module-list.nix7
-rw-r--r--nixpkgs/nixos/modules/programs/udevil.nix3
-rw-r--r--nixpkgs/nixos/modules/programs/xwayland.nix22
-rw-r--r--nixpkgs/nixos/modules/programs/zsh/zsh.nix26
-rw-r--r--nixpkgs/nixos/modules/services/backup/postgresql-backup.nix14
-rw-r--r--nixpkgs/nixos/modules/services/backup/sanoid.nix4
-rw-r--r--nixpkgs/nixos/modules/services/cluster/k3s/default.nix31
-rw-r--r--nixpkgs/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix2
-rw-r--r--nixpkgs/nixos/modules/services/hardware/ddccontrol.nix36
-rw-r--r--nixpkgs/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix3
-rw-r--r--nixpkgs/nixos/modules/services/misc/clipcat.nix31
-rw-r--r--nixpkgs/nixos/modules/services/misc/gitlab.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/home-assistant.nix1
-rw-r--r--nixpkgs/nixos/modules/services/misc/klipper.nix74
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix12
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix3
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/buildkite-agent.nix64
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/kea.nix1
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/telegraf.nix11
-rw-r--r--nixpkgs/nixos/modules/services/networking/bind.nix29
-rw-r--r--nixpkgs/nixos/modules/services/networking/corerad.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/ddclient.nix11
-rw-r--r--nixpkgs/nixos/modules/services/networking/kea.nix361
-rw-r--r--nixpkgs/nixos/modules/services/networking/matterbridge.nix6
-rw-r--r--nixpkgs/nixos/modules/services/networking/nix-serve.nix8
-rw-r--r--nixpkgs/nixos/modules/services/networking/pppd.nix26
-rw-r--r--nixpkgs/nixos/modules/services/networking/smartdns.nix1
-rw-r--r--nixpkgs/nixos/modules/services/networking/unbound.nix11
-rw-r--r--nixpkgs/nixos/modules/services/networking/znc/default.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/znc/options.nix6
-rw-r--r--nixpkgs/nixos/modules/services/security/hockeypuck.nix104
-rw-r--r--nixpkgs/nixos/modules/services/security/vaultwarden/default.nix18
-rw-r--r--nixpkgs/nixos/modules/services/ttys/getty.nix10
-rw-r--r--nixpkgs/nixos/modules/services/video/unifi-video.nix265
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/discourse.nix19
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/discourse.xml43
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix18
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nextcloud.nix16
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nextcloud.xml2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/plausible.nix18
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/vikunja.nix145
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/wordpress.nix126
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd.nix2
-rw-r--r--nixpkgs/nixos/modules/tasks/snapraid.nix230
-rw-r--r--nixpkgs/nixos/modules/virtualisation/cri-o.nix75
-rw-r--r--nixpkgs/nixos/modules/virtualisation/oci-containers.nix33
-rw-r--r--nixpkgs/nixos/modules/virtualisation/vmware-guest.nix2
55 files changed, 1782 insertions, 924 deletions
diff --git a/nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix
index 801e28cec44a..e3576074a5b7 100644
--- a/nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,6 +1,7 @@
 {
-  x86_64-linux = "/nix/store/d1ppfhjhdwcsb4npfzyifv5z8i00fzsk-nix-2.3.11";
-  i686-linux = "/nix/store/c6ikndcrzwpfn2sb5b9xb1f17p9b8iga-nix-2.3.11";
-  aarch64-linux = "/nix/store/fb0lfrn0m8s197d264jzd64vhz9c8zbx-nix-2.3.11";
-  x86_64-darwin = "/nix/store/qvb86ffv08q3r66qbd6nqifz425lyyhf-nix-2.3.11";
+  x86_64-linux = "/nix/store/qsgz2hhn6mzlzp53a7pwf9z2pq3l5z6h-nix-2.3.14";
+  i686-linux = "/nix/store/1yw40bj04lykisw2jilq06lir3k9ga4a-nix-2.3.14";
+  aarch64-linux = "/nix/store/32yzwmynmjxfrkb6y6l55liaqdrgkj4a-nix-2.3.14";
+  x86_64-darwin = "/nix/store/06j0vi2d13w4l0p3jsigq7lk4x6gkycj-nix-2.3.14";
+  aarch64-darwin = "/nix/store/77wi7vpbrghw5rgws25w30bwb8yggnk9-nix-2.3.14";
 }
diff --git a/nixpkgs/nixos/modules/installer/tools/nixos-option/CMakeLists.txt b/nixpkgs/nixos/modules/installer/tools/nixos-option/CMakeLists.txt
deleted file mode 100644
index e5834598c4fd..000000000000
--- a/nixpkgs/nixos/modules/installer/tools/nixos-option/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-cmake_minimum_required (VERSION 2.6)
-project (nixos-option)
-
-add_executable(nixos-option nixos-option.cc libnix-copy-paste.cc)
-target_link_libraries(nixos-option PRIVATE -lnixmain -lnixexpr -lnixstore -lnixutil)
-target_compile_features(nixos-option PRIVATE cxx_std_17)
-
-install (TARGETS nixos-option DESTINATION bin)
diff --git a/nixpkgs/nixos/modules/installer/tools/nixos-option/default.nix b/nixpkgs/nixos/modules/installer/tools/nixos-option/default.nix
index 72eec3a38363..061460f38a3b 100644
--- a/nixpkgs/nixos/modules/installer/tools/nixos-option/default.nix
+++ b/nixpkgs/nixos/modules/installer/tools/nixos-option/default.nix
@@ -1,11 +1 @@
-{lib, stdenv, boost, cmake, pkg-config, nix, ... }:
-stdenv.mkDerivation rec {
-  name = "nixos-option";
-  src = ./.;
-  nativeBuildInputs = [ cmake pkg-config ];
-  buildInputs = [ boost nix ];
-  meta = with lib; {
-    license = licenses.lgpl2Plus;
-    maintainers = with maintainers; [ chkno ];
-  };
-}
+{ pkgs, ... }: pkgs.nixos-option
diff --git a/nixpkgs/nixos/modules/installer/tools/nixos-option/libnix-copy-paste.cc b/nixpkgs/nixos/modules/installer/tools/nixos-option/libnix-copy-paste.cc
deleted file mode 100644
index 875c07da6399..000000000000
--- a/nixpkgs/nixos/modules/installer/tools/nixos-option/libnix-copy-paste.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-// These are useful methods inside the nix library that ought to be exported.
-// Since they are not, copy/paste them here.
-// TODO: Delete these and use the ones in the library as they become available.
-
-#include <nix/config.h> // for nix/globals.hh's reference to SYSTEM
-
-#include "libnix-copy-paste.hh"
-#include <boost/format/alt_sstream.hpp>           // for basic_altstringbuf...
-#include <boost/format/alt_sstream_impl.hpp>      // for basic_altstringbuf...
-#include <boost/format/format_class.hpp>          // for basic_format
-#include <boost/format/format_fwd.hpp>            // for format
-#include <boost/format/format_implementation.hpp> // for basic_format::basi...
-#include <boost/optional/optional.hpp>            // for get_pointer
-#include <iostream>                               // for operator<<, basic_...
-#include <nix/types.hh>                           // for Strings, Error
-#include <string>                                 // for string, basic_string
-
-using boost::format;
-using nix::Error;
-using nix::Strings;
-using std::string;
-
-// From nix/src/libexpr/attr-path.cc
-Strings parseAttrPath(const string & s)
-{
-    Strings res;
-    string cur;
-    string::const_iterator i = s.begin();
-    while (i != s.end()) {
-        if (*i == '.') {
-            res.push_back(cur);
-            cur.clear();
-        } else if (*i == '"') {
-            ++i;
-            while (1) {
-                if (i == s.end())
-                    throw Error(format("missing closing quote in selection path '%1%'") % s);
-                if (*i == '"')
-                    break;
-                cur.push_back(*i++);
-            }
-        } else
-            cur.push_back(*i);
-        ++i;
-    }
-    if (!cur.empty())
-        res.push_back(cur);
-    return res;
-}
-
-// From nix/src/nix/repl.cc
-bool isVarName(const string & s)
-{
-    if (s.size() == 0)
-        return false;
-    char c = s[0];
-    if ((c >= '0' && c <= '9') || c == '-' || c == '\'')
-        return false;
-    for (auto & i : s)
-        if (!((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == '-' ||
-              i == '\''))
-            return false;
-    return true;
-}
-
-// From nix/src/nix/repl.cc
-std::ostream & printStringValue(std::ostream & str, const char * string)
-{
-    str << "\"";
-    for (const char * i = string; *i; i++)
-        if (*i == '\"' || *i == '\\')
-            str << "\\" << *i;
-        else if (*i == '\n')
-            str << "\\n";
-        else if (*i == '\r')
-            str << "\\r";
-        else if (*i == '\t')
-            str << "\\t";
-        else
-            str << *i;
-    str << "\"";
-    return str;
-}
diff --git a/nixpkgs/nixos/modules/installer/tools/nixos-option/libnix-copy-paste.hh b/nixpkgs/nixos/modules/installer/tools/nixos-option/libnix-copy-paste.hh
deleted file mode 100644
index 2274e9a0f853..000000000000
--- a/nixpkgs/nixos/modules/installer/tools/nixos-option/libnix-copy-paste.hh
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma once
-
-#include <iostream>
-#include <nix/types.hh>
-#include <string>
-
-nix::Strings parseAttrPath(const std::string & s);
-bool isVarName(const std::string & s);
-std::ostream & printStringValue(std::ostream & str, const char * string);
diff --git a/nixpkgs/nixos/modules/installer/tools/nixos-option/nixos-option.cc b/nixpkgs/nixos/modules/installer/tools/nixos-option/nixos-option.cc
deleted file mode 100644
index f779d82edbd6..000000000000
--- a/nixpkgs/nixos/modules/installer/tools/nixos-option/nixos-option.cc
+++ /dev/null
@@ -1,643 +0,0 @@
-#include <nix/config.h> // for nix/globals.hh's reference to SYSTEM
-
-#include <exception>               // for exception_ptr, current_exception
-#include <functional>              // for function
-#include <iostream>                // for operator<<, basic_ostream, ostrin...
-#include <iterator>                // for next
-#include <list>                    // for _List_iterator
-#include <memory>                  // for allocator, unique_ptr, make_unique
-#include <new>                     // for operator new
-#include <nix/args.hh>             // for argvToStrings, UsageError
-#include <nix/attr-path.hh>        // for findAlongAttrPath
-#include <nix/attr-set.hh>         // for Attr, Bindings, Bindings::iterator
-#include <nix/common-eval-args.hh> // for MixEvalArgs
-#include <nix/eval-inline.hh>      // for EvalState::forceValue
-#include <nix/eval.hh>             // for EvalState, initGC, operator<<
-#include <nix/globals.hh>          // for initPlugins, Settings, settings
-#include <nix/nixexpr.hh>          // for Pos
-#include <nix/shared.hh>           // for getArg, LegacyArgs, printVersion
-#include <nix/store-api.hh>        // for openStore
-#include <nix/symbol-table.hh>     // for Symbol, SymbolTable
-#include <nix/types.hh>            // for Error, Path, Strings, PathSet
-#include <nix/util.hh>             // for absPath, baseNameOf
-#include <nix/value.hh>            // for Value, Value::(anonymous), Value:...
-#include <string>                  // for string, operator+, operator==
-#include <utility>                 // for move
-#include <variant>                 // for get, holds_alternative, variant
-#include <vector>                  // for vector<>::iterator, vector
-
-#include "libnix-copy-paste.hh"
-
-using nix::absPath;
-using nix::Bindings;
-using nix::Error;
-using nix::EvalError;
-using nix::EvalState;
-using nix::Path;
-using nix::PathSet;
-using nix::Strings;
-using nix::Symbol;
-using nix::tAttrs;
-using nix::ThrownError;
-using nix::tLambda;
-using nix::tString;
-using nix::UsageError;
-using nix::Value;
-
-// An ostream wrapper to handle nested indentation
-class Out
-{
-  public:
-    class Separator
-    {};
-    const static Separator sep;
-    enum LinePolicy
-    {
-        ONE_LINE,
-        MULTI_LINE
-    };
-    explicit Out(std::ostream & ostream) : ostream(ostream), policy(ONE_LINE), writeSinceSep(true) {}
-    Out(Out & o, const std::string & start, const std::string & end, LinePolicy policy);
-    Out(Out & o, const std::string & start, const std::string & end, int count)
-        : Out(o, start, end, count < 2 ? ONE_LINE : MULTI_LINE)
-    {}
-    Out(const Out &) = delete;
-    Out(Out &&) = default;
-    Out & operator=(const Out &) = delete;
-    Out & operator=(Out &&) = delete;
-    ~Out() { ostream << end; }
-
-  private:
-    std::ostream & ostream;
-    std::string indentation;
-    std::string end;
-    LinePolicy policy;
-    bool writeSinceSep;
-    template <typename T> friend Out & operator<<(Out & o, T thing);
-};
-
-template <typename T> Out & operator<<(Out & o, T thing)
-{
-    if (!o.writeSinceSep && o.policy == Out::MULTI_LINE) {
-        o.ostream << o.indentation;
-    }
-    o.writeSinceSep = true;
-    o.ostream << thing;
-    return o;
-}
-
-template <> Out & operator<<<Out::Separator>(Out & o, Out::Separator /* thing */)
-{
-    o.ostream << (o.policy == Out::ONE_LINE ? " " : "\n");
-    o.writeSinceSep = false;
-    return o;
-}
-
-Out::Out(Out & o, const std::string & start, const std::string & end, LinePolicy policy)
-    : ostream(o.ostream), indentation(policy == ONE_LINE ? o.indentation : o.indentation + "  "),
-      end(policy == ONE_LINE ? end : o.indentation + end), policy(policy), writeSinceSep(true)
-{
-    o << start;
-    *this << Out::sep;
-}
-
-// Stuff needed for evaluation
-struct Context
-{
-    Context(EvalState & state, Bindings & autoArgs, Value optionsRoot, Value configRoot)
-        : state(state), autoArgs(autoArgs), optionsRoot(optionsRoot), configRoot(configRoot),
-          underscoreType(state.symbols.create("_type"))
-    {}
-    EvalState & state;
-    Bindings & autoArgs;
-    Value optionsRoot;
-    Value configRoot;
-    Symbol underscoreType;
-};
-
-Value evaluateValue(Context & ctx, Value & v)
-{
-    ctx.state.forceValue(v);
-    if (ctx.autoArgs.empty()) {
-        return v;
-    }
-    Value called{};
-    ctx.state.autoCallFunction(ctx.autoArgs, v, called);
-    return called;
-}
-
-bool isOption(Context & ctx, const Value & v)
-{
-    if (v.type != tAttrs) {
-        return false;
-    }
-    const auto & actualType = v.attrs->find(ctx.underscoreType);
-    if (actualType == v.attrs->end()) {
-        return false;
-    }
-    try {
-        Value evaluatedType = evaluateValue(ctx, *actualType->value);
-        if (evaluatedType.type != tString) {
-            return false;
-        }
-        return static_cast<std::string>(evaluatedType.string.s) == "option";
-    } catch (Error &) {
-        return false;
-    }
-}
-
-// Add quotes to a component of a path.
-// These are needed for paths like:
-//    fileSystems."/".fsType
-//    systemd.units."dbus.service".text
-std::string quoteAttribute(const std::string & attribute)
-{
-    if (isVarName(attribute)) {
-        return attribute;
-    }
-    std::ostringstream buf;
-    printStringValue(buf, attribute.c_str());
-    return buf.str();
-}
-
-const std::string appendPath(const std::string & prefix, const std::string & suffix)
-{
-    if (prefix.empty()) {
-        return quoteAttribute(suffix);
-    }
-    return prefix + "." + quoteAttribute(suffix);
-}
-
-bool forbiddenRecursionName(std::string name) { return (!name.empty() && name[0] == '_') || name == "haskellPackages"; }
-
-void recurse(const std::function<bool(const std::string & path, std::variant<Value, std::exception_ptr>)> & f,
-             Context & ctx, Value v, const std::string & path)
-{
-    std::variant<Value, std::exception_ptr> evaluated;
-    try {
-        evaluated = evaluateValue(ctx, v);
-    } catch (Error &) {
-        evaluated = std::current_exception();
-    }
-    if (!f(path, evaluated)) {
-        return;
-    }
-    if (std::holds_alternative<std::exception_ptr>(evaluated)) {
-        return;
-    }
-    const Value & evaluated_value = std::get<Value>(evaluated);
-    if (evaluated_value.type != tAttrs) {
-        return;
-    }
-    for (const auto & child : evaluated_value.attrs->lexicographicOrder()) {
-        if (forbiddenRecursionName(child->name)) {
-            continue;
-        }
-        recurse(f, ctx, *child->value, appendPath(path, child->name));
-    }
-}
-
-bool optionTypeIs(Context & ctx, Value & v, const std::string & soughtType)
-{
-    try {
-        const auto & typeLookup = v.attrs->find(ctx.state.sType);
-        if (typeLookup == v.attrs->end()) {
-            return false;
-        }
-        Value type = evaluateValue(ctx, *typeLookup->value);
-        if (type.type != tAttrs) {
-            return false;
-        }
-        const auto & nameLookup = type.attrs->find(ctx.state.sName);
-        if (nameLookup == type.attrs->end()) {
-            return false;
-        }
-        Value name = evaluateValue(ctx, *nameLookup->value);
-        if (name.type != tString) {
-            return false;
-        }
-        return name.string.s == soughtType;
-    } catch (Error &) {
-        return false;
-    }
-}
-
-bool isAggregateOptionType(Context & ctx, Value & v)
-{
-    return optionTypeIs(ctx, v, "attrsOf") || optionTypeIs(ctx, v, "listOf");
-}
-
-MakeError(OptionPathError, EvalError);
-
-Value getSubOptions(Context & ctx, Value & option)
-{
-    Value getSubOptions = evaluateValue(ctx, *findAlongAttrPath(ctx.state, "type.getSubOptions", ctx.autoArgs, option));
-    if (getSubOptions.type != tLambda) {
-        throw OptionPathError("Option's type.getSubOptions isn't a function");
-    }
-    Value emptyString{};
-    nix::mkString(emptyString, "");
-    Value v;
-    ctx.state.callFunction(getSubOptions, emptyString, v, nix::Pos{});
-    return v;
-}
-
-// Carefully walk an option path, looking for sub-options when a path walks past
-// an option value.
-struct FindAlongOptionPathRet
-{
-    Value option;
-    std::string path;
-};
-FindAlongOptionPathRet findAlongOptionPath(Context & ctx, const std::string & path)
-{
-    Strings tokens = parseAttrPath(path);
-    Value v = ctx.optionsRoot;
-    std::string processedPath;
-    for (auto i = tokens.begin(); i != tokens.end(); i++) {
-        const auto & attr = *i;
-        try {
-            bool lastAttribute = std::next(i) == tokens.end();
-            v = evaluateValue(ctx, v);
-            if (attr.empty()) {
-                throw OptionPathError("empty attribute name");
-            }
-            if (isOption(ctx, v) && optionTypeIs(ctx, v, "submodule")) {
-                v = getSubOptions(ctx, v);
-            }
-            if (isOption(ctx, v) && isAggregateOptionType(ctx, v)) {
-                auto subOptions = getSubOptions(ctx, v);
-                if (lastAttribute && subOptions.attrs->empty()) {
-                    break;
-                }
-                v = subOptions;
-                // Note that we've consumed attr, but didn't actually use it.  This is the path component that's looked
-                // up in the list or attribute set that doesn't name an option -- the "root" in "users.users.root.name".
-            } else if (v.type != tAttrs) {
-                throw OptionPathError("Value is %s while a set was expected", showType(v));
-            } else {
-                const auto & next = v.attrs->find(ctx.state.symbols.create(attr));
-                if (next == v.attrs->end()) {
-                    throw OptionPathError("Attribute not found", attr, path);
-                }
-                v = *next->value;
-            }
-            processedPath = appendPath(processedPath, attr);
-        } catch (OptionPathError & e) {
-            throw OptionPathError("At '%s' in path '%s': %s", attr, path, e.msg());
-        }
-    }
-    return {v, processedPath};
-}
-
-// Calls f on all the option names at or below the option described by `path`.
-// Note that "the option described by `path`" is not trivial -- if path describes a value inside an aggregate
-// option (such as users.users.root), the *option* described by that path is one path component shorter
-// (eg: users.users), which results in f being called on sibling-paths (eg: users.users.nixbld1).  If f
-// doesn't want these, it must do its own filtering.
-void mapOptions(const std::function<void(const std::string & path)> & f, Context & ctx, const std::string & path)
-{
-    auto root = findAlongOptionPath(ctx, path);
-    recurse(
-        [f, &ctx](const std::string & path, std::variant<Value, std::exception_ptr> v) {
-            bool isOpt = std::holds_alternative<std::exception_ptr>(v) || isOption(ctx, std::get<Value>(v));
-            if (isOpt) {
-                f(path);
-            }
-            return !isOpt;
-        },
-        ctx, root.option, root.path);
-}
-
-// Calls f on all the config values inside one option.
-// Simple options have one config value inside, like sound.enable = true.
-// Compound options have multiple config values.  For example, the option
-// "users.users" has about 1000 config values inside it:
-//   users.users.avahi.createHome = false;
-//   users.users.avahi.cryptHomeLuks = null;
-//   users.users.avahi.description = "`avahi-daemon' privilege separation user";
-//   ...
-//   users.users.avahi.openssh.authorizedKeys.keyFiles = [ ];
-//   users.users.avahi.openssh.authorizedKeys.keys = [ ];
-//   ...
-//   users.users.avahi.uid = 10;
-//   users.users.avahi.useDefaultShell = false;
-//   users.users.cups.createHome = false;
-//   ...
-//   users.users.cups.useDefaultShell = false;
-//   users.users.gdm = ... ... ...
-//   users.users.messagebus = ... .. ...
-//   users.users.nixbld1 = ... .. ...
-//   ...
-//   users.users.systemd-timesync = ... .. ...
-void mapConfigValuesInOption(
-    const std::function<void(const std::string & path, std::variant<Value, std::exception_ptr> v)> & f,
-    const std::string & path, Context & ctx)
-{
-    Value * option;
-    try {
-        option = findAlongAttrPath(ctx.state, path, ctx.autoArgs, ctx.configRoot);
-    } catch (Error &) {
-        f(path, std::current_exception());
-        return;
-    }
-    recurse(
-        [f, ctx](const std::string & path, std::variant<Value, std::exception_ptr> v) {
-            bool leaf = std::holds_alternative<std::exception_ptr>(v) || std::get<Value>(v).type != tAttrs ||
-                        ctx.state.isDerivation(std::get<Value>(v));
-            if (!leaf) {
-                return true; // Keep digging
-            }
-            f(path, v);
-            return false;
-        },
-        ctx, *option, path);
-}
-
-std::string describeError(const Error & e) { return "«error: " + e.msg() + "»"; }
-
-void describeDerivation(Context & ctx, Out & out, Value v)
-{
-    // Copy-pasted from nix/src/nix/repl.cc  :(
-    Bindings::iterator i = v.attrs->find(ctx.state.sDrvPath);
-    PathSet pathset;
-    try {
-        Path drvPath = i != v.attrs->end() ? ctx.state.coerceToPath(*i->pos, *i->value, pathset) : "???";
-        out << "«derivation " << drvPath << "»";
-    } catch (Error & e) {
-        out << describeError(e);
-    }
-}
-
-Value parseAndEval(EvalState & state, const std::string & expression, const std::string & path)
-{
-    Value v{};
-    state.eval(state.parseExprFromString(expression, absPath(path)), v);
-    return v;
-}
-
-void printValue(Context & ctx, Out & out, std::variant<Value, std::exception_ptr> maybeValue, const std::string & path);
-
-void printList(Context & ctx, Out & out, Value & v)
-{
-    Out listOut(out, "[", "]", v.listSize());
-    for (unsigned int n = 0; n < v.listSize(); ++n) {
-        printValue(ctx, listOut, *v.listElems()[n], "");
-        listOut << Out::sep;
-    }
-}
-
-void printAttrs(Context & ctx, Out & out, Value & v, const std::string & path)
-{
-    Out attrsOut(out, "{", "}", v.attrs->size());
-    for (const auto & a : v.attrs->lexicographicOrder()) {
-        std::string name = a->name;
-        if (!forbiddenRecursionName(name)) {
-            attrsOut << name << " = ";
-            printValue(ctx, attrsOut, *a->value, appendPath(path, name));
-            attrsOut << ";" << Out::sep;
-        }
-    }
-}
-
-void multiLineStringEscape(Out & out, const std::string & s)
-{
-    int i;
-    for (i = 1; i < s.size(); i++) {
-        if (s[i - 1] == '$' && s[i] == '{') {
-            out << "''${";
-            i++;
-        } else if (s[i - 1] == '\'' && s[i] == '\'') {
-            out << "'''";
-            i++;
-        } else {
-            out << s[i - 1];
-        }
-    }
-    if (i == s.size()) {
-        out << s[i - 1];
-    }
-}
-
-void printMultiLineString(Out & out, const Value & v)
-{
-    std::string s = v.string.s;
-    Out strOut(out, "''", "''", Out::MULTI_LINE);
-    std::string::size_type begin = 0;
-    while (begin < s.size()) {
-        std::string::size_type end = s.find('\n', begin);
-        if (end == std::string::npos) {
-            multiLineStringEscape(strOut, s.substr(begin, s.size() - begin));
-            break;
-        }
-        multiLineStringEscape(strOut, s.substr(begin, end - begin));
-        strOut << Out::sep;
-        begin = end + 1;
-    }
-}
-
-void printValue(Context & ctx, Out & out, std::variant<Value, std::exception_ptr> maybeValue, const std::string & path)
-{
-    try {
-        if (auto ex = std::get_if<std::exception_ptr>(&maybeValue)) {
-            std::rethrow_exception(*ex);
-        }
-        Value v = evaluateValue(ctx, std::get<Value>(maybeValue));
-        if (ctx.state.isDerivation(v)) {
-            describeDerivation(ctx, out, v);
-        } else if (v.isList()) {
-            printList(ctx, out, v);
-        } else if (v.type == tAttrs) {
-            printAttrs(ctx, out, v, path);
-        } else if (v.type == tString && std::string(v.string.s).find('\n') != std::string::npos) {
-            printMultiLineString(out, v);
-        } else {
-            ctx.state.forceValueDeep(v);
-            out << v;
-        }
-    } catch (ThrownError & e) {
-        if (e.msg() == "The option `" + path + "' is used but not defined.") {
-            // 93% of errors are this, and just letting this message through would be
-            // misleading.  These values may or may not actually be "used" in the
-            // config.  The thing throwing the error message assumes that if anything
-            // ever looks at this value, it is a "use" of this value.  But here in
-            // nixos-option, we are looking at this value only to print it.
-            // In order to avoid implying that this undefined value is actually
-            // referenced, eat the underlying error message and emit "«not defined»".
-            out << "«not defined»";
-        } else {
-            out << describeError(e);
-        }
-    } catch (Error & e) {
-        out << describeError(e);
-    }
-}
-
-void printConfigValue(Context & ctx, Out & out, const std::string & path, std::variant<Value, std::exception_ptr> v)
-{
-    out << path << " = ";
-    printValue(ctx, out, std::move(v), path);
-    out << ";\n";
-}
-
-// Replace with std::starts_with when C++20 is available
-bool starts_with(const std::string & s, const std::string & prefix)
-{
-    return s.size() >= prefix.size() &&
-           std::equal(s.begin(), std::next(s.begin(), prefix.size()), prefix.begin(), prefix.end());
-}
-
-void printRecursive(Context & ctx, Out & out, const std::string & path)
-{
-    mapOptions(
-        [&ctx, &out, &path](const std::string & optionPath) {
-            mapConfigValuesInOption(
-                [&ctx, &out, &path](const std::string & configPath, std::variant<Value, std::exception_ptr> v) {
-                    if (starts_with(configPath, path)) {
-                        printConfigValue(ctx, out, configPath, v);
-                    }
-                },
-                optionPath, ctx);
-        },
-        ctx, path);
-}
-
-void printAttr(Context & ctx, Out & out, const std::string & path, Value & root)
-{
-    try {
-        printValue(ctx, out, *findAlongAttrPath(ctx.state, path, ctx.autoArgs, root), path);
-    } catch (Error & e) {
-        out << describeError(e);
-    }
-}
-
-bool hasExample(Context & ctx, Value & option)
-{
-    try {
-        findAlongAttrPath(ctx.state, "example", ctx.autoArgs, option);
-        return true;
-    } catch (Error &) {
-        return false;
-    }
-}
-
-void printOption(Context & ctx, Out & out, const std::string & path, Value & option)
-{
-    out << "Value:\n";
-    printAttr(ctx, out, path, ctx.configRoot);
-
-    out << "\n\nDefault:\n";
-    printAttr(ctx, out, "default", option);
-
-    out << "\n\nType:\n";
-    printAttr(ctx, out, "type.description", option);
-
-    if (hasExample(ctx, option)) {
-        out << "\n\nExample:\n";
-        printAttr(ctx, out, "example", option);
-    }
-
-    out << "\n\nDescription:\n";
-    printAttr(ctx, out, "description", option);
-
-    out << "\n\nDeclared by:\n";
-    printAttr(ctx, out, "declarations", option);
-
-    out << "\n\nDefined by:\n";
-    printAttr(ctx, out, "files", option);
-    out << "\n";
-}
-
-void printListing(Out & out, Value & v)
-{
-    out << "This attribute set contains:\n";
-    for (const auto & a : v.attrs->lexicographicOrder()) {
-        std::string name = a->name;
-        if (!name.empty() && name[0] != '_') {
-            out << name << "\n";
-        }
-    }
-}
-
-void printOne(Context & ctx, Out & out, const std::string & path)
-{
-    try {
-        auto result = findAlongOptionPath(ctx, path);
-        Value & option = result.option;
-        option = evaluateValue(ctx, option);
-        if (path != result.path) {
-            out << "Note: showing " << result.path << " instead of " << path << "\n";
-        }
-        if (isOption(ctx, option)) {
-            printOption(ctx, out, result.path, option);
-        } else {
-            printListing(out, option);
-        }
-    } catch (Error & e) {
-        std::cerr << "error: " << e.msg()
-                  << "\nAn error occurred while looking for attribute names. Are "
-                     "you sure that '"
-                  << path << "' exists?\n";
-    }
-}
-
-int main(int argc, char ** argv)
-{
-    bool recursive = false;
-    std::string path = ".";
-    std::string optionsExpr = "(import <nixpkgs/nixos> {}).options";
-    std::string configExpr = "(import <nixpkgs/nixos> {}).config";
-    std::vector<std::string> args;
-
-    struct MyArgs : nix::LegacyArgs, nix::MixEvalArgs
-    {
-        using nix::LegacyArgs::LegacyArgs;
-    };
-
-    MyArgs myArgs(nix::baseNameOf(argv[0]), [&](Strings::iterator & arg, const Strings::iterator & end) {
-        if (*arg == "--help") {
-            nix::showManPage("nixos-option");
-        } else if (*arg == "--version") {
-            nix::printVersion("nixos-option");
-        } else if (*arg == "-r" || *arg == "--recursive") {
-            recursive = true;
-        } else if (*arg == "--path") {
-            path = nix::getArg(*arg, arg, end);
-        } else if (*arg == "--options_expr") {
-            optionsExpr = nix::getArg(*arg, arg, end);
-        } else if (*arg == "--config_expr") {
-            configExpr = nix::getArg(*arg, arg, end);
-        } else if (!arg->empty() && arg->at(0) == '-') {
-            return false;
-        } else {
-            args.push_back(*arg);
-        }
-        return true;
-    });
-
-    myArgs.parseCmdline(nix::argvToStrings(argc, argv));
-
-    nix::initPlugins();
-    nix::initGC();
-    nix::settings.readOnlyMode = true;
-    auto store = nix::openStore();
-    auto state = std::make_unique<EvalState>(myArgs.searchPath, store);
-
-    Value optionsRoot = parseAndEval(*state, optionsExpr, path);
-    Value configRoot = parseAndEval(*state, configExpr, path);
-
-    Context ctx{*state, *myArgs.getAutoArgs(*state), optionsRoot, configRoot};
-    Out out(std::cout);
-
-    auto print = recursive ? printRecursive : printOne;
-    if (args.empty()) {
-        print(ctx, out, "");
-    }
-    for (const auto & arg : args) {
-        print(ctx, out, arg);
-    }
-
-    ctx.state.printStats();
-
-    return 0;
-}
diff --git a/nixpkgs/nixos/modules/installer/tools/tools.nix b/nixpkgs/nixos/modules/installer/tools/tools.nix
index 1dc0578daca6..f79ed3493dfb 100644
--- a/nixpkgs/nixos/modules/installer/tools/tools.nix
+++ b/nixpkgs/nixos/modules/installer/tools/tools.nix
@@ -42,7 +42,7 @@ let
   nixos-option =
     if lib.versionAtLeast (lib.getVersion config.nix.package) "2.4pre"
     then null
-    else pkgs.callPackage ./nixos-option { };
+    else pkgs.nixos-option;
 
   nixos-version = makeProg {
     name = "nixos-version";
diff --git a/nixpkgs/nixos/modules/misc/ids.nix b/nixpkgs/nixos/modules/misc/ids.nix
index b08c3cacf3a3..fe6e563d7ee5 100644
--- a/nixpkgs/nixos/modules/misc/ids.nix
+++ b/nixpkgs/nixos/modules/misc/ids.nix
@@ -229,7 +229,7 @@ in
       grafana = 196;
       skydns = 197;
       # ripple-rest = 198; # unused, removed 2017-08-12
-      nix-serve = 199;
+      # nix-serve = 199; # unused, removed 2020-12-12
       tvheadend = 200;
       uwsgi = 201;
       gitit = 202;
diff --git a/nixpkgs/nixos/modules/module-list.nix b/nixpkgs/nixos/modules/module-list.nix
index 9ea1c3fb62e1..0ae556cea138 100644
--- a/nixpkgs/nixos/modules/module-list.nix
+++ b/nixpkgs/nixos/modules/module-list.nix
@@ -389,6 +389,7 @@
   ./services/hardware/bolt.nix
   ./services/hardware/brltty.nix
   ./services/hardware/evscript.nix
+  ./services/hardware/ddccontrol.nix
   ./services/hardware/fancontrol.nix
   ./services/hardware/freefall.nix
   ./services/hardware/fwupd.nix
@@ -476,6 +477,7 @@
   ./services/misc/calibre-server.nix
   ./services/misc/cfdyndns.nix
   ./services/misc/clipmenu.nix
+  ./services/misc/clipcat.nix
   ./services/misc/cpuminer-cryptonight.nix
   ./services/misc/cgminer.nix
   ./services/misc/confd.nix
@@ -728,6 +730,7 @@
   ./services/networking/iwd.nix
   ./services/networking/jicofo.nix
   ./services/networking/jitsi-videobridge.nix
+  ./services/networking/kea.nix
   ./services/networking/keepalived/default.nix
   ./services/networking/keybase.nix
   ./services/networking/kippo.nix
@@ -849,6 +852,7 @@
   ./services/networking/ucarp.nix
   ./services/networking/unbound.nix
   ./services/networking/unifi.nix
+  ./services/video/unifi-video.nix
   ./services/networking/v2ray.nix
   ./services/networking/vsftpd.nix
   ./services/networking/wakeonlan.nix
@@ -885,6 +889,7 @@
   ./services/security/fprot.nix
   ./services/security/haka.nix
   ./services/security/haveged.nix
+  ./services/security/hockeypuck.nix
   ./services/security/hologram-server.nix
   ./services/security/hologram-agent.nix
   ./services/security/munge.nix
@@ -970,6 +975,7 @@
   ./services/web-apps/trilium.nix
   ./services/web-apps/selfoss.nix
   ./services/web-apps/shiori.nix
+  ./services/web-apps/vikunja.nix
   ./services/web-apps/virtlyst.nix
   ./services/web-apps/wiki-js.nix
   ./services/web-apps/whitebophir.nix
@@ -1103,6 +1109,7 @@
   ./tasks/network-interfaces-systemd.nix
   ./tasks/network-interfaces-scripted.nix
   ./tasks/scsi-link-power-management.nix
+  ./tasks/snapraid.nix
   ./tasks/swraid.nix
   ./tasks/trackpoint.nix
   ./tasks/powertop.nix
diff --git a/nixpkgs/nixos/modules/programs/udevil.nix b/nixpkgs/nixos/modules/programs/udevil.nix
index ba5670f9dfe9..25975d88ec86 100644
--- a/nixpkgs/nixos/modules/programs/udevil.nix
+++ b/nixpkgs/nixos/modules/programs/udevil.nix
@@ -10,5 +10,8 @@ in {
 
   config = mkIf cfg.enable {
     security.wrappers.udevil.source = "${lib.getBin pkgs.udevil}/bin/udevil";
+
+    systemd.packages = [ pkgs.udevil ];
+    systemd.services."devmon@".wantedBy = [ "multi-user.target" ];
   };
 }
diff --git a/nixpkgs/nixos/modules/programs/xwayland.nix b/nixpkgs/nixos/modules/programs/xwayland.nix
index 7e9a424a7150..cb3c9c5b156c 100644
--- a/nixpkgs/nixos/modules/programs/xwayland.nix
+++ b/nixpkgs/nixos/modules/programs/xwayland.nix
@@ -10,14 +10,16 @@ in
 {
   options.programs.xwayland = {
 
-    enable = mkEnableOption ''
-      Xwayland X server allows running X programs on a Wayland compositor.
-    '';
+    enable = mkEnableOption "Xwayland (an X server for interfacing X11 apps with the Wayland protocol)";
 
     defaultFontPath = mkOption {
       type = types.str;
       default = optionalString config.fonts.fontDir.enable
         "/run/current-system/sw/share/X11/fonts";
+      defaultText = literalExample ''
+        optionalString config.fonts.fontDir.enable
+          "/run/current-system/sw/share/X11/fonts";
+      '';
       description = ''
         Default font path. Setting this option causes Xwayland to be rebuilt.
       '';
@@ -25,7 +27,15 @@ in
 
     package = mkOption {
       type = types.path;
-      description = "The Xwayland package";
+      default = pkgs.xwayland.override (oldArgs: {
+        inherit (cfg) defaultFontPath;
+      });
+      defaultText = literalExample ''
+        pkgs.xwayland.override (oldArgs: {
+          inherit (config.programs.xwayland) defaultFontPath;
+        });
+      '';
+      description = "The Xwayland package to use.";
     };
 
   };
@@ -37,9 +47,5 @@ in
 
     environment.systemPackages = [ cfg.package ];
 
-    programs.xwayland.package = pkgs.xwayland.override (oldArgs: {
-      inherit (cfg) defaultFontPath;
-    });
-
   };
 }
diff --git a/nixpkgs/nixos/modules/programs/zsh/zsh.nix b/nixpkgs/nixos/modules/programs/zsh/zsh.nix
index 48638fda28da..6c824a692b75 100644
--- a/nixpkgs/nixos/modules/programs/zsh/zsh.nix
+++ b/nixpkgs/nixos/modules/programs/zsh/zsh.nix
@@ -53,7 +53,7 @@ in
       };
 
       shellAliases = mkOption {
-        default = {};
+        default = { };
         description = ''
           Set of aliases for zsh shell, which overrides <option>environment.shellAliases</option>.
           See <option>environment.shellAliases</option> for an option format description.
@@ -118,7 +118,9 @@ in
       setOptions = mkOption {
         type = types.listOf types.str;
         default = [
-          "HIST_IGNORE_DUPS" "SHARE_HISTORY" "HIST_FCNTL_LOCK"
+          "HIST_IGNORE_DUPS"
+          "SHARE_HISTORY"
+          "HIST_FCNTL_LOCK"
         ];
         example = [ "EXTENDED_HISTORY" "RM_STAR_WAIT" ];
         description = ''
@@ -278,15 +280,29 @@ in
 
     environment.etc.zinputrc.source = ./zinputrc;
 
-    environment.systemPackages = [ pkgs.zsh ]
-      ++ optional cfg.enableCompletion pkgs.nix-zsh-completions;
+    environment.systemPackages =
+      let
+        completions =
+          if lib.versionAtLeast (lib.getVersion config.nix.package) "2.4pre"
+          then
+            pkgs.nix-zsh-completions.overrideAttrs
+              (_: {
+                postInstall = ''
+                  rm $out/share/zsh/site-functions/_nix
+                '';
+              })
+          else pkgs.nix-zsh-completions;
+      in
+      [ pkgs.zsh ]
+      ++ optional cfg.enableCompletion completions;
 
     environment.pathsToLink = optional cfg.enableCompletion "/share/zsh";
 
     #users.defaultUserShell = mkDefault "/run/current-system/sw/bin/zsh";
 
     environment.shells =
-      [ "/run/current-system/sw/bin/zsh"
+      [
+        "/run/current-system/sw/bin/zsh"
         "${pkgs.zsh}/bin/zsh"
       ];
 
diff --git a/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix b/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix
index 9da2d522a68d..f658eb756f7b 100644
--- a/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix
+++ b/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix
@@ -14,15 +14,21 @@ let
 
       requires = [ "postgresql.service" ];
 
+      path = [ pkgs.coreutils pkgs.gzip config.services.postgresql.package ];
+
       script = ''
+        set -e -o pipefail
+
         umask 0077 # ensure backup is only readable by postgres user
 
         if [ -e ${cfg.location}/${db}.sql.gz ]; then
-          ${pkgs.coreutils}/bin/mv ${cfg.location}/${db}.sql.gz ${cfg.location}/${db}.prev.sql.gz
+          mv ${cfg.location}/${db}.sql.gz ${cfg.location}/${db}.prev.sql.gz
         fi
 
         ${dumpCmd} | \
-          ${pkgs.gzip}/bin/gzip -c > ${cfg.location}/${db}.sql.gz
+          gzip -c > ${cfg.location}/${db}.in-progress.sql.gz
+
+        mv ${cfg.location}/${db}.in-progress.sql.gz ${cfg.location}/${db}.sql.gz
       '';
 
       serviceConfig = {
@@ -113,12 +119,12 @@ in {
     })
     (mkIf (cfg.enable && cfg.backupAll) {
       systemd.services.postgresqlBackup =
-        postgresqlBackupService "all" "${config.services.postgresql.package}/bin/pg_dumpall";
+        postgresqlBackupService "all" "pg_dumpall";
     })
     (mkIf (cfg.enable && !cfg.backupAll) {
       systemd.services = listToAttrs (map (db:
         let
-          cmd = "${config.services.postgresql.package}/bin/pg_dump ${cfg.pgdumpOptions} ${db}";
+          cmd = "pg_dump ${cfg.pgdumpOptions} ${db}";
         in {
           name = "postgresqlBackup-${db}";
           value = postgresqlBackupService db cmd;
diff --git a/nixpkgs/nixos/modules/services/backup/sanoid.nix b/nixpkgs/nixos/modules/services/backup/sanoid.nix
index be44a43b6d3f..abc4def1c61f 100644
--- a/nixpkgs/nixos/modules/services/backup/sanoid.nix
+++ b/nixpkgs/nixos/modules/services/backup/sanoid.nix
@@ -108,8 +108,8 @@ in {
         type = types.attrsOf (types.submodule ({config, options, ...}: {
           freeformType = datasetSettingsType;
           options = commonOptions // datasetOptions;
-          config.use_template = mkAliasDefinitions (options.useTemplate or {});
-          config.process_children_only = mkAliasDefinitions (options.processChildrenOnly or {});
+          config.use_template = mkAliasDefinitions (mkDefault options.useTemplate or {});
+          config.process_children_only = mkAliasDefinitions (mkDefault options.processChildrenOnly or {});
         }));
         default = {};
         description = "Datasets to snapshot.";
diff --git a/nixpkgs/nixos/modules/services/cluster/k3s/default.nix b/nixpkgs/nixos/modules/services/cluster/k3s/default.nix
index 300c182406ce..e5c51441690a 100644
--- a/nixpkgs/nixos/modules/services/cluster/k3s/default.nix
+++ b/nixpkgs/nixos/modules/services/cluster/k3s/default.nix
@@ -35,10 +35,20 @@ in
 
     token = mkOption {
       type = types.str;
-      description = "The k3s token to use when connecting to the server. This option only makes sense for an agent.";
+      description = ''
+        The k3s token to use when connecting to the server. This option only makes sense for an agent.
+        WARNING: This option will expose store your token unencrypted world-readable in the nix store.
+        If this is undesired use the tokenFile option instead.
+      '';
       default = "";
     };
 
+    tokenFile = mkOption {
+      type = types.nullOr types.path;
+      description = "File path containing k3s token to use when connecting to the server. This option only makes sense for an agent.";
+      default = null;
+    };
+
     docker = mkOption {
       type = types.bool;
       default = false;
@@ -57,6 +67,12 @@ in
       default = false;
       description = "Only run the server. This option only makes sense for a server.";
     };
+
+    configPath = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = "File path containing the k3s YAML config. This is useful when the config is generated (for example on boot).";
+    };
   };
 
   # implementation
@@ -64,12 +80,12 @@ in
   config = mkIf cfg.enable {
     assertions = [
       {
-        assertion = cfg.role == "agent" -> cfg.serverAddr != "";
-        message = "serverAddr should be set if role is 'agent'";
+        assertion = cfg.role == "agent" -> (cfg.configPath != null || cfg.serverAddr != "");
+        message = "serverAddr or configPath (with 'server' key) should be set if role is 'agent'";
       }
       {
-        assertion = cfg.role == "agent" -> cfg.token != "";
-        message = "token should be set if role is 'agent'";
+        assertion = cfg.role == "agent" -> cfg.configPath != null || cfg.tokenFile != null || cfg.token != "";
+        message = "token or tokenFile or configPath (with 'token' or 'token-file' keys) should be set if role is 'agent'";
       }
     ];
 
@@ -105,7 +121,10 @@ in
             "${cfg.package}/bin/k3s ${cfg.role}"
           ] ++ (optional cfg.docker "--docker")
           ++ (optional cfg.disableAgent "--disable-agent")
-          ++ (optional (cfg.role == "agent") "--server ${cfg.serverAddr} --token ${cfg.token}")
+          ++ (optional (cfg.serverAddr != "") "--server ${cfg.serverAddr}")
+          ++ (optional (cfg.token != "") "--token ${cfg.token}")
+          ++ (optional (cfg.tokenFile != null) "--token-file ${cfg.tokenFile}")
+          ++ (optional (cfg.configPath != null) "--config ${cfg.configPath}")
           ++ [ cfg.extraFlags ]
         );
       };
diff --git a/nixpkgs/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix b/nixpkgs/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix
index 2f0b573e8721..70d85a97f3b7 100644
--- a/nixpkgs/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix
+++ b/nixpkgs/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix
@@ -105,7 +105,7 @@ let
       pkgs.stdenv.mkDerivation {
         name = "hercules-ci-check-system-nix-src";
         inherit (config.nix.package) src patches;
-        configurePhase = ":";
+        dontConfigure = true;
         buildPhase = ''
           echo "Checking in-memory pathInfoCache expiry"
           if ! grep 'PathInfoCacheValue' src/libstore/store-api.hh >/dev/null; then
diff --git a/nixpkgs/nixos/modules/services/hardware/ddccontrol.nix b/nixpkgs/nixos/modules/services/hardware/ddccontrol.nix
new file mode 100644
index 000000000000..766bf12ee9f0
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/hardware/ddccontrol.nix
@@ -0,0 +1,36 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+
+let
+  cfg = config.services.ddccontrol;
+in
+
+{
+  ###### interface
+
+  options = {
+    services.ddccontrol = {
+      enable = lib.mkEnableOption "ddccontrol for controlling displays";
+    };
+  };
+
+  ###### implementation
+
+  config = lib.mkIf cfg.enable {
+    # Give users access to the "gddccontrol" tool
+    environment.systemPackages = [
+      pkgs.ddccontrol
+    ];
+
+    services.dbus.packages = [
+      pkgs.ddccontrol
+    ];
+
+    systemd.packages = [
+      pkgs.ddccontrol
+    ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix b/nixpkgs/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix
index 556f6bbb419a..9d083a615a2c 100644
--- a/nixpkgs/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix
+++ b/nixpkgs/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix
@@ -54,8 +54,7 @@ stdenv.mkDerivation {
     ${addAllNetDev netDevices}
   '';
 
-  installPhase = ":";
-
+  dontInstall = true;
   dontStrip = true;
   dontPatchELF = true;
 
diff --git a/nixpkgs/nixos/modules/services/misc/clipcat.nix b/nixpkgs/nixos/modules/services/misc/clipcat.nix
new file mode 100644
index 000000000000..128bb9a89d69
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/clipcat.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.clipcat;
+in {
+
+  options.services.clipcat= {
+    enable = mkEnableOption "Clipcat clipboard daemon";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.clipcat;
+      defaultText = "pkgs.clipcat";
+      description = "clipcat derivation to use.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.user.services.clipcat = {
+      enable      = true;
+      description = "clipcat daemon";
+      wantedBy = [ "graphical-session.target" ];
+      after    = [ "graphical-session.target" ];
+      serviceConfig.ExecStart = "${cfg.package}/bin/clipcatd --no-daemon";
+    };
+
+    environment.systemPackages = [ cfg.package ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/gitlab.nix b/nixpkgs/nixos/modules/services/misc/gitlab.nix
index 40e35ed1cd16..1514cc0665df 100644
--- a/nixpkgs/nixos/modules/services/misc/gitlab.nix
+++ b/nixpkgs/nixos/modules/services/misc/gitlab.nix
@@ -911,7 +911,7 @@ in {
       }
       {
         assertion = versionAtLeast postgresqlPackage.version "12.0.0";
-        message = "PostgreSQL >=12 is required to run GitLab 14.";
+        message = "PostgreSQL >=12 is required to run GitLab 14. Follow the instructions in the manual section for upgrading PostgreSQL here: https://nixos.org/manual/nixos/stable/index.html#module-services-postgres-upgrading";
       }
     ];
 
diff --git a/nixpkgs/nixos/modules/services/misc/home-assistant.nix b/nixpkgs/nixos/modules/services/misc/home-assistant.nix
index d68d7b05c173..dcd825bba433 100644
--- a/nixpkgs/nixos/modules/services/misc/home-assistant.nix
+++ b/nixpkgs/nixos/modules/services/misc/home-assistant.nix
@@ -313,6 +313,7 @@ in {
           "w800rf32"
           "xbee"
           "zha"
+          "zwave"
         ];
       in {
         ExecStart = "${package}/bin/hass --runner --config '${cfg.configDir}'";
diff --git a/nixpkgs/nixos/modules/services/misc/klipper.nix b/nixpkgs/nixos/modules/services/misc/klipper.nix
index 2f04c011a650..4930648ba8e3 100644
--- a/nixpkgs/nixos/modules/services/misc/klipper.nix
+++ b/nixpkgs/nixos/modules/services/misc/klipper.nix
@@ -2,7 +2,6 @@
 with lib;
 let
   cfg = config.services.klipper;
-  package = pkgs.klipper;
   format = pkgs.formats.ini { mkKeyValue = generators.mkKeyValueDefault {} ":"; };
 in
 {
@@ -11,12 +10,51 @@ in
     services.klipper = {
       enable = mkEnableOption "Klipper, the 3D printer firmware";
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.klipper;
+        description = "The Klipper package.";
+      };
+
+      inputTTY = mkOption {
+        type = types.path;
+        default = "/run/klipper/tty";
+        description = "Path of the virtual printer symlink to create.";
+      };
+
+      apiSocket = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/run/klipper/api";
+        description = "Path of the API socket to create.";
+      };
+
       octoprintIntegration = mkOption {
         type = types.bool;
         default = false;
         description = "Allows Octoprint to control Klipper.";
       };
 
+      user = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          User account under which Klipper runs.
+
+          If null is specified (default), a temporary user will be created by systemd.
+        '';
+      };
+
+      group = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Group account under which Klipper runs.
+
+          If null is specified (default), a temporary user will be created by systemd.
+        '';
+      };
+
       settings = mkOption {
         type = format.type;
         default = { };
@@ -30,26 +68,40 @@ in
 
   ##### implementation
   config = mkIf cfg.enable {
-    assertions = [{
-      assertion = cfg.octoprintIntegration -> config.services.octoprint.enable;
-      message = "Option klipper.octoprintIntegration requires Octoprint to be enabled on this system. Please enable services.octoprint to use it.";
-    }];
+    assertions = [
+      {
+        assertion = cfg.octoprintIntegration -> config.services.octoprint.enable;
+        message = "Option klipper.octoprintIntegration requires Octoprint to be enabled on this system. Please enable services.octoprint to use it.";
+      }
+      {
+        assertion = cfg.user != null -> cfg.group != null;
+        message = "Option klipper.group is not set when a user is specified.";
+      }
+    ];
 
     environment.etc."klipper.cfg".source = format.generate "klipper.cfg" cfg.settings;
 
-    systemd.services.klipper = {
+    services.klipper = mkIf cfg.octoprintIntegration {
+      user = config.services.octoprint.user;
+      group = config.services.octoprint.group;
+    };
+
+    systemd.services.klipper = let
+      klippyArgs = "--input-tty=${cfg.inputTTY}"
+        + optionalString (cfg.apiSocket != null) " --api-server=${cfg.apiSocket}";
+    in {
       description = "Klipper 3D Printer Firmware";
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
 
       serviceConfig = {
-        ExecStart = "${package}/lib/klipper/klippy.py --input-tty=/run/klipper/tty /etc/klipper.cfg";
+        ExecStart = "${cfg.package}/lib/klipper/klippy.py ${klippyArgs} /etc/klipper.cfg";
         RuntimeDirectory = "klipper";
         SupplementaryGroups = [ "dialout" ];
-        WorkingDirectory = "${package}/lib";
-      } // (if cfg.octoprintIntegration then {
-        Group = config.services.octoprint.group;
-        User = config.services.octoprint.user;
+        WorkingDirectory = "${cfg.package}/lib";
+      } // (if cfg.user != null then {
+        Group = cfg.group;
+        User = cfg.user;
       } else {
         DynamicUser = true;
         User = "klipper";
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix
index 8fe689ef3dbb..3be247ffb24e 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix
@@ -323,15 +323,13 @@ let
               HTTP username
             '';
           };
-          password = mkOption {
-            type = types.str;
-            description = ''
-              HTTP password
-            '';
-          };
+          password = mkOpt types.str "HTTP password";
+          password_file = mkOpt types.str "HTTP password file";
         };
       }) ''
-        Optional http login credentials for metrics scraping.
+        Sets the `Authorization` header on every scrape request with the
+        configured username and password.
+        password and password_file are mutually exclusive.
       '';
 
       bearer_token = mkOpt types.str ''
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
index 46015c9ec1ef..d648de6a4148 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -27,6 +27,7 @@ let
     "bird"
     "bitcoin"
     "blackbox"
+    "buildkite-agent"
     "collectd"
     "dnsmasq"
     "domain"
@@ -180,7 +181,7 @@ let
         serviceConfig.PrivateTmp = mkDefault true;
         serviceConfig.WorkingDirectory = mkDefault /tmp;
         serviceConfig.DynamicUser = mkDefault enableDynamicUser;
-        serviceConfig.User = conf.user;
+        serviceConfig.User = mkDefault conf.user;
         serviceConfig.Group = conf.group;
       } serviceOpts ]);
   };
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/buildkite-agent.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/buildkite-agent.nix
new file mode 100644
index 000000000000..7557480ac062
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/buildkite-agent.nix
@@ -0,0 +1,64 @@
+{ config, lib, pkgs, options }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.buildkite-agent;
+in
+{
+  port = 9876;
+  extraOpts = {
+    tokenPath = mkOption {
+      type = types.nullOr types.path;
+      apply = final: if final == null then null else toString final;
+      description = ''
+        The token from your Buildkite "Agents" page.
+
+        A run-time path to the token file, which is supposed to be provisioned
+        outside of Nix store.
+      '';
+    };
+    interval = mkOption {
+      type = types.str;
+      default = "30s";
+      example = "1min";
+      description = ''
+        How often to update metrics.
+      '';
+    };
+    endpoint = mkOption {
+      type = types.str;
+      default = "https://agent.buildkite.com/v3";
+      description = ''
+        The Buildkite Agent API endpoint.
+      '';
+    };
+    queues = mkOption {
+      type = with types; nullOr (listOf str);
+      default = null;
+      example = literalExample ''[ "my-queue1" "my-queue2" ]'';
+      description = ''
+        Which specific queues to process.
+      '';
+    };
+  };
+  serviceOpts = {
+    script =
+      let
+        queues = concatStringsSep " " (map (q: "-queue ${q}") cfg.queues);
+      in
+      ''
+        export BUILDKITE_AGENT_TOKEN="$(cat ${toString cfg.tokenPath})"
+        exec ${pkgs.buildkite-agent-metrics}/bin/buildkite-agent-metrics \
+          -backend prometheus \
+          -interval ${cfg.interval} \
+          -endpoint ${cfg.endpoint} \
+          ${optionalString (cfg.queues != null) queues} \
+          -prometheus-addr "${cfg.listenAddress}:${toString cfg.port}" ${concatStringsSep " " cfg.extraFlags}
+      '';
+    serviceConfig = {
+      DynamicUser = false;
+      RuntimeDirectory = "buildkite-agent-metrics";
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/kea.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/kea.nix
index b6cd89c3866b..9677281f8772 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/kea.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/kea.nix
@@ -26,6 +26,7 @@ in {
   };
   serviceOpts = {
     serviceConfig = {
+      User = "kea";
       ExecStart = ''
         ${pkgs.prometheus-kea-exporter}/bin/kea-exporter \
           --address ${cfg.listenAddress} \
diff --git a/nixpkgs/nixos/modules/services/monitoring/telegraf.nix b/nixpkgs/nixos/modules/services/monitoring/telegraf.nix
index bc30ca3b77cf..4046260c1649 100644
--- a/nixpkgs/nixos/modules/services/monitoring/telegraf.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/telegraf.nix
@@ -25,10 +25,9 @@ in {
         default = [];
         example = "/run/keys/telegraf.env";
         description = ''
-          File to load as environment file. Environment variables
-          from this file will be interpolated into the config file
-          using envsubst with this syntax:
-          <literal>$ENVIRONMENT ''${VARIABLE}</literal>
+          File to load as environment file. Environment variables from this file
+          will be interpolated into the config file using envsubst with this
+          syntax: <literal>$ENVIRONMENT</literal> or <literal>''${VARIABLE}</literal>.
           This is useful to avoid putting secrets into the nix store.
         '';
       };
@@ -73,6 +72,7 @@ in {
         ExecReload="${pkgs.coreutils}/bin/kill -HUP $MAINPID";
         RuntimeDirectory = "telegraf";
         User = "telegraf";
+        Group = "telegraf";
         Restart = "on-failure";
         # for ping probes
         AmbientCapabilities = [ "CAP_NET_RAW" ];
@@ -81,7 +81,10 @@ in {
 
     users.users.telegraf = {
       uid = config.ids.uids.telegraf;
+      group = "telegraf";
       description = "telegraf daemon user";
     };
+
+    users.groups.telegraf = {};
   };
 }
diff --git a/nixpkgs/nixos/modules/services/networking/bind.nix b/nixpkgs/nixos/modules/services/networking/bind.nix
index 20eef2c3455b..480d5a184f25 100644
--- a/nixpkgs/nixos/modules/services/networking/bind.nix
+++ b/nixpkgs/nixos/modules/services/networking/bind.nix
@@ -6,6 +6,8 @@ let
 
   cfg = config.services.bind;
 
+  bindPkg = config.services.bind.package;
+
   bindUser = "named";
 
   bindZoneCoerce = list: builtins.listToAttrs (lib.forEach list (zone: { name = zone.name; value = zone; }));
@@ -59,7 +61,7 @@ let
         blackhole { badnetworks; };
         forward first;
         forwarders { ${concatMapStrings (entry: " ${entry}; ") cfg.forwarders} };
-        directory "/run/named";
+        directory "${cfg.directory}";
         pid-file "/run/named/named.pid";
         ${cfg.extraOptions}
       };
@@ -104,6 +106,14 @@ in
 
       enable = mkEnableOption "BIND domain name server";
 
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.bind;
+        defaultText = "pkgs.bind";
+        description = "The BIND package to use.";
+      };
+
       cacheNetworks = mkOption {
         default = [ "127.0.0.0/24" ];
         type = types.listOf types.str;
@@ -156,6 +166,12 @@ in
         ";
       };
 
+      directory = mkOption {
+        type = types.str;
+        default = "/run/named";
+        description = "Working directory of BIND.";
+      };
+
       zones = mkOption {
         default = [ ];
         type = with types; coercedTo (listOf attrs) bindZoneCoerce (attrsOf (types.submodule bindZoneOptions));
@@ -225,17 +241,20 @@ in
       preStart = ''
         mkdir -m 0755 -p /etc/bind
         if ! [ -f "/etc/bind/rndc.key" ]; then
-          ${pkgs.bind.out}/sbin/rndc-confgen -c /etc/bind/rndc.key -u ${bindUser} -a -A hmac-sha256 2>/dev/null
+          ${bindPkg.out}/sbin/rndc-confgen -c /etc/bind/rndc.key -u ${bindUser} -a -A hmac-sha256 2>/dev/null
         fi
 
         ${pkgs.coreutils}/bin/mkdir -p /run/named
         chown ${bindUser} /run/named
+
+        ${pkgs.coreutils}/bin/mkdir -p ${cfg.directory}
+        chown ${bindUser} ${cfg.directory}
       '';
 
       serviceConfig = {
-        ExecStart = "${pkgs.bind.out}/sbin/named -u ${bindUser} ${optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile} -f";
-        ExecReload = "${pkgs.bind.out}/sbin/rndc -k '/etc/bind/rndc.key' reload";
-        ExecStop = "${pkgs.bind.out}/sbin/rndc -k '/etc/bind/rndc.key' stop";
+        ExecStart = "${bindPkg.out}/sbin/named -u ${bindUser} ${optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile} -f";
+        ExecReload = "${bindPkg.out}/sbin/rndc -k '/etc/bind/rndc.key' reload";
+        ExecStop = "${bindPkg.out}/sbin/rndc -k '/etc/bind/rndc.key' stop";
       };
 
       unitConfig.Documentation = "man:named(8)";
diff --git a/nixpkgs/nixos/modules/services/networking/corerad.nix b/nixpkgs/nixos/modules/services/networking/corerad.nix
index 4acdd1d69cc4..e76ba9a2d00d 100644
--- a/nixpkgs/nixos/modules/services/networking/corerad.nix
+++ b/nixpkgs/nixos/modules/services/networking/corerad.nix
@@ -37,7 +37,7 @@ in {
         }
       '';
       description = ''
-        Configuration for CoreRAD, see <link xlink:href="https://github.com/mdlayher/corerad/blob/master/internal/config/default.toml"/>
+        Configuration for CoreRAD, see <link xlink:href="https://github.com/mdlayher/corerad/blob/main/internal/config/reference.toml"/>
         for supported values. Ignored if configFile is set.
       '';
     };
diff --git a/nixpkgs/nixos/modules/services/networking/ddclient.nix b/nixpkgs/nixos/modules/services/networking/ddclient.nix
index 053efe712709..7820eedd9327 100644
--- a/nixpkgs/nixos/modules/services/networking/ddclient.nix
+++ b/nixpkgs/nixos/modules/services/networking/ddclient.nix
@@ -18,6 +18,7 @@ let
     ${lib.optionalString (cfg.zone != "")   "zone=${cfg.zone}"}
     ssl=${boolToStr cfg.ssl}
     wildcard=YES
+    ipv6=${boolToStr cfg.ipv6}
     quiet=${boolToStr cfg.quiet}
     verbose=${boolToStr cfg.verbose}
     ${cfg.extraConfig}
@@ -116,7 +117,15 @@ with lib;
         default = true;
         type = bool;
         description = ''
-          Whether to use to use SSL/TLS to connect to dynamic DNS provider.
+          Whether to use SSL/TLS to connect to dynamic DNS provider.
+        '';
+      };
+
+      ipv6 = mkOption {
+        default = false;
+        type = bool;
+        description = ''
+          Whether to use IPv6.
         '';
       };
 
diff --git a/nixpkgs/nixos/modules/services/networking/kea.nix b/nixpkgs/nixos/modules/services/networking/kea.nix
new file mode 100644
index 000000000000..72773b83a496
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/kea.nix
@@ -0,0 +1,361 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+
+with lib;
+
+let
+  cfg = config.services.kea;
+
+  format = pkgs.formats.json {};
+
+  ctrlAgentConfig = format.generate "kea-ctrl-agent.conf" {
+    Control-agent = cfg.ctrl-agent.settings;
+  };
+  dhcp4Config = format.generate "kea-dhcp4.conf" {
+    Dhcp4 = cfg.dhcp4.settings;
+  };
+  dhcp6Config = format.generate "kea-dhcp6.conf" {
+    Dhcp6 = cfg.dhcp6.settings;
+  };
+  dhcpDdnsConfig = format.generate "kea-dhcp-ddns.conf" {
+    DhcpDdns = cfg.dhcp-ddns.settings;
+  };
+
+  package = pkgs.kea;
+in
+{
+  options.services.kea = with types; {
+    ctrl-agent = mkOption {
+      description = ''
+        Kea Control Agent configuration
+      '';
+      default = {};
+      type = submodule {
+        options = {
+          enable = mkEnableOption "Kea Control Agent";
+
+          extraArgs = mkOption {
+            type = listOf str;
+            default = [];
+            description = ''
+              List of additonal arguments to pass to the daemon.
+            '';
+          };
+
+          settings = mkOption {
+            type = format.type;
+            default = null;
+            description = ''
+              Kea Control Agent configuration as an attribute set, see <link xlink:href="https://kea.readthedocs.io/en/kea-${package.version}/arm/agent.html"/>.
+            '';
+          };
+        };
+      };
+    };
+
+    dhcp4 = mkOption {
+      description = ''
+        DHCP4 Server configuration
+      '';
+      default = {};
+      type = submodule {
+        options = {
+          enable = mkEnableOption "Kea DHCP4 server";
+
+          extraArgs = mkOption {
+            type = listOf str;
+            default = [];
+            description = ''
+              List of additonal arguments to pass to the daemon.
+            '';
+          };
+
+          settings = mkOption {
+            type = format.type;
+            default = null;
+            example = {
+              valid-lifetime = 4000;
+              renew-timer = 1000;
+              rebind-timer = 2000;
+              interfaces-config = {
+                interfaces = [
+                  "eth0"
+                ];
+              };
+              lease-database = {
+                type = "memfile";
+                persist = true;
+                name = "/var/lib/kea/dhcp4.leases";
+              };
+              subnet4 = [ {
+                subnet = "192.0.2.0/24";
+                pools = [ {
+                  pool = "192.0.2.100 - 192.0.2.240";
+                } ];
+              } ];
+            };
+            description = ''
+              Kea DHCP4 configuration as an attribute set, see <link xlink:href="https://kea.readthedocs.io/en/kea-${package.version}/arm/dhcp4-srv.html"/>.
+            '';
+          };
+        };
+      };
+    };
+
+    dhcp6 = mkOption {
+      description = ''
+        DHCP6 Server configuration
+      '';
+      default = {};
+      type = submodule {
+        options = {
+          enable = mkEnableOption "Kea DHCP6 server";
+
+          extraArgs = mkOption {
+            type = listOf str;
+            default = [];
+            description = ''
+              List of additonal arguments to pass to the daemon.
+            '';
+          };
+
+          settings = mkOption {
+            type = format.type;
+            default = null;
+            example = {
+              valid-lifetime = 4000;
+              renew-timer = 1000;
+              rebind-timer = 2000;
+              preferred-lifetime = 3000;
+              interfaces-config = {
+                interfaces = [
+                  "eth0"
+                ];
+              };
+              lease-database = {
+                type = "memfile";
+                persist = true;
+                name = "/var/lib/kea/dhcp6.leases";
+              };
+              subnet6 = [ {
+                subnet = "2001:db8:1::/64";
+                pools = [ {
+                  pool = "2001:db8:1::1-2001:db8:1::ffff";
+                } ];
+              } ];
+            };
+            description = ''
+              Kea DHCP6 configuration as an attribute set, see <link xlink:href="https://kea.readthedocs.io/en/kea-${package.version}/arm/dhcp6-srv.html"/>.
+            '';
+          };
+        };
+      };
+    };
+
+    dhcp-ddns = mkOption {
+      description = ''
+        Kea DHCP-DDNS configuration
+      '';
+      default = {};
+      type = submodule {
+        options = {
+          enable = mkEnableOption "Kea DDNS server";
+
+          extraArgs = mkOption {
+            type = listOf str;
+            default = [];
+            description = ''
+              List of additonal arguments to pass to the daemon.
+            '';
+          };
+
+          settings = mkOption {
+            type = format.type;
+            default = null;
+            example = {
+              ip-address = "127.0.0.1";
+              port = 53001;
+              dns-server-timeout = 100;
+              ncr-protocol = "UDP";
+              ncr-format = "JSON";
+              tsig-keys = [ ];
+              forward-ddns = {
+                ddns-domains = [ ];
+              };
+              reverse-ddns = {
+                ddns-domains = [ ];
+              };
+            };
+            description = ''
+              Kea DHCP-DDNS configuration as an attribute set, see <link xlink:href="https://kea.readthedocs.io/en/kea-${package.version}/arm/ddns.html"/>.
+            '';
+          };
+        };
+      };
+    };
+  };
+
+  config = let
+    commonServiceConfig = {
+      ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      DynamicUser = true;
+      User = "kea";
+      ConfigurationDirectory = "kea";
+      RuntimeDirectory = "kea";
+      StateDirectory = "kea";
+      UMask = "0077";
+    };
+  in mkIf (cfg.ctrl-agent.enable || cfg.dhcp4.enable || cfg.dhcp6.enable || cfg.dhcp-ddns.enable) (mkMerge [
+  {
+    environment.systemPackages = [ package ];
+  }
+
+  (mkIf cfg.ctrl-agent.enable {
+
+    environment.etc."kea/ctrl-agent.conf".source = ctrlAgentConfig;
+
+    systemd.services.kea-ctrl-agent = {
+      description = "Kea Control Agent";
+      documentation = [
+        "man:kea-ctrl-agent(8)"
+        "https://kea.readthedocs.io/en/kea-${package.version}/arm/agent.html"
+      ];
+
+      after = [
+        "network-online.target"
+        "time-sync.target"
+      ];
+      wantedBy = [
+        "kea-dhcp4-server.service"
+        "kea-dhcp6-server.service"
+        "kea-dhcp-ddns-server.service"
+      ];
+
+      environment = {
+        KEA_PIDFILE_DIR = "/run/kea";
+      };
+
+      serviceConfig = {
+        ExecStart = "${package}/bin/kea-ctrl-agent -c /etc/kea/ctrl-agent.conf ${lib.escapeShellArgs cfg.dhcp4.extraArgs}";
+        KillMode = "process";
+        Restart = "on-failure";
+      } // commonServiceConfig;
+    };
+  })
+
+  (mkIf cfg.dhcp4.enable {
+
+    environment.etc."kea/dhcp4-server.conf".source = dhcp4Config;
+
+    systemd.services.kea-dhcp4-server = {
+      description = "Kea DHCP4 Server";
+      documentation = [
+        "man:kea-dhcp4(8)"
+        "https://kea.readthedocs.io/en/kea-${package.version}/arm/dhcp4-srv.html"
+      ];
+
+      after = [
+        "network-online.target"
+        "time-sync.target"
+      ];
+      wantedBy = [
+        "multi-user.target"
+      ];
+
+      environment = {
+        KEA_PIDFILE_DIR = "/run/kea";
+      };
+
+      serviceConfig = {
+        ExecStart = "${package}/bin/kea-dhcp4 -c /etc/kea/dhcp4-server.conf ${lib.escapeShellArgs cfg.dhcp4.extraArgs}";
+        # Kea does not request capabilities by itself
+        AmbientCapabilities = [
+          "CAP_NET_BIND_SERVICE"
+          "CAP_NET_RAW"
+        ];
+        CapabilityBoundingSet = [
+          "CAP_NET_BIND_SERVICE"
+          "CAP_NET_RAW"
+        ];
+      } // commonServiceConfig;
+    };
+  })
+
+  (mkIf cfg.dhcp6.enable {
+
+    environment.etc."kea/dhcp6-server.conf".source = dhcp6Config;
+
+    systemd.services.kea-dhcp6-server = {
+      description = "Kea DHCP6 Server";
+      documentation = [
+        "man:kea-dhcp6(8)"
+        "https://kea.readthedocs.io/en/kea-${package.version}/arm/dhcp6-srv.html"
+      ];
+
+      after = [
+        "network-online.target"
+        "time-sync.target"
+      ];
+      wantedBy = [
+        "multi-user.target"
+      ];
+
+      environment = {
+        KEA_PIDFILE_DIR = "/run/kea";
+      };
+
+      serviceConfig = {
+        ExecStart = "${package}/bin/kea-dhcp6 -c /etc/kea/dhcp6-server.conf ${lib.escapeShellArgs cfg.dhcp6.extraArgs}";
+        # Kea does not request capabilities by itself
+        AmbientCapabilities = [
+          "CAP_NET_BIND_SERVICE"
+        ];
+        CapabilityBoundingSet = [
+          "CAP_NET_BIND_SERVICE"
+        ];
+      } // commonServiceConfig;
+    };
+  })
+
+  (mkIf cfg.dhcp-ddns.enable {
+
+    environment.etc."kea/dhcp-ddns.conf".source = dhcpDdnsConfig;
+
+    systemd.services.kea-dhcp-ddns-server = {
+      description = "Kea DHCP-DDNS Server";
+      documentation = [
+        "man:kea-dhcp-ddns(8)"
+        "https://kea.readthedocs.io/en/kea-${package.version}/arm/ddns.html"
+      ];
+
+      after = [
+        "network-online.target"
+        "time-sync.target"
+      ];
+      wantedBy = [
+        "multi-user.target"
+      ];
+
+      environment = {
+        KEA_PIDFILE_DIR = "/run/kea";
+      };
+
+      serviceConfig = {
+        ExecStart = "${package}/bin/kea-dhcp-ddns -c /etc/kea/dhcp-ddns.conf ${lib.escapeShellArgs cfg.dhcp-ddns.extraArgs}";
+        AmbientCapabilites = [
+          "CAP_NET_BIND_SERVICE"
+        ];
+        CapabilityBoundingSet = [
+          "CAP_NET_BIND_SERVICE"
+        ];
+      } // commonServiceConfig;
+    };
+  })
+
+  ]);
+
+  meta.maintainers = with maintainers; [ hexa ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/matterbridge.nix b/nixpkgs/nixos/modules/services/networking/matterbridge.nix
index b8b4f37c84a8..9186eee26abf 100644
--- a/nixpkgs/nixos/modules/services/networking/matterbridge.nix
+++ b/nixpkgs/nixos/modules/services/networking/matterbridge.nix
@@ -38,8 +38,8 @@ in
           # Use services.matterbridge.configPath instead.
 
           [irc]
-              [irc.freenode]
-              Server="irc.freenode.net:6667"
+              [irc.libera]
+              Server="irc.libera.chat:6667"
               Nick="matterbot"
 
           [mattermost]
@@ -55,7 +55,7 @@ in
           name="gateway1"
           enable=true
               [[gateway.inout]]
-              account="irc.freenode"
+              account="irc.libera"
               channel="#testing"
 
               [[gateway.inout]]
diff --git a/nixpkgs/nixos/modules/services/networking/nix-serve.nix b/nixpkgs/nixos/modules/services/networking/nix-serve.nix
index b17f35c769bb..7fc145f2303d 100644
--- a/nixpkgs/nixos/modules/services/networking/nix-serve.nix
+++ b/nixpkgs/nixos/modules/services/networking/nix-serve.nix
@@ -69,13 +69,9 @@ in
         ExecStart = "${pkgs.nix-serve}/bin/nix-serve " +
           "--listen ${cfg.bindAddress}:${toString cfg.port} ${cfg.extraParams}";
         User = "nix-serve";
-        Group = "nogroup";
+        Group = "nix-serve";
+        DynamicUser = true;
       };
     };
-
-    users.users.nix-serve = {
-      description = "Nix-serve user";
-      uid = config.ids.uids.nix-serve;
-    };
   };
 }
diff --git a/nixpkgs/nixos/modules/services/networking/pppd.nix b/nixpkgs/nixos/modules/services/networking/pppd.nix
index c1cbdb461765..37f44f07ac46 100644
--- a/nixpkgs/nixos/modules/services/networking/pppd.nix
+++ b/nixpkgs/nixos/modules/services/networking/pppd.nix
@@ -82,13 +82,21 @@ in
           LD_PRELOAD = "${pkgs.libredirect}/lib/libredirect.so";
           NIX_REDIRECTS = "/var/run=/run/pppd";
         };
-        serviceConfig = {
+        serviceConfig = let
+          capabilities = [
+            "CAP_BPF"
+            "CAP_SYS_TTY_CONFIG"
+            "CAP_NET_ADMIN"
+            "CAP_NET_RAW"
+          ];
+        in
+        {
           ExecStart = "${getBin cfg.package}/sbin/pppd call ${peerCfg.name} nodetach nolog";
           Restart = "always";
           RestartSec = 5;
 
-          AmbientCapabilities = "CAP_SYS_TTY_CONFIG CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_ADMIN";
-          CapabilityBoundingSet = "CAP_SYS_TTY_CONFIG CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_ADMIN";
+          AmbientCapabilities = capabilities;
+          CapabilityBoundingSet = capabilities;
           KeyringMode = "private";
           LockPersonality = true;
           MemoryDenyWriteExecute = true;
@@ -103,7 +111,17 @@ in
           ProtectKernelTunables = false;
           ProtectSystem = "strict";
           RemoveIPC = true;
-          RestrictAddressFamilies = "AF_PACKET AF_UNIX AF_PPPOX AF_ATMPVC AF_ATMSVC AF_INET AF_INET6 AF_IPX";
+          RestrictAddressFamilies = [
+            "AF_ATMPVC"
+            "AF_ATMSVC"
+            "AF_INET"
+            "AF_INET6"
+            "AF_IPX"
+            "AF_NETLINK"
+            "AF_PACKET"
+            "AF_PPPOX"
+            "AF_UNIX"
+          ];
           RestrictNamespaces = true;
           RestrictRealtime = true;
           RestrictSUIDSGID = true;
diff --git a/nixpkgs/nixos/modules/services/networking/smartdns.nix b/nixpkgs/nixos/modules/services/networking/smartdns.nix
index f1888af70416..f84c727f0343 100644
--- a/nixpkgs/nixos/modules/services/networking/smartdns.nix
+++ b/nixpkgs/nixos/modules/services/networking/smartdns.nix
@@ -54,6 +54,7 @@ in {
 
     systemd.packages = [ pkgs.smartdns ];
     systemd.services.smartdns.wantedBy = [ "multi-user.target" ];
+    systemd.services.smartdns.restartTriggers = [ confFile ];
     environment.etc."smartdns/smartdns.conf".source = confFile;
     environment.etc."default/smartdns".source =
       "${pkgs.smartdns}/etc/default/smartdns";
diff --git a/nixpkgs/nixos/modules/services/networking/unbound.nix b/nixpkgs/nixos/modules/services/networking/unbound.nix
index 09aef9a1dcf1..6d7178047ea8 100644
--- a/nixpkgs/nixos/modules/services/networking/unbound.nix
+++ b/nixpkgs/nixos/modules/services/networking/unbound.nix
@@ -21,7 +21,15 @@ let
                                 ))
     else throw (traceSeq v "services.unbound.settings: unexpected type");
 
-  confFile = pkgs.writeText "unbound.conf" (concatStringsSep "\n" ((mapAttrsToList (toConf "") cfg.settings) ++ [""]));
+  confNoServer = concatStringsSep "\n" ((mapAttrsToList (toConf "") (builtins.removeAttrs cfg.settings [ "server" ])) ++ [""]);
+  confServer = concatStringsSep "\n" (mapAttrsToList (toConf "  ") (builtins.removeAttrs cfg.settings.server [ "define-tag" ]));
+
+  confFile = pkgs.writeText "unbound.conf" ''
+    server:
+    ${optionalString (cfg.settings.server.define-tag != "") (toOption "  " "define-tag" cfg.settings.server.define-tag)}
+    ${confServer}
+    ${confNoServer}
+  '';
 
   rootTrustAnchorFile = "${cfg.stateDir}/root.key";
 
@@ -170,6 +178,7 @@ in {
         # prevent race conditions on system startup when interfaces are not yet
         # configured
         ip-freebind = mkDefault true;
+        define-tag = mkDefault "";
       };
       remote-control = {
         control-enable = mkDefault false;
diff --git a/nixpkgs/nixos/modules/services/networking/znc/default.nix b/nixpkgs/nixos/modules/services/networking/znc/default.nix
index aa79ed27dcef..b872b99976ce 100644
--- a/nixpkgs/nixos/modules/services/networking/znc/default.nix
+++ b/nixpkgs/nixos/modules/services/networking/znc/default.nix
@@ -133,8 +133,8 @@ in
               Nick = "paul";
               AltNick = "paul1";
               LoadModule = [ "chansaver" "controlpanel" ];
-              Network.freenode = {
-                Server = "chat.freenode.net +6697";
+              Network.libera = {
+                Server = "irc.libera.chat +6697";
                 LoadModule = [ "simple_away" ];
                 Chan = {
                   "#nixos" = { Detached = false; };
diff --git a/nixpkgs/nixos/modules/services/networking/znc/options.nix b/nixpkgs/nixos/modules/services/networking/znc/options.nix
index 7a43b45fabba..be9dc78c86d9 100644
--- a/nixpkgs/nixos/modules/services/networking/znc/options.nix
+++ b/nixpkgs/nixos/modules/services/networking/znc/options.nix
@@ -11,7 +11,7 @@ let
 
       server = mkOption {
         type = types.str;
-        example = "chat.freenode.net";
+        example = "irc.libera.chat";
         description = ''
           IRC server address.
         '';
@@ -150,8 +150,8 @@ in
           '';
           example = literalExample ''
             {
-              "freenode" = {
-                server = "chat.freenode.net";
+              "libera" = {
+                server = "irc.libera.chat";
                 port = 6697;
                 useSSL = true;
                 modules = [ "simple_away" ];
diff --git a/nixpkgs/nixos/modules/services/security/hockeypuck.nix b/nixpkgs/nixos/modules/services/security/hockeypuck.nix
new file mode 100644
index 000000000000..686634c8add8
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/security/hockeypuck.nix
@@ -0,0 +1,104 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.hockeypuck;
+  settingsFormat = pkgs.formats.toml { };
+in {
+  meta.maintainers = with lib.maintainers; [ etu ];
+
+  options.services.hockeypuck = {
+    enable = lib.mkEnableOption "Hockeypuck OpenPGP Key Server";
+
+    port = lib.mkOption {
+      default = 11371;
+      type = lib.types.port;
+      description = "HKP port to listen on.";
+    };
+
+    settings = lib.mkOption {
+      type = settingsFormat.type;
+      default = { };
+      example = lib.literalExample ''
+        {
+          hockeypuck = {
+            loglevel = "INFO";
+            logfile = "/var/log/hockeypuck/hockeypuck.log";
+            indexTemplate = "''${pkgs.hockeypuck-web}/share/templates/index.html.tmpl";
+            vindexTemplate = "''${pkgs.hockeypuck-web}/share/templates/index.html.tmpl";
+            statsTemplate = "''${pkgs.hockeypuck-web}/share/templates/stats.html.tmpl";
+            webroot = "''${pkgs.hockeypuck-web}/share/webroot";
+
+            hkp.bind = ":''${toString cfg.port}";
+
+            openpgp.db = {
+              driver = "postgres-jsonb";
+              dsn = "database=hockeypuck host=/var/run/postgresql sslmode=disable";
+            };
+          };
+        }
+      '';
+      description = ''
+        Configuration file for hockeypuck, here you can override
+        certain settings (<literal>loglevel</literal> and
+        <literal>openpgp.db.dsn</literal>) by just setting those values.
+
+        For other settings you need to use lib.mkForce to override them.
+
+        This service doesn't provision or enable postgres on your
+        system, it rather assumes that you enable postgres and create
+        the database yourself.
+
+        Example:
+        <literal>
+          services.postgresql = {
+            enable = true;
+            ensureDatabases = [ "hockeypuck" ];
+            ensureUsers = [{
+              name = "hockeypuck";
+              ensurePermissions."DATABASE hockeypuck" = "ALL PRIVILEGES";
+            }];
+          };
+        </literal>
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    services.hockeypuck.settings.hockeypuck = {
+      loglevel = lib.mkDefault "INFO";
+      logfile = "/var/log/hockeypuck/hockeypuck.log";
+      indexTemplate = "${pkgs.hockeypuck-web}/share/templates/index.html.tmpl";
+      vindexTemplate = "${pkgs.hockeypuck-web}/share/templates/index.html.tmpl";
+      statsTemplate = "${pkgs.hockeypuck-web}/share/templates/stats.html.tmpl";
+      webroot = "${pkgs.hockeypuck-web}/share/webroot";
+
+      hkp.bind = ":${toString cfg.port}";
+
+      openpgp.db = {
+        driver = "postgres-jsonb";
+        dsn = lib.mkDefault "database=hockeypuck host=/var/run/postgresql sslmode=disable";
+      };
+    };
+
+    users.users.hockeypuck = {
+      isSystemUser = true;
+      description = "Hockeypuck user";
+    };
+
+    systemd.services.hockeypuck = {
+      description = "Hockeypuck OpenPGP Key Server";
+      after = [ "network.target" "postgresql.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        WorkingDirectory = "/var/lib/hockeypuck";
+        User = "hockeypuck";
+        ExecStart = "${pkgs.hockeypuck}/bin/hockeypuck -config ${settingsFormat.generate "config.toml" cfg.settings}";
+        Restart = "always";
+        RestartSec = "5s";
+        LogsDirectory = "hockeypuck";
+        LogsDirectoryMode = "0755";
+        StateDirectory = "hockeypuck";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix b/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix
index 940ac7832dae..d28ea61e66aa 100644
--- a/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix
+++ b/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix
@@ -26,12 +26,12 @@ let
         if value != null then [ (nameValuePair (nameToEnvVar name) (if isBool value then boolToString value else toString value)) ] else []
       ) cfg.config));
     in { DATA_FOLDER = "/var/lib/bitwarden_rs"; } // optionalAttrs (!(configEnv ? WEB_VAULT_ENABLED) || configEnv.WEB_VAULT_ENABLED == "true") {
-      WEB_VAULT_FOLDER = "${pkgs.vaultwarden-vault}/share/vaultwarden/vault";
+      WEB_VAULT_FOLDER = "${cfg.webVaultPackage}/share/vaultwarden/vault";
     } // configEnv;
 
   configFile = pkgs.writeText "vaultwarden.env" (concatStrings (mapAttrsToList (name: value: "${name}=${value}\n") configEnv));
 
-  vaultwarden = pkgs.vaultwarden.override { inherit (cfg) dbBackend; };
+  vaultwarden = cfg.package.override { inherit (cfg) dbBackend; };
 
 in {
   imports = [
@@ -102,6 +102,20 @@ in {
         <literal>vaultwarden</literal> is running.
       '';
     };
+
+    package = mkOption {
+      type = package;
+      default = pkgs.vaultwarden;
+      defaultText = "pkgs.vaultwarden";
+      description = "Vaultwarden package to use.";
+    };
+
+    webVaultPackage = mkOption {
+      type = package;
+      default = pkgs.vaultwarden-vault;
+      defaultText = "pkgs.vaultwarden-vault";
+      description = "Web vault package to use.";
+    };
   };
 
   config = mkIf cfg.enable {
diff --git a/nixpkgs/nixos/modules/services/ttys/getty.nix b/nixpkgs/nixos/modules/services/ttys/getty.nix
index 8345dfabeb7e..7cf2ff87da26 100644
--- a/nixpkgs/nixos/modules/services/ttys/getty.nix
+++ b/nixpkgs/nixos/modules/services/ttys/getty.nix
@@ -6,7 +6,7 @@ let
   cfg = config.services.getty;
 
   baseArgs = [
-    "--login-program" "${pkgs.shadow}/bin/login"
+    "--login-program" "${cfg.loginProgram}"
   ] ++ optionals (cfg.autologinUser != null) [
     "--autologin" cfg.autologinUser
   ] ++ optionals (cfg.loginOptions != null) [
@@ -39,6 +39,14 @@ in
         '';
       };
 
+      loginProgram = mkOption {
+        type = types.path;
+        default = "${pkgs.shadow}/bin/login";
+        description = ''
+          Path to the login binary executed by agetty.
+        '';
+      };
+
       loginOptions = mkOption {
         type = types.nullOr types.str;
         default = null;
diff --git a/nixpkgs/nixos/modules/services/video/unifi-video.nix b/nixpkgs/nixos/modules/services/video/unifi-video.nix
new file mode 100644
index 000000000000..d4c0268ed66c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/video/unifi-video.nix
@@ -0,0 +1,265 @@
+{ config, lib, pkgs, utils, ... }:
+with lib;
+let
+  cfg = config.services.unifi-video;
+  mainClass = "com.ubnt.airvision.Main";
+  cmd = ''
+    ${pkgs.jsvc}/bin/jsvc \
+    -cwd ${stateDir} \
+    -debug \
+    -verbose:class \
+    -nodetach \
+    -user unifi-video \
+    -home ${cfg.jrePackage}/lib/openjdk \
+    -cp ${pkgs.commonsDaemon}/share/java/commons-daemon-1.2.4.jar:${stateDir}/lib/airvision.jar \
+    -pidfile ${cfg.pidFile} \
+    -procname unifi-video \
+    -Djava.security.egd=file:/dev/./urandom \
+    -Xmx${cfg.maximumJavaHeapSize}M \
+    -Xss512K \
+    -XX:+UseG1GC \
+    -XX:+UseStringDeduplication \
+    -XX:MaxMetaspaceSize=768M \
+    -Djava.library.path=${stateDir}/lib \
+    -Djava.awt.headless=true \
+    -Djavax.net.ssl.trustStore=${stateDir}/etc/ufv-truststore \
+    -Dfile.encoding=UTF-8 \
+    -Dav.tempdir=/var/cache/unifi-video
+  '';
+
+  mongoConf = pkgs.writeTextFile {
+    name = "mongo.conf";
+    executable = false;
+    text = ''
+      # for documentation of all options, see http://docs.mongodb.org/manual/reference/configuration-options/
+
+      storage:
+         dbPath: ${cfg.dataDir}/db
+         journal:
+            enabled: true
+         syncPeriodSecs: 60
+
+      systemLog:
+         destination: file
+         logAppend: true
+         path: ${stateDir}/logs/mongod.log
+
+      net:
+         port: 7441
+         bindIp: 127.0.0.1
+         http:
+            enabled: false
+
+      operationProfiling:
+         slowOpThresholdMs: 500
+         mode: off
+    '';
+  };
+
+
+  mongoWtConf = pkgs.writeTextFile {
+    name = "mongowt.conf";
+    executable = false;
+    text = ''
+      # for documentation of all options, see:
+      #   http://docs.mongodb.org/manual/reference/configuration-options/
+
+      storage:
+         dbPath: ${cfg.dataDir}/db-wt
+         journal:
+            enabled: true
+         wiredTiger:
+            engineConfig:
+               cacheSizeGB: 1
+
+      systemLog:
+         destination: file
+         logAppend: true
+         path: logs/mongod.log
+
+      net:
+         port: 7441
+         bindIp: 127.0.0.1
+
+      operationProfiling:
+         slowOpThresholdMs: 500
+         mode: off
+    '';
+  };
+
+  stateDir = "/var/lib/unifi-video";
+
+in
+  {
+
+    options.services.unifi-video = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether or not to enable the unifi-video service.
+        '';
+      };
+
+      jrePackage = mkOption {
+        type = types.package;
+        default = pkgs.jre8;
+        defaultText = "pkgs.jre8";
+        description = ''
+          The JRE package to use. Check the release notes to ensure it is supported.
+        '';
+      };
+
+      unifiVideoPackage = mkOption {
+        type = types.package;
+        default = pkgs.unifi-video;
+        defaultText = "pkgs.unifi-video";
+        description = ''
+          The unifi-video package to use.
+        '';
+      };
+
+      mongodbPackage = mkOption {
+        type = types.package;
+        default = pkgs.mongodb-4_0;
+        defaultText = "pkgs.mongodb";
+        description = ''
+          The mongodb package to use.
+        '';
+      };
+
+      logDir = mkOption {
+        type = types.str;
+        default = "${stateDir}/logs";
+        description = ''
+          Where to store the logs.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.str;
+        default = "${stateDir}/data";
+        description = ''
+          Where to store the database and other data.
+        '';
+      };
+
+      openPorts = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether or not to open the required ports on the firewall.
+        '';
+      };
+
+      maximumJavaHeapSize = mkOption {
+        type = types.nullOr types.int;
+        default = 1024;
+        example = 4096;
+        description = ''
+          Set the maximimum heap size for the JVM in MB.
+        '';
+      };
+
+      pidFile = mkOption {
+        type = types.path;
+        default = "${cfg.dataDir}/unifi-video.pid";
+        description = "Location of unifi-video pid file.";
+      };
+
+};
+
+config = mkIf cfg.enable {
+  users = {
+    users.unifi-video = {
+      description = "UniFi Video controller daemon user";
+      home = stateDir;
+      group = "unifi-video";
+      isSystemUser = true;
+    };
+    groups.unifi-video = {};
+  };
+
+  networking.firewall = mkIf cfg.openPorts {
+      # https://help.ui.com/hc/en-us/articles/217875218-UniFi-Video-Ports-Used
+      allowedTCPPorts = [
+        7080 # HTTP portal
+        7443 # HTTPS portal
+        7445 # Video over HTTP (mobile app)
+        7446 # Video over HTTPS (mobile app)
+        7447 # RTSP via the controller
+        7442 # Camera management from cameras to NVR over WAN
+      ];
+      allowedUDPPorts = [
+        6666 # Inbound camera streams sent over WAN
+      ];
+    };
+
+    systemd.tmpfiles.rules = [
+      "d '${stateDir}' 0700 unifi-video unifi-video - -"
+      "d '/var/cache/unifi-video' 0700 unifi-video unifi-video - -"
+
+      "d '${stateDir}/logs' 0700 unifi-video unifi-video - -"
+      "C '${stateDir}/etc' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/etc"
+      "C '${stateDir}/webapps' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/webapps"
+      "C '${stateDir}/email' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/email"
+      "C '${stateDir}/fw' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/fw"
+      "C '${stateDir}/lib' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/lib"
+
+      "d '${stateDir}/data' 0700 unifi-video unifi-video - -"
+      "d '${stateDir}/data/db' 0700 unifi-video unifi-video - -"
+      "C '${stateDir}/data/system.properties' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/etc/system.properties"
+
+      "d '${stateDir}/bin' 0700 unifi-video unifi-video - -"
+      "f '${stateDir}/bin/evostreamms' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/bin/evostreamms"
+      "f '${stateDir}/bin/libavcodec.so.54' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/bin/libavcodec.so.54"
+      "f '${stateDir}/bin/libavformat.so.54' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/bin/libavformat.so.54"
+      "f '${stateDir}/bin/libavutil.so.52' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/bin/libavutil.so.52"
+      "f '${stateDir}/bin/ubnt.avtool' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/bin/ubnt.avtool"
+      "f '${stateDir}/bin/ubnt.updater' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/bin/ubnt.updater"
+      "C '${stateDir}/bin/mongo' 0700 unifi-video unifi-video - ${cfg.mongodbPackage}/bin/mongo"
+      "C '${stateDir}/bin/mongod' 0700 unifi-video unifi-video - ${cfg.mongodbPackage}/bin/mongod"
+      "C '${stateDir}/bin/mongoperf' 0700 unifi-video unifi-video - ${cfg.mongodbPackage}/bin/mongoperf"
+      "C '${stateDir}/bin/mongos' 0700 unifi-video unifi-video - ${cfg.mongodbPackage}/bin/mongos"
+
+      "d '${stateDir}/conf' 0700 unifi-video unifi-video - -"
+      "C '${stateDir}/conf/evostream' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/evostream"
+      "Z '${stateDir}/conf/evostream' 0700 unifi-video unifi-video - -"
+      "L+ '${stateDir}/conf/mongodv3.0+.conf' 0700 unifi-video unifi-video - ${mongoConf}"
+      "L+ '${stateDir}/conf/mongodv3.6+.conf' 0700 unifi-video unifi-video - ${mongoConf}"
+      "L+ '${stateDir}/conf/mongod-wt.conf' 0700 unifi-video unifi-video - ${mongoWtConf}"
+      "L+ '${stateDir}/conf/catalina.policy' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/catalina.policy"
+      "L+ '${stateDir}/conf/catalina.properties' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/catalina.properties"
+      "L+ '${stateDir}/conf/context.xml' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/context.xml"
+      "L+ '${stateDir}/conf/logging.properties' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/logging.properties"
+      "L+ '${stateDir}/conf/server.xml' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/server.xml"
+      "L+ '${stateDir}/conf/tomcat-users.xml' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/tomcat-users.xml"
+      "L+ '${stateDir}/conf/web.xml' 0700 unifi-video unifi-video - ${pkgs.unifi-video}/lib/unifi-video/conf/web.xml"
+
+    ];
+
+    systemd.services.unifi-video = {
+      description = "UniFi Video NVR daemon";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ] ;
+      unitConfig.RequiresMountsFor = stateDir;
+      # Make sure package upgrades trigger a service restart
+      restartTriggers = [ cfg.unifiVideoPackage cfg.mongodbPackage ];
+      path = with pkgs; [ gawk coreutils busybox which jre8 lsb-release libcap util-linux ];
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = "${(removeSuffix "\n" cmd)} ${mainClass} start";
+        ExecStop = "${(removeSuffix "\n" cmd)} stop ${mainClass} stop";
+        Restart = "on-failure";
+        UMask = "0077";
+        User = "unifi-video";
+        WorkingDirectory = "${stateDir}";
+      };
+    };
+
+  };
+
+  meta = {
+    maintainers = with lib.maintainers; [ rsynnest ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/discourse.nix b/nixpkgs/nixos/modules/services/web-apps/discourse.nix
index d3ae072f86a8..8d5302ba267b 100644
--- a/nixpkgs/nixos/modules/services/web-apps/discourse.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/discourse.nix
@@ -475,21 +475,16 @@ in
       plugins = lib.mkOption {
         type = lib.types.listOf lib.types.package;
         default = [];
-        example = ''
-          [
-            (pkgs.fetchFromGitHub {
-              owner = "discourse";
-              repo = "discourse-spoiler-alert";
-              rev = "e200cfa571d252cab63f3d30d619b370986e4cee";
-              sha256 = "0ya69ix5g77wz4c9x9gmng6l25ghb5xxlx3icr6jam16q14dzc33";
-            })
+        example = lib.literalExample ''
+          with config.services.discourse.package.plugins; [
+            discourse-canned-replies
+            discourse-github
           ];
         '';
         description = ''
-          <productname>Discourse</productname> plugins to install as a
-          list of derivations. As long as a plugin supports the
-          standard install method, packaging it should only require
-          fetching its source with an appropriate fetcher.
+          Plugins to install as part of
+          <productname>Discourse</productname>, expressed as a list of
+          derivations.
         '';
       };
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/discourse.xml b/nixpkgs/nixos/modules/services/web-apps/discourse.xml
index bae562423213..1d6866e7b352 100644
--- a/nixpkgs/nixos/modules/services/web-apps/discourse.xml
+++ b/nixpkgs/nixos/modules/services/web-apps/discourse.xml
@@ -262,9 +262,31 @@ services.discourse = {
     <para>
       You can install <productname>Discourse</productname> plugins
       using the <xref linkend="opt-services.discourse.plugins" />
-      option. As long as a plugin supports the standard install
-      method, packaging it should only require fetching its source
-      with an appropriate fetcher.
+      option. Pre-packaged plugins are provided in
+      <literal>&lt;your_discourse_package_here&gt;.plugins</literal>. If
+      you want the full suite of plugins provided through
+      <literal>nixpkgs</literal>, you can also set the <xref
+      linkend="opt-services.discourse.package" /> option to
+      <literal>pkgs.discourseAllPlugins</literal>.
+    </para>
+
+    <para>
+      Plugins can be built with the
+      <literal>&lt;your_discourse_package_here&gt;.mkDiscoursePlugin</literal>
+      function. Normally, it should suffice to provide a
+      <literal>name</literal> and <literal>src</literal> attribute. If
+      the plugin has Ruby dependencies, however, they need to be
+      packaged in accordance with the <link
+      xlink:href="https://nixos.org/manual/nixpkgs/stable/#developing-with-ruby">Developing
+      with Ruby</link> section of the Nixpkgs manual and the
+      appropriate gem options set in <literal>bundlerEnvArgs</literal>
+      (normally <literal>gemdir</literal> is sufficient). A plugin's
+      Ruby dependencies are listed in its
+      <filename>plugin.rb</filename> file as function calls to
+      <literal>gem</literal>. To construct the corresponding
+      <filename>Gemfile</filename>, run <command>bundle
+      init</command>, then add the <literal>gem</literal> lines to it
+      verbatim.
     </para>
 
     <para>
@@ -280,7 +302,10 @@ services.discourse = {
     <para>
       For example, to add the <link
       xlink:href="https://github.com/discourse/discourse-spoiler-alert">discourse-spoiler-alert</link>
-      plugin and disable it by default:
+      and <link
+      xlink:href="https://github.com/discourse/discourse-solved">discourse-solved</link>
+      plugins, and disable <literal>discourse-spoiler-alert</literal>
+      by default:
 
 <programlisting>
 services.discourse = {
@@ -301,13 +326,9 @@ services.discourse = {
     <link linkend="opt-services.discourse.mail.outgoing.passwordFile">passwordFile</link> = "/path/to/smtp_password_file";
   };
   <link linkend="opt-services.discourse.mail.incoming.enable">mail.incoming.enable</link> = true;
-  <link linkend="opt-services.discourse.mail.incoming.enable">plugins</link> = [
-    (pkgs.fetchFromGitHub {
-      owner = "discourse";
-      repo = "discourse-spoiler-alert";
-      rev = "e200cfa571d252cab63f3d30d619b370986e4cee";
-      sha256 = "0ya69ix5g77wz4c9x9gmng6l25ghb5xxlx3icr6jam16q14dzc33";
-    })
+  <link linkend="opt-services.discourse.mail.incoming.enable">plugins</link> = with config.services.discourse.package.plugins; [
+    discourse-spoiler-alert
+    discourse-solved
   ];
   <link linkend="opt-services.discourse.siteSettings">siteSettings</link> = {
     plugins = {
diff --git a/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix b/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix
index eea49bda283b..f8f0854f1bcb 100644
--- a/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/icingaweb2/icingaweb2.nix
@@ -23,6 +23,16 @@ in {
       '';
     };
 
+    libraryPaths = mkOption {
+      type = attrsOf package;
+      default = { };
+      description = ''
+        Libraries to add to the Icingaweb2 library path.
+        The name of the attribute is the name of the library, the value
+        is the package to add.
+      '';
+    };
+
     virtualHost = mkOption {
       type = nullOr str;
       default = "icingaweb2";
@@ -167,6 +177,9 @@ in {
     services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") {
       ${poolName} = {
         user = "icingaweb2";
+        phpEnv = {
+          ICINGAWEB_LIBDIR = toString (pkgs.linkFarm "icingaweb2-libdir" (mapAttrsToList (name: path: { inherit name path; }) cfg.libraryPaths));
+        };
         phpPackage = pkgs.php.withExtensions ({ enabled, all }: [ all.imagick ] ++ enabled);
         phpOptions = ''
           date.timezone = "${cfg.timezone}"
@@ -184,6 +197,11 @@ in {
       };
     };
 
+    services.icingaweb2.libraryPaths = {
+      ipl = pkgs.icingaweb2-ipl;
+      thirdparty = pkgs.icingaweb2-thirdparty;
+    };
+
     systemd.services."phpfpm-${poolName}".serviceConfig.ReadWritePaths = [ "/etc/icingaweb2" ];
 
     services.nginx = {
diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
index 545deaa905f2..111b31734696 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
@@ -92,7 +92,7 @@ in {
     package = mkOption {
       type = types.package;
       description = "Which package to use for the Nextcloud instance.";
-      relatedPackages = [ "nextcloud19" "nextcloud20" "nextcloud21" ];
+      relatedPackages = [ "nextcloud20" "nextcloud21" "nextcloud22" ];
     };
 
     maxUploadSize = mkOption {
@@ -385,7 +385,7 @@ in {
       ];
 
       warnings = let
-        latest = 21;
+        latest = 22;
         upgradeWarning = major: nixos:
           ''
             A legacy Nextcloud install (from before NixOS ${nixos}) may be installed.
@@ -403,9 +403,9 @@ in {
           Using config.services.nextcloud.poolConfig is deprecated and will become unsupported in a future release.
           Please migrate your configuration to config.services.nextcloud.poolSettings.
         '')
-        ++ (optional (versionOlder cfg.package.version "19") (upgradeWarning 18 "20.09"))
         ++ (optional (versionOlder cfg.package.version "20") (upgradeWarning 19 "21.05"))
-        ++ (optional (versionOlder cfg.package.version "21") (upgradeWarning 20 "21.05"));
+        ++ (optional (versionOlder cfg.package.version "21") (upgradeWarning 20 "21.05"))
+        ++ (optional (versionOlder cfg.package.version "22") (upgradeWarning 21 "21.11"));
 
       services.nextcloud.package = with pkgs;
         mkDefault (
@@ -415,13 +415,13 @@ in {
               nextcloud defined in an overlay, please set `services.nextcloud.package` to
               `pkgs.nextcloud`.
             ''
-          else if versionOlder stateVersion "20.09" then nextcloud18
           # 21.03 will not be an official release - it was instead 21.05.
           # This versionOlder statement remains set to 21.03 for backwards compatibility.
           # See https://github.com/NixOS/nixpkgs/pull/108899 and
           # https://github.com/NixOS/rfcs/blob/master/rfcs/0080-nixos-release-schedule.md.
           else if versionOlder stateVersion "21.03" then nextcloud19
-          else nextcloud21
+          else if versionOlder stateVersion "21.11" then nextcloud21
+          else nextcloud22
         );
     }
 
@@ -616,9 +616,7 @@ in {
 
       services.nginx.enable = mkDefault true;
 
-      services.nginx.virtualHosts.${cfg.hostName} = let
-        major = toInt (versions.major cfg.package.version);
-      in {
+      services.nginx.virtualHosts.${cfg.hostName} = {
         root = cfg.package;
         locations = {
           "= /robots.txt" = {
diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml b/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml
index 83a6f68edcbf..3af37b15dd56 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml
+++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml
@@ -11,7 +11,7 @@
   desktop client is packaged at <literal>pkgs.nextcloud-client</literal>.
  </para>
  <para>
-  The current default by NixOS is <package>nextcloud21</package> which is also the latest
+  The current default by NixOS is <package>nextcloud22</package> which is also the latest
   major version available.
  </para>
  <section xml:id="module-services-nextcloud-basic-usage">
diff --git a/nixpkgs/nixos/modules/services/web-apps/plausible.nix b/nixpkgs/nixos/modules/services/web-apps/plausible.nix
index caf5ba466dfe..b56848b79d21 100644
--- a/nixpkgs/nixos/modules/services/web-apps/plausible.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/plausible.nix
@@ -7,10 +7,15 @@ let
 
   # FIXME consider using LoadCredential as soon as it actually works.
   envSecrets = ''
-    export ADMIN_USER_PWD="$(<${cfg.adminUser.passwordFile})"
-    export SECRET_KEY_BASE="$(<${cfg.server.secretKeybaseFile})"
+    ADMIN_USER_PWD="$(<${cfg.adminUser.passwordFile})"
+    export ADMIN_USER_PWD # separate export to make `set -e` work
+
+    SECRET_KEY_BASE="$(<${cfg.server.secretKeybaseFile})"
+    export SECRET_KEY_BASE # separate export to make `set -e` work
+
     ${optionalString (cfg.mail.smtp.passwordFile != null) ''
-      export SMTP_USER_PWD="$(<${cfg.mail.smtp.passwordFile})"
+      SMTP_USER_PWD="$(<${cfg.mail.smtp.passwordFile})"
+      export SMTP_USER_PWD # separate export to make `set -e` work
     ''}
   '';
 in {
@@ -102,6 +107,11 @@ in {
         type = types.str;
         description = ''
           Public URL where plausible is available.
+
+          Note that <literal>/path</literal> components are currently ignored:
+          <link xlink:href="https://github.com/plausible/analytics/issues/1182">
+            https://github.com/plausible/analytics/issues/1182
+          </link>.
         '';
       };
     };
@@ -228,6 +238,7 @@ in {
             WorkingDirectory = "/var/lib/plausible";
             StateDirectory = "plausible";
             ExecStartPre = "@${pkgs.writeShellScript "plausible-setup" ''
+              set -eu -o pipefail
               ${envSecrets}
               ${pkgs.plausible}/createdb.sh
               ${pkgs.plausible}/migrate.sh
@@ -238,6 +249,7 @@ in {
               ''}
             ''} plausible-setup";
             ExecStart = "@${pkgs.writeShellScript "plausible" ''
+              set -eu -o pipefail
               ${envSecrets}
               plausible start
             ''} plausible";
diff --git a/nixpkgs/nixos/modules/services/web-apps/vikunja.nix b/nixpkgs/nixos/modules/services/web-apps/vikunja.nix
new file mode 100644
index 000000000000..b0b6eb6df17e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/vikunja.nix
@@ -0,0 +1,145 @@
+{ pkgs, lib, config, ... }:
+
+with lib;
+
+let
+  cfg = config.services.vikunja;
+  format = pkgs.formats.yaml {};
+  configFile = format.generate "config.yaml" cfg.settings;
+  useMysql = cfg.database.type == "mysql";
+  usePostgresql = cfg.database.type == "postgres";
+in {
+  options.services.vikunja = with lib; {
+    enable = mkEnableOption "vikunja service";
+    package-api = mkOption {
+      default = pkgs.vikunja-api;
+      type = types.package;
+      defaultText = "pkgs.vikunja-api";
+      description = "vikunja-api derivation to use.";
+    };
+    package-frontend = mkOption {
+      default = pkgs.vikunja-frontend;
+      type = types.package;
+      defaultText = "pkgs.vikunja-frontend";
+      description = "vikunja-frontend derivation to use.";
+    };
+    environmentFiles = mkOption {
+      type = types.listOf types.path;
+      default = [ ];
+      description = ''
+        List of environment files set in the vikunja systemd service.
+        For example passwords should be set in one of these files.
+      '';
+    };
+    setupNginx = mkOption {
+      type = types.bool;
+      default = config.services.nginx.enable;
+      defaultText = "config.services.nginx.enable";
+      description = ''
+        Whether to setup NGINX.
+        Further nginx configuration can be done by changing
+        <option>services.nginx.virtualHosts.&lt;frontendHostname&gt;</option>.
+        This does not enable TLS or ACME by default. To enable this, set the
+        <option>services.nginx.virtualHosts.&lt;frontendHostname&gt;.enableACME</option> to
+        <literal>true</literal> and if appropriate do the same for
+        <option>services.nginx.virtualHosts.&lt;frontendHostname&gt;.forceSSL</option>.
+      '';
+    };
+    frontendScheme = mkOption {
+      type = types.enum [ "http" "https" ];
+      description = ''
+        Whether the site is available via http or https.
+        This does not configure https or ACME in nginx!
+      '';
+    };
+    frontendHostname = mkOption {
+      type = types.str;
+      description = "The Hostname under which the frontend is running.";
+    };
+
+    settings = mkOption {
+      type = format.type;
+      default = {};
+      description = ''
+        Vikunja configuration. Refer to
+        <link xlink:href="https://vikunja.io/docs/config-options/"/>
+        for details on supported values.
+        '';
+    };
+    database = {
+      type = mkOption {
+        type = types.enum [ "sqlite" "mysql" "postgres" ];
+        example = "postgres";
+        default = "sqlite";
+        description = "Database engine to use.";
+      };
+      host = mkOption {
+        type = types.str;
+        default = "localhost";
+        description = "Database host address. Can also be a socket.";
+      };
+      user = mkOption {
+        type = types.str;
+        default = "vikunja";
+        description = "Database user.";
+      };
+      database = mkOption {
+        type = types.str;
+        default = "vikunja";
+        description = "Database name.";
+      };
+      path = mkOption {
+        type = types.str;
+        default = "/var/lib/vikunja/vikunja.db";
+        description = "Path to the sqlite3 database file.";
+      };
+    };
+  };
+  config = lib.mkIf cfg.enable {
+    services.vikunja.settings = {
+      database = {
+        inherit (cfg.database) type host user database path;
+      };
+      service = {
+        frontendurl = "${cfg.frontendScheme}://${cfg.frontendHostname}/";
+      };
+      files = {
+        basepath = "/var/lib/vikunja/files";
+      };
+    };
+
+    systemd.services.vikunja-api = {
+      description = "vikunja-api";
+      after = [ "network.target" ] ++ lib.optional usePostgresql "postgresql.service" ++ lib.optional useMysql "mysql.service";
+      wantedBy = [ "multi-user.target" ];
+      path = [ cfg.package-api ];
+      restartTriggers = [ configFile ];
+
+      serviceConfig = {
+        Type = "simple";
+        DynamicUser = true;
+        StateDirectory = "vikunja";
+        ExecStart = "${cfg.package-api}/bin/vikunja";
+        Restart = "always";
+        EnvironmentFile = cfg.environmentFiles;
+      };
+    };
+
+    services.nginx.virtualHosts."${cfg.frontendHostname}" = mkIf cfg.setupNginx {
+      locations = {
+        "/" = {
+          root = cfg.package-frontend;
+          tryFiles = "try_files $uri $uri/ /";
+        };
+        "~* ^/(api|dav|\\.well-known)/" = {
+          proxyPass = "http://localhost:3456";
+          extraConfig = ''
+            client_max_body_size 20M;
+          '';
+        };
+      };
+    };
+
+    environment.etc."vikunja/config.yaml".source = configFile;
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/wordpress.nix b/nixpkgs/nixos/modules/services/web-apps/wordpress.nix
index 775ecb3acaf0..6f1ef815bc46 100644
--- a/nixpkgs/nixos/modules/services/web-apps/wordpress.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/wordpress.nix
@@ -3,13 +3,18 @@
 let
   inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption types;
   inherit (lib) any attrValues concatMapStringsSep flatten literalExample;
-  inherit (lib) mapAttrs mapAttrs' mapAttrsToList nameValuePair optional optionalAttrs optionalString;
+  inherit (lib) filterAttrs mapAttrs mapAttrs' mapAttrsToList nameValuePair optional optionalAttrs optionalString;
 
-  eachSite = config.services.wordpress;
+  cfg = migrateOldAttrs config.services.wordpress;
+  eachSite = cfg.sites;
   user = "wordpress";
-  group = config.services.httpd.group;
+  webserver = config.services.${cfg.webserver};
   stateDir = hostName: "/var/lib/wordpress/${hostName}";
 
+  # Migrate config.services.wordpress.<hostName> to config.services.wordpress.sites.<hostName>
+  oldSites = filterAttrs (o: _: o != "sites" && o != "webserver");
+  migrateOldAttrs = cfg: cfg // { sites = cfg.sites // oldSites cfg; };
+
   pkg = hostName: cfg: pkgs.stdenv.mkDerivation rec {
     pname = "wordpress-${hostName}";
     version = src.version;
@@ -261,21 +266,48 @@ in
   # interface
   options = {
     services.wordpress = mkOption {
-      type = types.attrsOf (types.submodule siteOpts);
+      type = types.submodule {
+        # Used to support old interface
+        freeformType = types.attrsOf (types.submodule siteOpts);
+
+        # New interface
+        options.sites = mkOption {
+          type = types.attrsOf (types.submodule siteOpts);
+          default = {};
+          description = "Specification of one or more WordPress sites to serve";
+        };
+
+        options.webserver = mkOption {
+          type = types.enum [ "httpd" "nginx" ];
+          default = "httpd";
+          description = ''
+            Whether to use apache2 or nginx for virtual host management.
+
+            Further nginx configuration can be done by adapting <literal>services.nginx.virtualHosts.&lt;name&gt;</literal>.
+            See <xref linkend="opt-services.nginx.virtualHosts"/> for further information.
+
+            Further apache2 configuration can be done by adapting <literal>services.httpd.virtualHosts.&lt;name&gt;</literal>.
+            See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
+          '';
+        };
+      };
       default = {};
-      description = "Specification of one or more WordPress sites to serve via Apache.";
+      description = "Wordpress configuration";
     };
+
   };
 
   # implementation
-  config = mkIf (eachSite != {}) {
+  config = mkIf (eachSite != {}) (mkMerge [{
 
     assertions = mapAttrsToList (hostName: cfg:
       { assertion = cfg.database.createLocally -> cfg.database.user == user;
-        message = "services.wordpress.${hostName}.database.user must be ${user} if the database is to be automatically provisioned";
+        message = ''services.wordpress.sites."${hostName}".database.user must be ${user} if the database is to be automatically provisioned'';
       }
     ) eachSite;
 
+    warnings = mapAttrsToList (hostName: _: ''services.wordpress."${hostName}" is deprecated use services.wordpress.sites."${hostName}"'') (oldSites cfg);
+
     services.mysql = mkIf (any (v: v.database.createLocally) (attrValues eachSite)) {
       enable = true;
       package = mkDefault pkgs.mariadb;
@@ -289,14 +321,18 @@ in
 
     services.phpfpm.pools = mapAttrs' (hostName: cfg: (
       nameValuePair "wordpress-${hostName}" {
-        inherit user group;
+        inherit user;
+        group = webserver.group;
         settings = {
-          "listen.owner" = config.services.httpd.user;
-          "listen.group" = config.services.httpd.group;
+          "listen.owner" = webserver.user;
+          "listen.group" = webserver.group;
         } // cfg.poolConfig;
       }
     )) eachSite;
 
+  }
+
+  (mkIf (cfg.webserver == "httpd") {
     services.httpd = {
       enable = true;
       extraModules = [ "proxy_fcgi" ];
@@ -332,11 +368,13 @@ in
         '';
       } ]) eachSite;
     };
+  })
 
+  {
     systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [
-      "d '${stateDir hostName}' 0750 ${user} ${group} - -"
-      "d '${cfg.uploadsDir}' 0750 ${user} ${group} - -"
-      "Z '${cfg.uploadsDir}' 0750 ${user} ${group} - -"
+      "d '${stateDir hostName}' 0750 ${user} ${webserver.group} - -"
+      "d '${cfg.uploadsDir}' 0750 ${user} ${webserver.group} - -"
+      "Z '${cfg.uploadsDir}' 0750 ${user} ${webserver.group} - -"
     ]) eachSite);
 
     systemd.services = mkMerge [
@@ -350,7 +388,7 @@ in
           serviceConfig = {
             Type = "oneshot";
             User = user;
-            Group = group;
+            Group = webserver.group;
           };
       })) eachSite)
 
@@ -360,9 +398,65 @@ in
     ];
 
     users.users.${user} = {
-      group = group;
+      group = webserver.group;
       isSystemUser = true;
     };
+  }
 
-  };
+  (mkIf (cfg.webserver == "nginx") {
+    services.nginx = {
+      enable = true;
+      virtualHosts = mapAttrs (hostName: cfg: {
+        serverName = mkDefault hostName;
+        root = "${pkg hostName cfg}/share/wordpress";
+        extraConfig = ''
+          index index.php;
+        '';
+        locations = {
+          "/" = {
+            priority = 200;
+            extraConfig = ''
+              try_files $uri $uri/ /index.php$is_args$args;
+            '';
+          };
+          "~ \\.php$" = {
+            priority = 500;
+            extraConfig = ''
+              fastcgi_split_path_info ^(.+\.php)(/.+)$;
+              fastcgi_pass unix:${config.services.phpfpm.pools."wordpress-${hostName}".socket};
+              fastcgi_index index.php;
+              include "${config.services.nginx.package}/conf/fastcgi.conf";
+              fastcgi_param PATH_INFO $fastcgi_path_info;
+              fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
+              # Mitigate https://httpoxy.org/ vulnerabilities
+              fastcgi_param HTTP_PROXY "";
+              fastcgi_intercept_errors off;
+              fastcgi_buffer_size 16k;
+              fastcgi_buffers 4 16k;
+              fastcgi_connect_timeout 300;
+              fastcgi_send_timeout 300;
+              fastcgi_read_timeout 300;
+            '';
+          };
+          "~ /\\." = {
+            priority = 800;
+            extraConfig = "deny all;";
+          };
+          "~* /(?:uploads|files)/.*\\.php$" = {
+            priority = 900;
+            extraConfig = "deny all;";
+          };
+          "~* \\.(js|css|png|jpg|jpeg|gif|ico)$" = {
+            priority = 1000;
+            extraConfig = ''
+              expires max;
+              log_not_found off;
+            '';
+          };
+        };
+      }) eachSite;
+    };
+  })
+
+  ]);
 }
diff --git a/nixpkgs/nixos/modules/system/boot/systemd.nix b/nixpkgs/nixos/modules/system/boot/systemd.nix
index 1d6f46f1d17f..adc226d89913 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd.nix
@@ -755,7 +755,7 @@ in
       default = [];
       example = [ "d /tmp 1777 root root 10d" ];
       description = ''
-        Rules for creating and cleaning up temporary files
+        Rules for creation, deletion and cleaning of volatile and temporary files
         automatically. See
         <citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
         for the exact format.
diff --git a/nixpkgs/nixos/modules/tasks/snapraid.nix b/nixpkgs/nixos/modules/tasks/snapraid.nix
new file mode 100644
index 000000000000..4529009930fc
--- /dev/null
+++ b/nixpkgs/nixos/modules/tasks/snapraid.nix
@@ -0,0 +1,230 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.snapraid;
+in
+{
+  options.snapraid = with types; {
+    enable = mkEnableOption "SnapRAID";
+    dataDisks = mkOption {
+      default = { };
+      example = {
+        d1 = "/mnt/disk1/";
+        d2 = "/mnt/disk2/";
+        d3 = "/mnt/disk3/";
+      };
+      description = "SnapRAID data disks.";
+      type = attrsOf str;
+    };
+    parityFiles = mkOption {
+      default = [ ];
+      example = [
+        "/mnt/diskp/snapraid.parity"
+        "/mnt/diskq/snapraid.2-parity"
+        "/mnt/diskr/snapraid.3-parity"
+        "/mnt/disks/snapraid.4-parity"
+        "/mnt/diskt/snapraid.5-parity"
+        "/mnt/disku/snapraid.6-parity"
+      ];
+      description = "SnapRAID parity files.";
+      type = listOf str;
+    };
+    contentFiles = mkOption {
+      default = [ ];
+      example = [
+        "/var/snapraid.content"
+        "/mnt/disk1/snapraid.content"
+        "/mnt/disk2/snapraid.content"
+      ];
+      description = "SnapRAID content list files.";
+      type = listOf str;
+    };
+    exclude = mkOption {
+      default = [ ];
+      example = [ "*.unrecoverable" "/tmp/" "/lost+found/" ];
+      description = "SnapRAID exclude directives.";
+      type = listOf str;
+    };
+    touchBeforeSync = mkOption {
+      default = true;
+      example = false;
+      description =
+        "Whether <command>snapraid touch</command> should be run before <command>snapraid sync</command>.";
+      type = bool;
+    };
+    sync.interval = mkOption {
+      default = "01:00";
+      example = "daily";
+      description = "How often to run <command>snapraid sync</command>.";
+      type = str;
+    };
+    scrub = {
+      interval = mkOption {
+        default = "Mon *-*-* 02:00:00";
+        example = "weekly";
+        description = "How often to run <command>snapraid scrub</command>.";
+        type = str;
+      };
+      plan = mkOption {
+        default = 8;
+        example = 5;
+        description =
+          "Percent of the array that should be checked by <command>snapraid scrub</command>.";
+        type = int;
+      };
+      olderThan = mkOption {
+        default = 10;
+        example = 20;
+        description =
+          "Number of days since data was last scrubbed before it can be scrubbed again.";
+        type = int;
+      };
+    };
+    extraConfig = mkOption {
+      default = "";
+      example = ''
+        nohidden
+        blocksize 256
+        hashsize 16
+        autosave 500
+        pool /pool
+      '';
+      description = "Extra config options for SnapRAID.";
+      type = lines;
+    };
+  };
+
+  config =
+    let
+      nParity = builtins.length cfg.parityFiles;
+      mkPrepend = pre: s: pre + s;
+    in
+    mkIf cfg.enable {
+      assertions = [
+        {
+          assertion = nParity <= 6;
+          message = "You can have no more than six SnapRAID parity files.";
+        }
+        {
+          assertion = builtins.length cfg.contentFiles >= nParity + 1;
+          message =
+            "There must be at least one SnapRAID content file for each SnapRAID parity file plus one.";
+        }
+      ];
+
+      environment = {
+        systemPackages = with pkgs; [ snapraid ];
+
+        etc."snapraid.conf" = {
+          text = with cfg;
+            let
+              prependData = mkPrepend "data ";
+              prependContent = mkPrepend "content ";
+              prependExclude = mkPrepend "exclude ";
+            in
+            concatStringsSep "\n"
+              (map prependData
+                ((mapAttrsToList (name: value: name + " " + value)) dataDisks)
+              ++ zipListsWith (a: b: a + b)
+                ([ "parity " ] ++ map (i: toString i + "-parity ") (range 2 6))
+                parityFiles ++ map prependContent contentFiles
+              ++ map prependExclude exclude) + "\n" + extraConfig;
+        };
+      };
+
+      systemd.services = with cfg; {
+        snapraid-scrub = {
+          description = "Scrub the SnapRAID array";
+          startAt = scrub.interval;
+          serviceConfig = {
+            Type = "oneshot";
+            ExecStart = "${pkgs.snapraid}/bin/snapraid scrub -p ${
+              toString scrub.plan
+            } -o ${toString scrub.olderThan}";
+            Nice = 19;
+            IOSchedulingPriority = 7;
+            CPUSchedulingPolicy = "batch";
+
+            LockPersonality = true;
+            MemoryDenyWriteExecute = true;
+            NoNewPrivileges = true;
+            PrivateDevices = true;
+            PrivateTmp = true;
+            ProtectClock = true;
+            ProtectControlGroups = true;
+            ProtectHostname = true;
+            ProtectKernelLogs = true;
+            ProtectKernelModules = true;
+            ProtectKernelTunables = true;
+            RestrictAddressFamilies = "none";
+            RestrictNamespaces = true;
+            RestrictRealtime = true;
+            RestrictSUIDSGID = true;
+            SystemCallArchitectures = "native";
+            SystemCallFilter = "@system-service";
+            SystemCallErrorNumber = "EPERM";
+            CapabilityBoundingSet = "CAP_DAC_OVERRIDE";
+
+            ProtectSystem = "strict";
+            ProtectHome = "read-only";
+            ReadWritePaths =
+              # scrub requires access to directories containing content files
+              # to remove them if they are stale
+              let
+                contentDirs = map dirOf contentFiles;
+              in
+              unique (
+                attrValues dataDisks ++ contentDirs
+              );
+          };
+          unitConfig.After = "snapraid-sync.service";
+        };
+        snapraid-sync = {
+          description = "Synchronize the state of the SnapRAID array";
+          startAt = sync.interval;
+          serviceConfig = {
+            Type = "oneshot";
+            ExecStart = "${pkgs.snapraid}/bin/snapraid sync";
+            Nice = 19;
+            IOSchedulingPriority = 7;
+            CPUSchedulingPolicy = "batch";
+
+            LockPersonality = true;
+            MemoryDenyWriteExecute = true;
+            NoNewPrivileges = true;
+            PrivateDevices = true;
+            PrivateTmp = true;
+            ProtectClock = true;
+            ProtectControlGroups = true;
+            ProtectHostname = true;
+            ProtectKernelLogs = true;
+            ProtectKernelModules = true;
+            ProtectKernelTunables = true;
+            RestrictAddressFamilies = "none";
+            RestrictNamespaces = true;
+            RestrictRealtime = true;
+            RestrictSUIDSGID = true;
+            SystemCallArchitectures = "native";
+            SystemCallFilter = "@system-service";
+            SystemCallErrorNumber = "EPERM";
+            CapabilityBoundingSet = "CAP_DAC_OVERRIDE";
+
+            ProtectSystem = "strict";
+            ProtectHome = "read-only";
+            ReadWritePaths =
+              # sync requires access to directories containing content files
+              # to remove them if they are stale
+              let
+                contentDirs = map dirOf contentFiles;
+              in
+              unique (
+                attrValues dataDisks ++ parityFiles ++ contentDirs
+              );
+          } // optionalAttrs touchBeforeSync {
+            ExecStartPre = "${pkgs.snapraid}/bin/snapraid touch";
+          };
+        };
+      };
+    };
+}
diff --git a/nixpkgs/nixos/modules/virtualisation/cri-o.nix b/nixpkgs/nixos/modules/virtualisation/cri-o.nix
index 8d352e36ef99..c135081959a6 100644
--- a/nixpkgs/nixos/modules/virtualisation/cri-o.nix
+++ b/nixpkgs/nixos/modules/virtualisation/cri-o.nix
@@ -6,6 +6,9 @@ let
 
   crioPackage = (pkgs.cri-o.override { inherit (cfg) extraPackages; });
 
+  format = pkgs.formats.toml { };
+
+  cfgFile = format.generate "00-default.conf" cfg.settings;
 in
 {
   imports = [
@@ -13,7 +16,7 @@ in
   ];
 
   meta = {
-    maintainers = lib.teams.podman.members;
+    maintainers = teams.podman.members;
   };
 
   options.virtualisation.cri-o = {
@@ -55,7 +58,7 @@ in
     extraPackages = mkOption {
       type = with types; listOf package;
       default = [ ];
-      example = lib.literalExample ''
+      example = literalExample ''
         [
           pkgs.gvisor
         ]
@@ -65,7 +68,7 @@ in
       '';
     };
 
-    package = lib.mkOption {
+    package = mkOption {
       type = types.package;
       default = crioPackage;
       internal = true;
@@ -80,6 +83,15 @@ in
       description = "Override the network_dir option.";
       internal = true;
     };
+
+    settings = mkOption {
+      type = format.type;
+      default = { };
+      description = ''
+        Configuration for cri-o, see
+        <link xlink:href="https://github.com/cri-o/cri-o/blob/master/docs/crio.conf.5.md"/>.
+      '';
+    };
   };
 
   config = mkIf cfg.enable {
@@ -87,36 +99,38 @@ in
 
     environment.etc."crictl.yaml".source = utils.copyFile "${pkgs.cri-o-unwrapped.src}/crictl.yaml";
 
-    environment.etc."crio/crio.conf.d/00-default.conf".text = ''
-      [crio]
-      storage_driver = "${cfg.storageDriver}"
-
-      [crio.image]
-      ${optionalString (cfg.pauseImage != null) ''pause_image = "${cfg.pauseImage}"''}
-      ${optionalString (cfg.pauseCommand != null) ''pause_command = "${cfg.pauseCommand}"''}
-
-      [crio.network]
-      plugin_dirs = ["${pkgs.cni-plugins}/bin/"]
-      ${optionalString (cfg.networkDir != null) ''network_dir = "${cfg.networkDir}"''}
-
-      [crio.runtime]
-      cgroup_manager = "systemd"
-      log_level = "${cfg.logLevel}"
-      pinns_path = "${cfg.package}/bin/pinns"
-      hooks_dir = [
-      ${lib.optionalString config.virtualisation.containers.ociSeccompBpfHook.enable
-        ''"${config.boot.kernelPackages.oci-seccomp-bpf-hook}",''}
-      ]
-
-      ${optionalString (cfg.runtime != null) ''
-      default_runtime = "${cfg.runtime}"
-      [crio.runtime.runtimes]
-      [crio.runtime.runtimes.${cfg.runtime}]
-      ''}
-    '';
+    virtualisation.cri-o.settings.crio = {
+      storage_driver = cfg.storageDriver;
+
+      image = {
+        pause_image = mkIf (cfg.pauseImage != null) cfg.pauseImage;
+        pause_command = mkIf (cfg.pauseCommand != null) cfg.pauseCommand;
+      };
+
+      network = {
+        plugin_dirs = [ "${pkgs.cni-plugins}/bin" ];
+        network_dir = mkIf (cfg.networkDir != null) cfg.networkDir;
+      };
+
+      runtime = {
+        cgroup_manager = "systemd";
+        log_level = cfg.logLevel;
+        manage_ns_lifecycle = true;
+        pinns_path = "${cfg.package}/bin/pinns";
+        hooks_dir =
+          optional (config.virtualisation.containers.ociSeccompBpfHook.enable)
+            config.boot.kernelPackages.oci-seccomp-bpf-hook;
+
+        default_runtime = mkIf (cfg.runtime != null) cfg.runtime;
+        runtimes = mkIf (cfg.runtime != null) {
+          "${cfg.runtime}" = { };
+        };
+      };
+    };
 
     environment.etc."cni/net.d/10-crio-bridge.conf".source = utils.copyFile "${pkgs.cri-o-unwrapped.src}/contrib/cni/10-crio-bridge.conf";
     environment.etc."cni/net.d/99-loopback.conf".source = utils.copyFile "${pkgs.cri-o-unwrapped.src}/contrib/cni/99-loopback.conf";
+    environment.etc."crio/crio.conf.d/00-default.conf".source = cfgFile;
 
     # Enable common /etc/containers configuration
     virtualisation.containers.enable = true;
@@ -139,6 +153,7 @@ in
         TimeoutStartSec = "0";
         Restart = "on-abnormal";
       };
+      restartTriggers = [ cfgFile ];
     };
   };
 }
diff --git a/nixpkgs/nixos/modules/virtualisation/oci-containers.nix b/nixpkgs/nixos/modules/virtualisation/oci-containers.nix
index 65b63cebc79c..a4a92f22506c 100644
--- a/nixpkgs/nixos/modules/virtualisation/oci-containers.nix
+++ b/nixpkgs/nixos/modules/virtualisation/oci-containers.nix
@@ -31,6 +31,30 @@ let
           example = literalExample "pkgs.dockerTools.buildDockerImage {...};";
         };
 
+        login = {
+
+          username = mkOption {
+            type = with types; nullOr str;
+            default = null;
+            description = "Username for login.";
+          };
+
+          passwordFile = mkOption {
+            type = with types; nullOr str;
+            default = null;
+            description = "Path to file containing password.";
+            example = "/etc/nixos/dockerhub-password.txt";
+          };
+
+          registry = mkOption {
+            type = with types; nullOr str;
+            default = null;
+            description = "Registry where to login to.";
+            example = "https://docker.pkg.github.com";
+          };
+
+        };
+
         cmd = mkOption {
           type =  with types; listOf str;
           default = [];
@@ -220,6 +244,8 @@ let
       };
     };
 
+  isValidLogin = login: login.username != null && login.passwordFile != null && login.registry != null;
+
   mkService = name: container: let
     dependsOn = map (x: "${cfg.backend}-${x}.service") container.dependsOn;
   in {
@@ -235,6 +261,13 @@ let
 
     preStart = ''
       ${cfg.backend} rm -f ${name} || true
+      ${optionalString (isValidLogin container.login) ''
+        cat ${container.login.passwordFile} | \
+          ${cfg.backend} login \
+            ${container.login.registry} \
+            --username ${container.login.username} \
+            --password-stdin
+        ''}
       ${optionalString (container.imageFile != null) ''
         ${cfg.backend} load -i ${container.imageFile}
         ''}
diff --git a/nixpkgs/nixos/modules/virtualisation/vmware-guest.nix b/nixpkgs/nixos/modules/virtualisation/vmware-guest.nix
index 962a9059ea47..9465a8d6800d 100644
--- a/nixpkgs/nixos/modules/virtualisation/vmware-guest.nix
+++ b/nixpkgs/nixos/modules/virtualisation/vmware-guest.nix
@@ -56,5 +56,7 @@ in
           ${open-vm-tools}/bin/vmware-user-suid-wrapper
         '';
     };
+
+    services.udev.packages = [ open-vm-tools ];
   };
 }