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}