1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.services.jupyterhub; 8 9 kernels = (pkgs.jupyter-kernel.create { 10 definitions = if cfg.kernels != null 11 then cfg.kernels 12 else pkgs.jupyter-kernel.default; 13 }); 14 15 jupyterhubConfig = pkgs.writeText "jupyterhub_config.py" '' 16 c.JupyterHub.bind_url = "http://${cfg.host}:${toString cfg.port}" 17 18 c.JupyterHub.authenticator_class = "${cfg.authentication}" 19 c.JupyterHub.spawner_class = "${cfg.spawner}" 20 21 c.SystemdSpawner.default_url = '/lab' 22 c.SystemdSpawner.cmd = "${cfg.jupyterlabEnv}/bin/jupyterhub-singleuser" 23 c.SystemdSpawner.environment = { 24 'JUPYTER_PATH': '${kernels}' 25 } 26 27 ${cfg.extraConfig} 28 ''; 29in { 30 meta.maintainers = with maintainers; [ costrouc ]; 31 32 options.services.jupyterhub = { 33 enable = mkEnableOption (lib.mdDoc "Jupyterhub development server"); 34 35 authentication = mkOption { 36 type = types.str; 37 default = "jupyterhub.auth.PAMAuthenticator"; 38 description = lib.mdDoc '' 39 Jupyterhub authentication to use 40 41 There are many authenticators available including: oauth, pam, 42 ldap, kerberos, etc. 43 ''; 44 }; 45 46 spawner = mkOption { 47 type = types.str; 48 default = "systemdspawner.SystemdSpawner"; 49 description = lib.mdDoc '' 50 Jupyterhub spawner to use 51 52 There are many spawners available including: local process, 53 systemd, docker, kubernetes, yarn, batch, etc. 54 ''; 55 }; 56 57 extraConfig = mkOption { 58 type = types.lines; 59 default = ""; 60 description = lib.mdDoc '' 61 Extra contents appended to the jupyterhub configuration 62 63 Jupyterhub configuration is a normal python file using 64 Traitlets. https://jupyterhub.readthedocs.io/en/stable/getting-started/config-basics.html. The 65 base configuration of this module was designed to have sane 66 defaults for configuration but you can override anything since 67 this is a python file. 68 ''; 69 example = '' 70 c.SystemdSpawner.mem_limit = '8G' 71 c.SystemdSpawner.cpu_limit = 2.0 72 ''; 73 }; 74 75 jupyterhubEnv = mkOption { 76 type = types.package; 77 default = pkgs.python3.withPackages (p: with p; [ 78 jupyterhub 79 jupyterhub-systemdspawner 80 ]); 81 defaultText = literalExpression '' 82 pkgs.python3.withPackages (p: with p; [ 83 jupyterhub 84 jupyterhub-systemdspawner 85 ]) 86 ''; 87 description = lib.mdDoc '' 88 Python environment to run jupyterhub 89 90 Customizing will affect the packages available in the hub and 91 proxy. This will allow packages to be available for the 92 extraConfig that you may need. This will not normally need to 93 be changed. 94 ''; 95 }; 96 97 jupyterlabEnv = mkOption { 98 type = types.package; 99 default = pkgs.python3.withPackages (p: with p; [ 100 jupyterhub 101 jupyterlab 102 ]); 103 defaultText = literalExpression '' 104 pkgs.python3.withPackages (p: with p; [ 105 jupyterhub 106 jupyterlab 107 ]) 108 ''; 109 description = lib.mdDoc '' 110 Python environment to run jupyterlab 111 112 Customizing will affect the packages available in the 113 jupyterlab server and the default kernel provided. This is the 114 way to customize the jupyterlab extensions and jupyter 115 notebook extensions. This will not normally need to 116 be changed. 117 ''; 118 }; 119 120 kernels = mkOption { 121 type = types.nullOr (types.attrsOf(types.submodule (import ../jupyter/kernel-options.nix { 122 inherit lib pkgs; 123 }))); 124 125 default = null; 126 example = literalExpression '' 127 { 128 python3 = let 129 env = (pkgs.python3.withPackages (pythonPackages: with pythonPackages; [ 130 ipykernel 131 pandas 132 scikit-learn 133 ])); 134 in { 135 displayName = "Python 3 for machine learning"; 136 argv = [ 137 "''${env.interpreter}" 138 "-m" 139 "ipykernel_launcher" 140 "-f" 141 "{connection_file}" 142 ]; 143 language = "python"; 144 logo32 = "''${env}/''${env.sitePackages}/ipykernel/resources/logo-32x32.png"; 145 logo64 = "''${env}/''${env.sitePackages}/ipykernel/resources/logo-64x64.png"; 146 }; 147 } 148 ''; 149 description = lib.mdDoc '' 150 Declarative kernel config 151 152 Kernels can be declared in any language that supports and has 153 the required dependencies to communicate with a jupyter server. 154 In python's case, it means that ipykernel package must always be 155 included in the list of packages of the targeted environment. 156 ''; 157 }; 158 159 port = mkOption { 160 type = types.port; 161 default = 8000; 162 description = lib.mdDoc '' 163 Port number Jupyterhub will be listening on 164 ''; 165 }; 166 167 host = mkOption { 168 type = types.str; 169 default = "0.0.0.0"; 170 description = lib.mdDoc '' 171 Bind IP JupyterHub will be listening on 172 ''; 173 }; 174 175 stateDirectory = mkOption { 176 type = types.str; 177 default = "jupyterhub"; 178 description = lib.mdDoc '' 179 Directory for jupyterhub state (token + database) 180 ''; 181 }; 182 }; 183 184 config = mkMerge [ 185 (mkIf cfg.enable { 186 systemd.services.jupyterhub = { 187 description = "Jupyterhub development server"; 188 189 after = [ "network.target" ]; 190 wantedBy = [ "multi-user.target" ]; 191 192 serviceConfig = { 193 Restart = "always"; 194 ExecStart = "${cfg.jupyterhubEnv}/bin/jupyterhub --config ${jupyterhubConfig}"; 195 User = "root"; 196 StateDirectory = cfg.stateDirectory; 197 WorkingDirectory = "/var/lib/${cfg.stateDirectory}"; 198 }; 199 }; 200 }) 201 ]; 202}