···
1
+
{ config, lib, pkgs, ... }:
7
+
cfg = config.services.jupyter;
9
+
# NOTE: We don't use top-level jupyter because we don't
10
+
# want to pass in JUPYTER_PATH but use .environment instead,
12
+
package = pkgs.python3.pkgs.notebook;
14
+
kernels = (pkgs.jupyter-kernel.create {
15
+
definitions = if cfg.kernels != null
17
+
else pkgs.jupyter-kernel.default;
20
+
notebookConfig = pkgs.writeText "jupyter_config.py" ''
21
+
${cfg.notebookConfig}
23
+
c.NotebookApp.password = ${cfg.password}
27
+
meta.maintainers = with maintainers; [ aborsu ];
29
+
options.services.jupyter = {
30
+
enable = mkEnableOption "Jupyter development server";
34
+
default = "localhost";
36
+
IP address Jupyter will be listening on.
44
+
Port number Jupyter will be listening on.
48
+
notebookDir = mkOption {
52
+
Root directory for notebooks.
58
+
default = "jupyter";
60
+
Name of the user used to run the jupyter service.
61
+
For security reason, jupyter should really not be run as root.
62
+
If not set (jupyter), the service will create a jupyter user with appropriate settings.
69
+
default = "jupyter";
71
+
Name of the group used to run the jupyter service.
72
+
Use this if you want to create a group of users that are able to view the notebook directory's content.
77
+
password = mkOption {
80
+
Password to use with notebook.
81
+
Can be generated using:
82
+
In [1]: from notebook.auth import passwd
83
+
In [2]: passwd('test')
84
+
Out[2]: 'sha1:1b961dc713fb:88483270a63e57d18d43cf337e629539de1436ba'
85
+
NOTE: you need to keep the single quote inside the nix string.
86
+
Or you can use a python oneliner:
87
+
"open('/path/secret_file', 'r', encoding='utf8').read().strip()"
88
+
It will be interpreted at the end of the notebookConfig.
91
+
"'sha1:1b961dc713fb:88483270a63e57d18d43cf337e629539de1436ba'"
92
+
"open('/path/secret_file', 'r', encoding='utf8').read().strip()"
96
+
notebookConfig = mkOption {
100
+
Raw jupyter config.
104
+
kernels = mkOption {
105
+
type = types.nullOr (types.attrsOf(types.submodule (import ./kernel-options.nix {
110
+
example = literalExample ''
113
+
env = (pkgs.python3.withPackages (pythonPackages: with pythonPackages; [
119
+
displayName = "Python 3 for machine learning";
121
+
"$ {env.interpreter}"
123
+
"ipykernel_launcher"
125
+
"{connection_file}"
127
+
language = "python";
128
+
logo32 = "$ {env.sitePackages}/ipykernel/resources/logo-32x32.png";
129
+
logo64 = "$ {env.sitePackages}/ipykernel/resources/logo-64x64.png";
133
+
description = "Declarative kernel config
135
+
Kernels can be declared in any language that supports and has the required
136
+
dependencies to communicate with a jupyter server.
137
+
In python's case, it means that ipykernel package must always be included in
138
+
the list of packages of the targeted environment.
145
+
systemd.services.jupyter = {
146
+
description = "Jupyter development server";
148
+
wantedBy = [ "multi-user.target" ];
150
+
# TODO: Patch notebook so we can explicitly pass in a shell
151
+
path = [ pkgs.bash ]; # needed for sh in cell magic to work
154
+
JUPYTER_PATH = toString kernels;
158
+
Restart = "always";
159
+
ExecStart = ''${package}/bin/jupyter-notebook \
162
+
--port=${toString cfg.port} --port-retries 0 \
163
+
--notebook-dir=${cfg.notebookDir} \
164
+
--NotebookApp.config_file=${notebookConfig}
168
+
WorkingDirectory = "~";
172
+
(mkIf (cfg.enable && (cfg.group == "jupyter")) {
173
+
users.groups.jupyter = {};
175
+
(mkIf (cfg.enable && (cfg.user == "jupyter")) {
176
+
users.extraUsers.jupyter = {
177
+
extraGroups = [ cfg.group ];
178
+
home = "/var/lib/jupyter";
180
+
useDefaultShell = true; # needed so that the user can start a terminal.