Troubleshooting
When Positron does not start for a student, the cause is almost always in one of four places: the launcher extension in the user environment, the license file, the install itself, which covers architecture and version, or, on Positron Server 2026.07.0 through 2026.08.2, the Hub verifier service. This page is organized by symptom.
On Kubernetes, license-mount problems are the usual cause. See Zero to JupyterHub (Kubernetes) specifics.
Where to look first
Most answers are in the logs.
Terminal
# Full Hub log, which covers spawn errors and service registration
journalctl -u jupyterhub --no-pager -n 200Terminal
# Hub log
docker compose logs hub
# A student's single-user container, named by `docker ps`
docker logs <jupyter-USERNAME-container>The single-user server log shows what jupyter-positron-server and positron-server reported at launch. Reach it from the JupyterHub UI under the student server, or from the spawner logs.
The Positron tile does not appear
jupyter-positron-server provides the tile through jupyter-server-proxy, in the user environment.
Check the user environment:
Terminal
/opt/tljh/user/bin/pip show jupyter-positron-server
/opt/tljh/user/bin/jupyter server extension listFixes:
Extension missing: install it in the user environment and enable the proxy.
Terminal
/opt/tljh/user/bin/pip install 'jupyter-positron-server>=0.0.5' /opt/tljh/user/bin/jupyter server extension enable --sys-prefix jupyter_server_proxyInstalled but no tile: confirm
JSP_POSITRON_LAUNCHER_DISABLEDis not set in the spawner environment. Any value hides the tile.Restart the student server, by stopping and starting it from the JupyterHub UI. The extension is picked up on server start, not on Hub reload.
The tile is there, but Positron never loads
The launcher runs, and positron-server fails to start.
Check the browser tab first. When positron-server exits during startup, the tab shows a “Positron Server failed to start” page with the end of the log positron-server writes on its own. That log names the failing step, so read it before going to the single-user server log. The launch is retried about every 30 seconds, so once the cause is fixed, refreshing the tab is enough. No server restart is needed. A refresh that briefly shows a proxy error landed mid-retry. Refresh again.
If the tab instead sits on a spinner for two minutes and then shows could not start positron in time, positron-server never exited. It hung. Go to the single-user server log for that one.
Check the single-user server log for the failing step, then:
positron-server: command not found, or binary not found:positron-serveris not on the sessionPATH. Confirm the spawner sets it:# TLJH: jupyterhub_config.py c.Spawner.environment = { "PATH": "/opt/positron-server/bin:" + os.environ.get("PATH", "/usr/local/bin:/usr/bin:/bin"), }In the docker template the binary is on
PATHthrough the single-user imageENV, and the spawner does not overridePATH.A license error: see the section below that matches your Positron Server version.
License errors on 2026.09.0 and newer
These versions read license.lic from the activation directory and validate it with the bundled license-manager. Check the license first:
Terminal
/opt/positron-server/resources/activation/linux/<arch>/license-manager statusCommon failures:
Permission denied on the license file: each session reads the file as the student, so a root-only mode such as
600fails every session. Confirm the mode is644.Terminal
ls -l /opt/positron-server/resources/activation/linux/<arch>/license.lic chmod 644 /opt/positron-server/resources/activation/linux/<arch>/license.licExpired or altered license:
license-manager statusreports the reason. A license file that exists but does not verify stops the session rather than falling back to anything else. Request a replacement from academic-licenses@posit.co.Wrong architecture directory:
positron-serverlooks underresources/activation/linux/x86_64on x64 hosts andresources/activation/linux/aarch64on arm64 hosts. A license placed in the other directory is not found.
License errors on 2026.07.0 through 2026.08.2
These versions accept a signed token only, which the Hub verifier mints. The verifier runs as a managed JupyterHub service, so its output lands in the Hub log:
Terminal
journalctl -u jupyterhub --no-pager | grep -i positron-licenseThis section covers a hand-rolled Verifier setup deployment. Neither scripts/install-positron.sh nor the scripts/docker/ template in this repository sets up the verifier anymore. Both target Positron Server 2026.09.0 and newer, and 2026.06.1 and older, which validate license.lic directly. See License errors on 2026.09.0 and newer instead if you deployed with one of those templates.
The verifier is not running or not registered
No mention at all: the service config was not loaded. Confirm that
jupyterhub_config.py, or the generatedpositron-license.pyinjupyterhub_config.d/, contains thec.JupyterHub.servicesblock, then reload.Terminal
tljh-config reload # or: systemctl restart jupyterhubThe service starts and then dies: confirm the command exists in the Hub environment.
Terminal
/opt/tljh/hub/bin/pip show jupyter-positron-verifier ls -l /opt/tljh/hub/bin/positron-verifierPort conflict on
VERIFIER_PORT, which defaults to10101: another process holds the port. ChangeVERIFIER_PORTand updatePOSITRON_LICENSE_MINTING_ENDPOINTto match.
Mint calls fail
Signing key not readable: the verifier runs as the Hub service account and reads
POSITRON_MINTING_KEY_FILE. Confirm the file exists and that account can read it.Terminal
ls -l /etc/positron/signing-key.pem # mode 600, owned by the Hub userMinting endpoint unreachable or wrong URL:
POSITRON_LICENSE_MINTING_ENDPOINTmust point at the Hub-proxied service path, e.g.,http://127.0.0.1:10101/services/positron-license/mint.403or other authorization errors from the mint call: the service role is missing scopes. Confirm thec.JupyterHub.load_rolesblock grants thepositron-licenseserviceread:users.
Upgrading to Positron Server 2026.09.0 or newer removes this whole class of failure. See Move an existing deployment off the verifier.
Install-time failures
- Download fails with
curl403 or 404: thePOSITRON_VERSIONandPOSITRON_ARCHcombination does not exist on the Content Delivery Network (CDN). Verify the version string against the Positron releases page, or download the tarball yourself and passPOSITRON_TARBALL=./positron-server.tar.gz.
docker-compose specifics
The hub container exits at boot reporting a file not found or empty: the license secret was not placed. Copy it into
scripts/docker/secrets/. A missing bind-mount source makes Docker create an empty directory, which is why the hub checks at boot.Terminal
cp /path/to/license.lic secrets/license.lic chmod 644 secrets/license.lic docker compose up -dSingle-user containers fail to spawn with a license error: confirm
POSITRON_LICENSE_HOST_PATH(set from${PWD}indocker-compose.yml) resolved to the right absolute path.docker compose configprints the resolved value. It must be a path on the Docker host, which is whydocker composeneeds to be run fromscripts/docker/itself.
Zero to JupyterHub (Kubernetes) specifics
Logs live in the single-user pod, and positron-server restart-loops on failure, so always use --tail:
Terminal
POD=$(kubectl -n jupyterhub get pod -l component=singleuser-server -o name | head -1)
kubectl -n jupyterhub logs $POD -c notebook --tail=200-c notebook avoids the “Defaulted container” notice from the pod’s init container.
could not start positron in time
The browser shows a blank page with a spinner for about two minutes, then a 500 with could not start positron in time. The message implicates slowness, but in a Kubernetes deployment it is far more often the license mount. The two-minute delay is jupyter-server-proxy giving up.
Check the activation directory first:
Terminal
kubectl -n jupyterhub exec $POD -c notebook -- \
ls -l /opt/positron-server/resources/activation/linux/<arch>/If only license.lic is there, plus entries like ..data and a timestamped directory, the Secret was mounted at the directory instead of at the file path. Add subPath: license.lic and make mountPath the full file path, or switch to singleuser.extraFiles, which derives subPath for you.
If the directory looks healthy but Positron still fails, the license is probably in the wrong architecture directory. Both x86_64 and aarch64 ship in every tarball and both look complete, so this is invisible to inspection:
Terminal
kubectl -n jupyterhub exec $POD -c notebook -- \
python3 -c "import platform; print(platform.machine())"Whatever that prints is the only directory Positron consults.
No license key provided in the pod log
No license key provided. A license key is required to use Positron in a hosted
environment. Provide a license key with the --license-key or --license-key-file ...
Taken literally this suggests the license is missing, and following its advice leads nowhere. It appears whenever Positron cannot validate a license, which includes the case where the license is mounted and readable but the activation libraries beside it were masked. Work through the activation-directory checks above before touching --license-key-file.
Confirming the license is readable at all
Terminal
kubectl -n jupyterhub exec $POD -c notebook -- id
kubectl -n jupyterhub exec $POD -c notebook -- \
head -c 30 /opt/positron-server/resources/activation/linux/<arch>/license.licid reports uid=1000(jovyan) gid=100(users), and the license appears as root:users with mode 0440. Note that license-manager status requires root: usable via sudo on The Littlest JupyterHub (TLJH), but not in user pods, which have no privilege escalation.
User servers fail to spawn
The single-user image’s jupyterhub must be compatible with the Hub’s. Compare:
Terminal
kubectl -n jupyterhub exec deploy/hub -- jupyterhub --version
docker run --rm --entrypoint pip <your-image> show jupyterhubA patch-level difference is fine. A larger gap needs a pin in the image.
Extensions reinstall on every launch
Expected when singleuser.storage.type is none, because $HOME is ephemeral and Positron re-bootstraps ~/.positron-server/extensions each spawn. Set storage.type: dynamic so it happens once.
Still stuck?
Open an issue at the jupyter-positron-server issue tracker with the relevant Hub and single-user log excerpts, your Positron Server version, and which setup path you used.