jupyterlab
JupyterLab is the browser-based workbench for Project Jupyter: notebooks, a terminal, a file browser, a text editor, a variable inspector, and a visual debugger, all in one tabbed layout you can split and rearrange. It is not a library you import. Installing it gives you a jupyter lab command that starts a local web server (Jupyter Server) which talks to language kernels over ZeroMQ, so the Python running your cells is a separate process from the UI. Everything in the interface is a plugin, and third-party extensions ship as ordinary pip packages that need no build step since version 3.
The default interactive computing environment for Python, and the right choice when the work is exploratory, remote, or heavy on plots and widgets. It is the wrong choice for scheduled execution, for multiple users, and for code that has to survive review, so know which of those you are doing before you install it.
Use it if
- You do exploratory work where you want to keep a long-lived Python process alive and poke at it: loading a big dataframe once and iterating on the analysis over it for an hour is what notebooks are actually good at
- You want rich output next to the code: matplotlib figures, HTML tables, ipywidgets sliders, and Markdown notes in one scrollable document that a colleague can read top to bottom
- You need to work on a remote machine's GPU or data without copying it locally: run jupyter lab on the box, forward a port, and get a full editing environment against remote files
- You want the extension shelf: git integration, LSP-backed autocomplete and diagnostics, real-time collaboration, and language kernels beyond Python all install as pip packages
- You only need a kernel for an editor. VS Code, PyCharm, and Positron all speak the Jupyter protocol directly, and pip install ipykernel is the whole dependency; JupyterLab drags in jupyter-server, jupyterlab-server, jupyter-lsp, tornado, httpx, notebook-shim, and tens of megabytes of bundled JavaScript you will never look at
- You want to run notebooks on a schedule or in CI. JupyterLab is an interactive front end with no scheduler and no parameterisation; jupyter nbconvert --execute or papermill is the right tool and needs none of the UI
- More than one person needs to use the server. A jupyter lab process is single-user by design: everyone who reaches it runs code as the same OS account with the same filesystem access. Multi-user means JupyterHub, which is a separate deployment project
- Your output is production code. Notebooks hide execution order, keep state that no longer matches the visible cells, and produce JSON diffs that code review cannot read; teams that ship from notebooks usually end up bolting on jupytext, nbstripout, and nbval to compensate
- You are exposing it beyond localhost casually. The default is token authentication for good reason: a JupyterLab reachable on 0.0.0.0 without a token is a remote shell for anyone who finds it, since it ships a terminal and arbitrary code execution
- You depend on a JupyterLab 3 extension that was never ported. Version 3 hit end of maintenance in May 2024, and the 3 to 4 jump broke extension APIs; the tracker currently carries roughly 2382 open issues, so niche breakage can sit for a long time
Setup reality
pip install jupyterlab then jupyter lab is genuinely all it takes on a laptop, and since prebuilt extensions became the norm you no longer need Node.js just to add a plugin. The rough edges show up elsewhere. With pip install --user the jupyter script lands in ~/.local/bin and you get command not found until that is on PATH. Configuration is spread across jupyter_lab_config.py, jupyter_server_config.py, and per-extension JSON under share/jupyter/lab/settings, and half the tutorials online still use the pre-2.0 c.NotebookApp keys instead of c.ServerApp. Kernels are separate installs: a fresh virtualenv does not appear in the launcher until you run python -m ipykernel install inside it, and the single most common confusion is a notebook importing packages from a different environment than the one you think you are in. Behind a reverse proxy you must set base_url and let websockets through, or the UI loads and then hangs with no kernel connection.
Patterns
Install into a project environment and start itinstall-and-launch
python -m venv .venv
source .venv/bin/activate
pip install jupyterlab
jupyter lab # opens a browser at 127.0.0.1:8888
jupyter lab --version
jupyter --paths # where config, data and runtime liveInstall it inside the project virtualenv, not globally, or the launcher shows kernels from an environment that does not have your dependencies. With pip install --user, add ~/.local/bin to PATH first.
Run on a server and reach it over an SSH tunnelrun-on-a-remote-machine
# on the server
jupyter lab --no-browser --port=8888 --ip=127.0.0.1
# on your laptop
ssh -N -L 8888:127.0.0.1:8888 user@gpu-box
# then open the http://127.0.0.1:8888/lab?token=... URL the server printedBind to 127.0.0.1 and tunnel rather than using --ip=0.0.0.0. The printed token is the only credential, and it changes on every restart; jupyter server list recovers it if you lose the terminal.
Write and edit the config filegenerate-a-config-file
jupyter lab --generate-config # ~/.jupyter/jupyter_lab_config.py
# jupyter_lab_config.py
c.ServerApp.ip = "0.0.0.0"
c.ServerApp.port = 8888
c.ServerApp.open_browser = False
c.ServerApp.root_dir = "/srv/notebooks"
c.ServerApp.terminals_enabled = False
c.LabApp.default_url = "/lab"The modern prefix is ServerApp for server settings and LabApp for the frontend. Older answers use c.NotebookApp, which now only applies if you are running the classic notebook server.
Replace the rotating token with a passwordset-a-password
jupyter server password # prompts, writes a hash to jupyter_server_config.json
# or disable auth entirely, only ever behind something else that authenticates
jupyter lab --ServerApp.token='' --ServerApp.password=''The empty-token form turns the server into an unauthenticated remote code execution endpoint. Only do it when the listener is on localhost or behind a proxy that already checks identity.
Make another virtualenv show up as a kernelregister-a-kernel
source ~/envs/forecasting/bin/activate
pip install ipykernel
python -m ipykernel install --user \
--name forecasting --display-name "Python (forecasting)"
jupyter kernelspec list
jupyter kernelspec uninstall forecastingRun this inside the environment you want to expose, not inside the one running JupyterLab. If imports fail in a notebook, check sys.executable in a cell before touching anything else.
Add prebuilt extensions and check what is loadedinstall-extensions
pip install jupyterlab-git jupyterlab-lsp python-lsp-server[all]
jupyter labextension list # frontend plugins
jupyter server extension list # server-side halves
# disable one without uninstalling
jupyter labextension disable @jupyterlab/notebook-extension:tocPrebuilt extensions need no Node.js and take effect after a page reload plus a server restart. Many extensions have both a frontend and a server part, so check both lists when one half quietly fails to load.
Mount it under a path on an existing domainserve-behind-a-proxy
jupyter lab --ServerApp.base_url=/lab/ --ServerApp.allow_remote_access=True
# nginx
location /lab/ {
proxy_pass http://127.0.0.1:8888;
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;
}Missing the Upgrade and Connection headers is the classic failure: the interface loads, then every kernel stays on Connecting because the websocket never establishes. base_url must match the proxy path including the trailing slash.
Ship default UI settings to every userpin-default-settings
// $PREFIX/share/jupyter/lab/settings/overrides.json
{
"@jupyterlab/apputils-extension:themes": {
"theme": "JupyterLab Dark"
},
"@jupyterlab/docmanager-extension:plugin": {
"autosave": true,
"autosaveInterval": 60
},
"@jupyterlab/terminal-extension:plugin": {
"fontSize": 14
}
}These are defaults, not locks: anything a user changes in the Settings Editor still wins. Use jupyter labextension disable when you actually need a feature gone.
Containerise it for a reproducible environmentrun-in-docker
FROM python:3.12-slim
RUN pip install --no-cache-dir jupyterlab pandas matplotlib
RUN useradd -m jovyan
USER jovyan
WORKDIR /home/jovyan/work
EXPOSE 8888
CMD ["jupyter", "lab", "--ip=0.0.0.0", "--no-browser", \
"--ServerApp.root_dir=/home/jovyan/work"]Mount your notebooks as a volume or the work disappears with the container. Run as a non-root user: JupyterLab includes a terminal, so root inside the container means root over anything you bind-mount.
Run a notebook without the UIexecute-notebooks-headless
pip install nbconvert papermill nbstripout
# execute in place and export
jupyter nbconvert --to notebook --execute report.ipynb --output run.ipynb
jupyter nbconvert --to html --execute report.ipynb
# with parameters (tag a cell 'parameters' first)
papermill report.ipynb out/2026-08-06.ipynb -p run_date 2026-08-06
# keep outputs out of git
nbstripout --installNone of this needs jupyterlab installed, only nbconvert or papermill plus a kernel. Add --ExecutePreprocessor.timeout=600 for slow cells, since the default kills a cell after 30 seconds.
Scaffold and develop your own extensionbuild-an-extension
pip install "copier>=9,<10" jinja2-time "jupyterlab>=4"
copier copy --trust https://github.com/jupyterlab/extension-template .
pip install -e ".[test]"
jupyter labextension develop . --overwrite
jlpm watch # rebuild on change, reload the browser tabjupyter labextension develop symlinks the built assets so a reload picks up changes; without it you are rebuilding and reinstalling for every edit. jlpm is the pinned yarn that ships with JupyterLab, and mixing in a system npm causes lockfile drift.
Save and restore a tab layoutmanage-workspaces
# open a named workspace in the browser
# http://localhost:8888/lab/workspaces/analysis
jupyter lab workspaces export analysis > analysis.json
jupyter lab workspaces import analysis.json
jupyter lab workspaces list
jupyter lab clean --all # nuke stale build state when the UI misbehavesWorkspace state includes which files were open, so exporting one is a decent way to hand a colleague a reproducible starting layout. A corrupted workspace can make the UI fail to load; opening a different workspace name is the fastest way out.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| notebook | PyPI | You want the simpler one-document-per-tab interface; Notebook 7 is built on the same components with less chrome. |
| marimo | PyPI | You want reactive notebooks stored as plain .py files that diff and import like normal code. |
| jupyterhub | PyPI | Several people need their own server with real authentication and per-user isolation. |
| papermill | PyPI | The notebook is a batch job that needs parameters and a schedule rather than a human at the keyboard. |