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