Setting Up Jupyter Lab on Digital Ocean Droplet

Timestamp Notes
0:38 Deployed the LTS option Ubuntu 24.04 x64
2:43 Enabled the Uncomplicated Firewall before creating an A record
4:03 created A record
login as root
4:37 ran sudo adduser --disabled-password --gecos "" jupyter
4:41 ran sudo usermod -aG sudo jupyter
4:46 ran apt update
4:59 ran apt install -y python3-venv python3-pip nginx ufw
5:38 ran -iu jupyter which failed
5:53 ran sudo -iu jupyter which worked
5:59 ran python3 -m venv ~/jlab-venv
6:08 ran ~/jlab-venv/bin/pip install --upgrade pip
6:17 ran ~/jlab-venv/bin/pip install jupyterlab jupyterlab-git
6:49 ran ~/jlab-venv/bin/jupyter server --generate-config
7:01 ran python3 -c "from jupyter_server.auth import passwd; print(passwd())" which failed
7:44 ran ~/jlab-venv/bin/python -c "from jupyter_server.auth import passwd; print(passwd())"
7:46 entered the password that will be used to login to Jupyter Lab webpage
12:20 used PuttyGen to get OpenSSH key from the .ppk

17:13

in cmd i ran scp -i C:\Users\ppyem\Documents\id_rsa root@134.209.30.67:/home/jupyter/.jupyter/jupyter_server_config.py C:\Users\ppyem\Documents\ to get the full configuration file locally

Timestamp Notes
18:57 i ran nano ~/.jupyter/jupyter_server_config.py

20:09

i appended to bootom

c.ServerApp.ip = "127.0.0.1"
c.ServerApp.port = 8888
c.ServerApp.open_browser = False
c.ServerApp.allow_remote_access = True
c.ServerApp.trust_xheaders = True

# require login everywhere
c.ServerApp.allow_unauthenticated_access = False

# authentication
c.ServerApp.token = ""
c.ServerApp.password = "argon2:$argon2id$v=19$m=10240,t=10,p=8$XOEmNWiKRa+XjdjRYROfPg$ob4eQEU3/Cp+uL9dfT6SF9XHMulApkh6y/fELz3C61A"
Timestamp Notes
20:29 i ran nano ~/.jupyter/jupyter_server_config.py
20:30 i ran ls -l ~/.jupyter/jupyter_server_config.py
20:52 i ran chmod 600 /home/jupyter/.jupyter/jupyter_server_config.py
20:59 i ran sudo systemctl restart jupyterlab which failed becuase there’s no such password to run this command
23:56 ran exit to change user from jupyter to root

24:29

ran

printf 'Cmnd_Alias JLABCMDS = /bin/systemctl restart jupyterlab, /bin/systemctl status jupyterlab, /bin/systemctl stop jupyterlab, /bin/systemctl start jupyterlab\njupyter ALL=(root) NOPASSWD: JLABCMDS\n' > /etc/sudoers.d/jupyter-jupyterlab
chmod 440 /etc/sudoers.d/jupyter-jupyterlab
Timestamp Notes
24:37 ran ls -l /etc/sudoers.d/jupyter-jupyterlab
24:43 ran cat /etc/sudoers.d/jupyter-jupyterlab
25:10 ran exit
can’t login to user jupyter via PuTTy
26:37 logged in as root
26:52 ran install -d -m 700 -o jupyter -g jupyter /home/jupyter/.ssh
26:56 ran cp /root/.ssh/authorized_keys /home/jupyter/.ssh/authorized_keys
27:02 ran chown jupyter:jupyter /home/jupyter/.ssh/authorized_keys
27:07 ran chmod 600 /home/jupyter/.ssh/authorized_keys
27:13 ran ls -l /home/jupyter/.ssh/authorized_keys
27:25 logged out
27:58 logged in via SSH key as jupyter
28:05 ran sudo systemctl restart jupyterlab which failed due to non-existant service unit
28:35 ran sudo tee /etc/systemd/system/jupyterlab.service >/dev/null <<'EOF'
[Unit]
Description=JupyterLab (proxied by Nginx)
After=network.target

[Service]
Type=simple
User=jupyter
Group=jupyter
WorkingDirectory=/home/jupyter
Environment="PATH=/home/jupyter/jlab-venv/bin:/usr/bin"
ExecStart=/home/jupyter/jlab-venv/bin/jupyter lab
Restart=on-failure
RestartSec=5

# Hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true

[Install]
WantedBy=multi-user.target
EOF
Timestamp Notes
32:33 ran systemctl daemon-reload
32:37 ran systemctl enable --now jupyterlab
32:43 ran systemctl status jupyterlab --no-pager
33:08 ran journalctl -u jupyterlab -e --no-pager
33:12 ran sudo -u jupyter /home/jupyter/jlab-venv/bin/jupyter --version
33:43 ran nano /etc/systemd/system/jupyterlab.service
33:53 replace a line to ExecStart=/home/jupyter/jlab-venv/bin/python3 -m jupyterlab --no-browser --config=/home/jupyter/.jupyter/jupyter_server_config.py
34:08 ran systemctl daemon-reload
34:13 systemctl restart jupyterlab
34:25 ran journalctl -u jupyterlab -e --no-pager
35:17 ran nano /etc/systemd/system/jupyterlab.service
35:27 grey-ed out ProtectHome
35:45 ran systemctl daemon-reload
35:49 ran systemctl restart jupyterlab
35:54 ran systemctl status jupyterlab --no-pager
36:24 ran apt update
36:34 ran apt install -y nginx certbot python3-certbot-nginx
36:45 ran ufw allow OpenSSH
36:52 ran ufw allow 'Nginx Full'
36:57 ran ufw enable
new putty session on root user
37:36 ran tee /etc/nginx/sites-available/jupyterlab >/dev/null <<'EOF'
same as nano /etc/nginx/sites-available/jupyterlab
# Simple rate limit to protect /login from bots
limit_req_zone $binary_remote_addr zone=jrate:10m rate=10r/s;

