1 { config, lib, pkgs, ... }:
4 cfg = config.services.netbox;
5 pythonFmt = pkgs.formats.pythonVars {};
6 staticDir = cfg.dataDir + "/static";
8 settingsFile = pythonFmt.generate "netbox-settings.py" cfg.settings;
9 extraConfigFile = pkgs.writeTextFile {
10 name = "netbox-extraConfig.py";
11 text = cfg.extraConfig;
13 configFile = pkgs.concatText "configuration.py" [ settingsFile extraConfigFile ];
15 pkg = (cfg.package.overrideAttrs (old: {
16 installPhase = old.installPhase + ''
17 ln -s ${configFile} $out/opt/netbox/netbox/netbox/configuration.py
18 '' + lib.optionalString cfg.enableLdap ''
19 ln -s ${cfg.ldapConfigPath} $out/opt/netbox/netbox/netbox/ldap_config.py
22 inherit (cfg) plugins;
24 netboxManageScript = with pkgs; (writeScriptBin "netbox-manage" ''
26 export PYTHONPATH=${pkg.pythonPath}
27 sudo -u netbox ${pkg}/bin/netbox "$@"
31 options.services.netbox = {
32 enable = lib.mkOption {
33 type = lib.types.bool;
38 This module requires a reverse proxy that serves `/static` separately.
39 See this [example](https://github.com/netbox-community/netbox/blob/develop/contrib/nginx.conf/) on how to configure this.
43 settings = lib.mkOption {
45 Configuration options to set in `configuration.py`.
46 See the [documentation](https://docs.netbox.dev/en/stable/configuration/) for more possible options.
51 type = lib.types.submodule {
52 freeformType = pythonFmt.type;
55 ALLOWED_HOSTS = lib.mkOption {
56 type = with lib.types; listOf str;
59 A list of valid fully-qualified domain names (FQDNs) and/or IP
60 addresses that can be used to reach the NetBox service.
67 listenAddress = lib.mkOption {
71 Address the server will listen on.
75 package = lib.mkOption {
76 type = lib.types.package;
78 if lib.versionAtLeast config.system.stateVersion "24.11"
80 else if lib.versionAtLeast config.system.stateVersion "24.05"
83 defaultText = lib.literalExpression ''
84 if lib.versionAtLeast config.system.stateVersion "24.11"
86 else if lib.versionAtLeast config.system.stateVersion "24.05"
91 NetBox package to use.
96 type = lib.types.port;
99 Port the server will listen on.
103 plugins = lib.mkOption {
104 type = with lib.types; functionTo (listOf package);
106 defaultText = lib.literalExpression ''
107 python3Packages: with python3Packages; [];
110 List of plugin packages to install.
114 dataDir = lib.mkOption {
115 type = lib.types.str;
116 default = "/var/lib/netbox";
118 Storage path of netbox.
122 secretKeyFile = lib.mkOption {
123 type = lib.types.path;
125 Path to a file containing the secret key.
129 extraConfig = lib.mkOption {
130 type = lib.types.lines;
133 Additional lines of configuration appended to the `configuration.py`.
134 See the [documentation](https://docs.netbox.dev/en/stable/configuration/) for more possible options.
138 enableLdap = lib.mkOption {
139 type = lib.types.bool;
142 Enable LDAP-Authentication for Netbox.
144 This requires a configuration file being pass through `ldapConfigPath`.
148 ldapConfigPath = lib.mkOption {
149 type = lib.types.path;
152 Path to the Configuration-File for LDAP-Authentication, will be loaded as `ldap_config.py`.
153 See the [documentation](https://netbox.readthedocs.io/en/stable/installation/6-ldap/#configuration) for possible options.
157 from django_auth_ldap.config import LDAPSearch, PosixGroupType
159 AUTH_LDAP_SERVER_URI = "ldaps://ldap.example.com/"
161 AUTH_LDAP_USER_SEARCH = LDAPSearch(
162 "ou=accounts,ou=posix,dc=example,dc=com",
167 AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
168 "ou=groups,ou=posix,dc=example,dc=com",
170 "(objectClass=posixGroup)",
172 AUTH_LDAP_GROUP_TYPE = PosixGroupType()
174 # Mirror LDAP group assignments.
175 AUTH_LDAP_MIRROR_GROUPS = True
177 # For more granular permissions, we can map LDAP groups to Django groups.
178 AUTH_LDAP_FIND_GROUP_PERMS = True
181 keycloakClientSecret = lib.mkOption {
182 type = with lib.types; nullOr path;
185 File that contains the keycloak client secret.
190 config = lib.mkIf cfg.enable {
192 plugins = lib.mkIf cfg.enableLdap (ps: [ ps.django-auth-ldap ]);
194 STATIC_ROOT = staticDir;
195 MEDIA_ROOT = "${cfg.dataDir}/media";
196 REPORTS_ROOT = "${cfg.dataDir}/reports";
197 SCRIPTS_ROOT = "${cfg.dataDir}/scripts";
199 GIT_PATH = "${pkgs.gitMinimal}/bin/git";
204 HOST = "/run/postgresql";
207 # Redis database settings. Redis is used for caching and for queuing
208 # background tasks such as webhook events. A separate configuration
209 # exists for each. Full connection details are required in both
210 # sections, and it is strongly recommended to use two separate database
214 URL = "unix://${config.services.redis.servers.netbox.unixSocket}?db=0";
218 URL = "unix://${config.services.redis.servers.netbox.unixSocket}?db=1";
223 REMOTE_AUTH_BACKEND = lib.mkIf cfg.enableLdap "netbox.authentication.LDAPBackend";
225 LOGGING = lib.mkDefault {
228 formatters.precise.format = "[%(levelname)s@%(name)s] %(message)s";
231 class = "logging.StreamHandler";
232 formatter = "precise";
235 # log to console/systemd instead of file
238 handlers = [ "console" ];
244 with open("${cfg.secretKeyFile}", "r") as file:
245 SECRET_KEY = file.readline()
246 '' + (lib.optionalString (cfg.keycloakClientSecret != null) ''
247 with open("${cfg.keycloakClientSecret}", "r") as file:
248 SOCIAL_AUTH_KEYCLOAK_SECRET = file.readline()
252 services.redis.servers.netbox.enable = true;
254 services.postgresql = {
256 ensureDatabases = [ "netbox" ];
260 ensureDBOwnership = true;
265 environment.systemPackages = [ netboxManageScript ];
267 systemd.targets.netbox = {
268 description = "Target for all NetBox services";
269 wantedBy = [ "multi-user.target" ];
270 wants = [ "network-online.target" ];
271 after = [ "network-online.target" "redis-netbox.service" ];
274 systemd.services = let
275 defaultServiceConfig = {
276 WorkingDirectory = "${cfg.dataDir}";
279 StateDirectory = "netbox";
280 StateDirectoryMode = "0750";
281 Restart = "on-failure";
286 description = "NetBox WSGI Service";
287 documentation = [ "https://docs.netbox.dev/" ];
289 wantedBy = [ "netbox.target" ];
291 after = [ "network-online.target" ];
292 wants = [ "network-online.target" ];
294 environment.PYTHONPATH = pkg.pythonPath;
297 # On the first run, or on upgrade / downgrade, run migrations and related.
298 # This mostly correspond to upstream NetBox's 'upgrade.sh' script.
299 versionFile="${cfg.dataDir}/version"
301 if [[ -h "$versionFile" && "$(readlink -- "$versionFile")" == "${cfg.package}" ]]; then
305 ${pkg}/bin/netbox migrate
306 ${pkg}/bin/netbox trace_paths --no-input
307 ${pkg}/bin/netbox collectstatic --clear --no-input
308 ${pkg}/bin/netbox remove_stale_contenttypes --no-input
309 ${pkg}/bin/netbox reindex --lazy
310 ${pkg}/bin/netbox clearsessions
312 # The clearcache command was removed in 3.7.0:
313 # https://github.com/netbox-community/netbox/issues/14458
314 (lib.versionOlder cfg.package.version "3.7.0")
315 "${pkg}/bin/netbox clearcache"}
317 ln -sfn "${cfg.package}" "$versionFile"
320 serviceConfig = defaultServiceConfig // {
322 ${pkg.gunicorn}/bin/gunicorn netbox.wsgi \
323 --bind ${cfg.listenAddress}:${toString cfg.port} \
324 --pythonpath ${pkg}/opt/netbox/netbox
327 TimeoutStartSec = lib.mkDefault "5min";
332 description = "NetBox Request Queue Worker";
333 documentation = [ "https://docs.netbox.dev/" ];
335 wantedBy = [ "netbox.target" ];
336 after = [ "netbox.service" ];
338 environment.PYTHONPATH = pkg.pythonPath;
340 serviceConfig = defaultServiceConfig // {
342 ${pkg}/bin/netbox rqworker high default low
348 netbox-housekeeping = {
349 description = "NetBox housekeeping job";
350 documentation = [ "https://docs.netbox.dev/" ];
352 wantedBy = [ "multi-user.target" ];
354 after = [ "network-online.target" "netbox.service" ];
355 wants = [ "network-online.target" ];
357 environment.PYTHONPATH = pkg.pythonPath;
359 serviceConfig = defaultServiceConfig // {
362 ${pkg}/bin/netbox housekeeping
368 systemd.timers.netbox-housekeeping = {
369 description = "Run NetBox housekeeping job";
370 documentation = [ "https://docs.netbox.dev/" ];
372 wantedBy = [ "multi-user.target" ];
374 after = [ "network-online.target" "netbox.service" ];
375 wants = [ "network-online.target" ];
378 OnCalendar = "daily";
384 users.users.netbox = {
385 home = "${cfg.dataDir}";
389 users.groups.netbox = {};
390 users.groups."${config.services.redis.servers.netbox.user}".members = [ "netbox" ];