The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Install JupyterLab in a dedicated Python virtual environment, run it as a non-root user, and keep its default listener on 127.0.0.1:8888. For private administration, connect through an SSH tunnel. If other people must reach it over the internet, put Jupyter Server behind an HTTPS reverse proxy, keep authentication enabled, and restrict the firewall. The procedure below covers installation, first login, persistent service management, public exposure, upgrades, and the failures most often encountered on an Ubuntu VPS.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
What you are building
This guide creates a single-user JupyterLab server on an Ubuntu virtual private server. The notebook service runs under a normal account, inside an isolated Python environment, and stores notebooks in that account’s home directory or another directory you explicitly choose. JupyterLab is free software; your VPS provider, storage, bandwidth, and any attached services are separate costs.
As an Amazon Associate I earn from qualifying purchases.
There are two access designs:
- Private administration: Jupyter listens only on loopback and your laptop reaches it through an SSH tunnel. This is the safest and simplest arrangement for one operator.
- Public web access: Jupyter remains on loopback while an HTTPS reverse proxy accepts connections on the public web port. This requires a domain or reachable address, a TLS certificate, authentication, proxy WebSocket support, and carefully scoped firewall rules.
Jupyter Server’s default behavior is local binding at 127.0.0.1:8888. A browser on your laptop cannot connect to that address on the VPS directly; the tunnel or proxy is the component that carries the connection.
Prepare the Ubuntu VPS
Create a dedicated, non-root account
Log in over SSH with an administrative account, then create a normal user for notebooks and services. Replace jupyter with the account name you want to use.
#1 Best Overall
sudo adduser jupyter
sudo usermod -aG sudo jupyter
Using a separate account limits the damage a notebook can do compared with running kernels as root. If the account does not need administrative privileges after setup, remove it from the sudo group and perform maintenance through a separate administrator account.
Apply updates and verify SSH first
sudo apt update
sudo apt upgrade
Open a second terminal and confirm that you can sign in as the new account before changing any firewall or SSH settings. Keep an existing administrative session open while testing so a mistake does not lock you out.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Choose a data directory
The account’s home directory is adequate for a small installation. For larger notebooks or datasets, create a dedicated mount point and grant ownership to the service account:
sudo mkdir -p /srv/jupyter
sudo chown -R jupyter:jupyter /srv/jupyter
Plan backups for this directory. A virtual machine snapshot is not a substitute for an independent backup, and notebooks may contain credentials, personal data, or proprietary code.
Install JupyterLab in an isolated Python environment
Use Ubuntu’s packaged Python, virtual-environment support, and pip. Do not install the application into the system interpreter, where OS packages and your project dependencies can conflict.
sudo apt update
sudo apt install python3 python3-venv python3-pip
Switch to the notebook account and create a virtual environment:
Recommended Free Tools
sudo -iu jupyter
python3 -m venv ~/venvs/jupyterlab
source ~/venvs/jupyterlab/bin/activate
python -m pip install --upgrade pip
python -m pip install jupyterlab
The source command affects only the current shell. Every manual start and every service definition must use the environment’s executable, or explicitly activate it first. Using an absolute path such as /home/jupyter/venvs/jupyterlab/bin/jupyter is more reliable for automation.
Alternative installation methods
| Option | Best fit | Trade-offs to evaluate |
|---|---|---|
venv plus pip |
Small, single-user VPS | Lightweight and familiar; you are responsible for dependency upgrades and resolving conflicts. |
| conda or mamba | Data-science stacks with compiled dependencies | Convenient environment management, but a larger disk footprint and another package ecosystem. |
| uv | Teams adopting modern Python tooling | Fast environment and package operations; confirm that your team and operational policies support it. |
| Docker | Reproducible, image-based deployments | Adds container operations, image updates, and persistent-volume planning. When using Jupyter’s Docker image, set JUPYTER_ENABLE_LAB=yes. |
| JupyterHub | Several users needing separate accounts | More components and administration, but it is the official multi-user architecture rather than a shared single-user server. |
Compare an option’s dependency isolation, reproducibility, upgrade path, memory and disk requirements, backup design, and number of users—not just the initial install command.
Rank #2
Start JupyterLab and verify it locally
From the activated environment, start the server without trying to open a browser on the VPS:
jupyter lab --no-browser
The terminal prints a URL containing an authentication token, usually on the local endpoint. Leave this process running while you test it. In another shell, you can list running servers and their URLs:
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsjupyter server list
At this stage, Jupyter should listen on 127.0.0.1:8888, not on every network interface. That loopback binding is intentional: it prevents an internet client from reaching the notebook service before you have selected and secured an access method.
Access it privately through an SSH tunnel
For one administrator, an SSH tunnel avoids opening a Jupyter port in the VPS firewall. Keep Jupyter bound to loopback, then run this command on your laptop:
ssh -N -L 8888:127.0.0.1:8888 jupyter@VPS_IP
The first 8888 is the laptop’s local port. The address and second port identify the service as seen from the VPS. If local port 8888 is already occupied, use another local port:
ssh -N -L 8899:127.0.0.1:8888 jupyter@VPS_IP
Now open http://127.0.0.1:8888 (or port 8899 in the second example) on the laptop and paste the token printed by Jupyter. Keep the SSH process running; closing it closes the forwarding path. You can add SSH keepalive settings in your client configuration if an idle connection is routinely dropped.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallKeep authentication enabled
The launch token is an effective first login method. Do not disable both token and password unless another, fully managed authentication layer replaces them. Anyone who obtains an unauthenticated notebook endpoint can execute code as the service account.
Set a password
With the virtual environment active, run:
jupyter server password
Jupyter prompts for the password and stores a hash in jupyter_server_config.json; it does not need to keep the clear-text password in that file. Use a long, unique password and protect the configuration and home directory with normal Unix permissions.
After setting a password, a login page can accept it instead of the one-time URL token. Keep the token mechanism available during initial setup and recovery unless you have deliberately configured another authentication provider.
Make public access safe enough for production
Public access is a different deployment from an SSH tunnel. Do not simply bind Jupyter to 0.0.0.0 and rely on obscurity. Use a fixed internal port, authentication, TLS, and a reverse proxy that forwards WebSocket traffic.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Configure Jupyter for a proxy
Set the server to avoid launching a local browser and to use a known port. A configuration file lets you keep these choices consistent across restarts. Generate one if needed:
jupyter server --generate-config
Edit the resulting Jupyter Server configuration and set the equivalent of:
c.ServerApp.ip = '127.0.0.1'
c.ServerApp.port = 8888
c.ServerApp.open_browser = False
Keep the loopback address when the reverse proxy runs on the same VPS. If you intentionally expose Jupyter directly, use a narrowly scoped firewall rule and still require authentication and HTTPS; a proxy is generally easier to harden and to renew certificates for.
Terminate TLS at the reverse proxy
Point your DNS name at the VPS, obtain a certificate from a trusted certificate authority, and configure an HTTPS reverse proxy such as Nginx or Caddy. The proxy should:
- Listen publicly on TCP 443 and redirect plain HTTP to HTTPS.
- Forward normal HTTP requests to
127.0.0.1:8888. - Forward the
UpgradeandConnectionheaders required for WebSockets. - Preserve the Jupyter authentication layer rather than replacing it with an accidental unauthenticated route.
- Apply sensible request-size, timeout, and access-log policies for your workload.
Jupyter kernels use WebSockets for interactive communication. A page may load while terminals and notebooks fail if the proxy omits WebSocket forwarding or applies an overly restrictive content-security policy.
Use HTTPS with password authentication
When a password is accepted on a public endpoint, TLS is mandatory so the credential is encrypted in transit. Jupyter’s public-server guidance also supports supplying certificate and key files directly, but a reverse proxy commonly centralizes certificate renewal and public firewall policy. Never send a password over an unencrypted public HTTP connection.
Run JupyterLab reliably with systemd
A shell process ends when its SSH session ends and will not automatically recover from a crash. A systemd unit runs the virtual-environment executable as the dedicated account, sets the notebook directory, disables browser launching, and restarts after failure.
Create /etc/systemd/system/jupyter.service as an administrator:
[Unit]
Description=JupyterLab
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=jupyter
Group=jupyter
WorkingDirectory=/srv/jupyter
ExecStart=/home/jupyter/venvs/jupyterlab/bin/jupyter lab --no-browser --ip=127.0.0.1 --port=8888
Restart=on-failure
RestartSec=5
# Add environment-specific limits deliberately; do not copy arbitrary values.
[Install]
WantedBy=multi-user.target
Adapt the paths, account, port, and working directory. The Jupyter documentation does not prescribe a canonical systemd unit, so treat this as deployment glue and review it against your VPS’s resource limits and security policy.
Enable and start it:
sudo systemctl daemon-reload
sudo systemctl enable --now jupyter.service
sudo systemctl status jupyter.service
sudo journalctl -u jupyter.service -f
Keep passwords and other secrets in protected Jupyter configuration or an approved secret-management method. Do not put credentials directly in a world-readable unit file or shell history. If you change the environment or configuration, restart the service and inspect the journal for the actual executable path and bind address.
Configure the VPS firewall
At minimum, allow SSH from the addresses and ports you actually use. For a reverse-proxy deployment, allow HTTPS on TCP 443 and normally leave Jupyter’s 8888 port closed to the internet. If you expose Jupyter directly, allow its configured port only from trusted source addresses—not from the entire internet.
Jupyter’s local kernel communication may use randomly selected ports in the range 49152–65535. Do not broadly open that range. Keeping the server and kernels on loopback behind the proxy avoids exposing those internal channels. Verify both the VPS firewall and any provider-level security group; either layer can block a correctly configured service.
When to choose JupyterHub instead
A single-user remote server is appropriate when one operator owns the notebooks and the VPS. It is not the multi-user solution. Choose JupyterHub when several people need independent authenticated sessions, separate home directories, per-user permissions, or centrally managed spawning and resource limits. JupyterHub adds administration and components, so do not introduce it merely to serve one account.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Upgrade and backup routine
Upgrade the isolated environment
Schedule maintenance rather than upgrading a running production kernel unexpectedly:
Best Value
sudo -iu jupyter
source ~/venvs/jupyterlab/bin/activate
python -m pip install --upgrade jupyterlab jupyter-server
python -m pip check
exit
sudo systemctl restart jupyter.service
Test a representative notebook after upgrades. For reproducibility, record the environment after a known-good change:
Free tools Windows power users keep installed
One-click scans. No signup required.
sudo -iu jupyter
source ~/venvs/jupyterlab/bin/activate
python -m pip freeze > ~/requirements-lock.txt
Review that file before restoring it on another machine; a freeze captures versions, but it does not replace a tested backup and does not include external services or system packages.
Back up notebooks and configuration
Back up the notebook directory, Jupyter configuration, environment requirements, and any reverse-proxy configuration. Encrypt backups when notebooks contain secrets or personal data. Test restoration on a separate directory or VPS; a backup that has never been restored is only an assumption.
Troubleshooting checklist
jupyter: command not found
Activate the virtual environment, or call its executable with the full path. If you used a user-level pip install instead, the executable may be under $HOME/.local/bin; add that directory to the account’s PATH or use the environment-based installation described above.
The browser cannot connect remotely
Check the service log and confirm whether it is intentionally listening on 127.0.0.1. That address is local to the VPS, not your laptop. Use the SSH tunnel for private access or verify that the reverse proxy forwards to the correct loopback port. Do not “fix” the symptom by exposing every interface before securing authentication and TLS.
The login page loops or rejects a known password
Obtain the current URL and token with jupyter server list. If the password is the problem, run jupyter server password again as the same account that owns the service, then restart it. Confirm that the systemd unit is using the same home directory and configuration location as your manual shell.
HTTPS loads, but terminals or kernels do not work
Inspect the reverse-proxy error log and verify WebSocket upgrade forwarding. Check for a restrictive content-security policy or a proxy timeout that closes idle kernel connections. The browser may render the initial HTML even when the WebSocket channel is broken.
A kernel starts and then dies
Read journalctl -u jupyter.service and the Jupyter log. Confirm that the selected kernel has the packages your notebook imports and that the kernel points to the intended virtual environment. Check available RAM, swap, disk space, and any provider or systemd resource limits; a notebook can be killed even when the Jupyter web process remains healthy.
The service works manually but not under systemd
Compare the manual command with ExecStart, including the executable path, user, working directory, port, and configuration search path. Systemd does not load your interactive shell profile, so aliases, activated environments, and user-level PATH changes are not automatically present.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Quick Recap
Security checks before you share the URL
- Jupyter runs as a non-root account.
- The service uses the intended virtual environment and notebook directory.
- Authentication remains enabled; neither token nor password was disabled accidentally.
- Public credentials are protected by HTTPS.
- The firewall exposes only SSH and the required HTTPS port, or restricts a direct Jupyter port to trusted addresses.
- Reverse-proxy WebSockets work in a real terminal and notebook session.
- Notebook and configuration backups are encrypted where appropriate and have been tested.
- Logs, disk usage, memory, and certificate expiry are monitored.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