server {
    listen 80;
    server_name lab.example.com;

    # Security headers (added on HTTP too; Certbot will keep them on HTTPS)
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    add_header Referrer-Policy strict-origin-when-cross-origin;

    location / {
        limit_req zone=jrate burst=20 nodelay;

        proxy_pass http://127.0.0.1:8888;
        proxy_http_version 1.1;

        # Websocket + proxy headers Jupyter needs
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        client_max_body_size 64m;
        proxy_read_timeout 600;
        proxy_send_timeout 600;
    }
}
Timestamp Notes
40:21 ran ln -s /etc/nginx/sites-available/jupyterlab /etc/nginx/sites-enabled/jupyterlab
40:29 ran nginx -t && systemctl reload nginx
40:40 ran certbot --nginx -d lab.example.com --redirect -m you@example.com --agree-tos -n
41:25 ran certbot renew --dry-run
49:25 i show Jupyter Lab up and running with Let’s Encrypt HTTPS

Here’s a new (untested) set of commands that might lead to a more secure outcome;

  1. Deployed the LTS option Ubuntu 24.04 x64
  2. ssh root@<your_server_ip>
  3. ufw allow http
  4. ufw allow https
  5. ufw allow ssh
  6. ufw enable
  7. adduser --disabled-password --gecos "" jupyter
  8. usermod -aG sudo jupyter
  9. su - jupyter
  10. sudo apt update && sudo apt upgrade -y
  11. exit
  12. passwd jupyter
  13. su - jupyter
  14. sudo apt update && sudo apt upgrade -y
  15. sudo apt install -y python3-pip python3-venv
  16. python3 -m venv ~/jupyterlab-env
  17. source ~/jupyterlab-env/bin/activate
  18. pip install --upgrade pip jupyterlab
  19. jupyter lab --generate-config
  20. jupyter lab password
  1. sudo nano /etc/systemd/system/jupyterlab.service
[Unit]
Description=JupyterLab
After=network.target

[Service]
Type=simple
User=jupyter
WorkingDirectory=/home/jupyter
Environment="PATH=/home/jupyter/jupyterlab-env/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
ExecStart=/home/jupyter/jupyterlab-env/bin/jupyter lab --no-browser --ip=127.0.0.1 --port=8888
Restart=on-failure

[Install]
WantedBy=multi-user.target
  1. sudo systemctl daemon-reload
  2. sudo systemctl enable jupyterlab
  3. sudo systemctl start jupyterlab
  4. sudo systemctl status jupyterlab
  5. sudo apt install -y nginx
  6. sudo nano /etc/nginx/sites-available/jupyterlab
server {
    listen 80;
    server_name lab.year2physics.site;

    location / {
        proxy_pass http://127.0.0.1:8888;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Increase the buffer size for large notebooks
        proxy_buffer_size          128k;
        proxy_buffers              4 256k;
        proxy_busy_buffers_size    256k;
    }
}
  1. sudo ln -s /etc/nginx/sites-available/jupyterlab /etc/nginx/sites-enabled/
  2. sudo nginx -t
  3. sudo systemctl reload nginx
  4. sudo apt install -y certbot python3-certbot-nginx
  5. sudo certbot --nginx -d lab.year2physics.site
  6. sudo nano /etc/nginx/sites-available/jupyterlab
  7. you may need to replace your config so Nginx preserves /lab when proxying:
server {
    listen 80;
    server_name lab.year2physics.site;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name lab.year2physics.site;

    ssl_certificate /etc/letsencrypt/live/lab.year2physics.site/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/lab.year2physics.site/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    client_max_body_size 64M;

    # redirect root to /lab
    location = / {
        return 302 /lab;
    }

    # proxy all /lab routes
    location /lab/ {
        proxy_pass http://127.0.0.1:8888/lab/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_buffer_size          128k;
        proxy_buffers              4 256k;
        proxy_busy_buffers_size    256k;
    }
}
  1. run sudo nano /home/jupyter/.jupyter/jupyter_server_config.json
  2. then replace to, merging your password hash
{
  "IdentityProvider": {
    "hashed_password": "argon2:$argon2id$v=19$m=10240,t=10,p=8$Kucdq4m2c8qoTBbjRsP6Ig$rXB/oS24X2nWWlbGPYbxbQzoQAvlQj..."
  },
  "ServerApp": {
    "ip": "127.0.0.1",
    "port": 8888,
    "open_browser": false,
    "allow_remote_access": true,
    "trust_xheaders": true,
    "token": "",
    "base_url": "/lab",
    "allow_origin": "https://lab.year2physics.site",
    "allow_credentials": true
  }
}
  1. run sudo systemctl restart jupyterlab
  2. shoul;d be working now
1 Like