Jupyter Notebook runs in root dir, not where it is located

Hi all,

I am running a JupyterHub platform based loosely off of the Zero-to-Kubernetes setup. In my JupyterLab Singleuser server, I am having an issue where the directory of where the notebook runs is not the directory where it is located. All of my notebooks, regardless of where they are located, run from the root directory (/) by default. I can prove this by running !pwd in a cell in any notebook and see that the working directory is / even though the notebook may be in /notebooks/testing, for example.

We are using Hybrid Contents Manager to have S3 mounts and EBS volumes present in the UI. Files from both locations (S3 and EBS) have this same issue. For all other intents and purposes, all files from both locations appear to be working as expected.

I have perused the forum and haven’t found a question like this, but apologies if there is one I missed. Any help would be appreciated, thank y’all!

What’s the default working directory in your image, or in your configuration? Can you share your Dockerfile, or perhaps a minimal reproducible example if your Dockerfile is private?

1 Like

Hey @manics , the below is generally what our Dockerfile looks like with some internal elements stripped from it. Most of it is setting up internal company certs followed by the out-of-the-box debian + python packages that we care about. Perhaps the fact that we set WORKDIR / is what you are referring to?

# syntax = <internal Docker image storage location>/docker/dockerfile:1
FROM scratch AS certificates

FROM <internal Docker image storage location>/python:3.11.4-slim-bookworm

COPY --from=certificates . /usr/local/share/ca-certificates
RUN cd /usr/local/share/ca-certificates && \
    for f in *.cer; do mv -- "$f" "${f%.cer}.crt"; done && \
    update-ca-certificates

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && \
    apt-get upgrade --yes && \
    apt-get install --yes --no-install-recommends \
        dnsutils \
        iputils-ping \
        tini \
        openssh-client \
        build-essential \
        libc6-dev \
        git \
        curl unzip awscli vim wget bash nano less micro strace \
        gcc-multilib \
        libxft2 \
        libncurses5 \
        net-tools \
        telnet \
        tmux \
        htop && \
    mkdir -p /usr/lib/x86_64-pc-linux-gnu && \
    ln -s /usr/lib/x86_64-linux-gnu /usr/lib/x86_64-pc-linux-gnu/10.3.0 && \
    rm -rf /var/lib/apt/lists/*

#region Setup system Python environment
WORKDIR /uv

ADD wheels.tar ./wheels/
COPY uv venv/pyproject.toml venv/uv.lock ./
COPY <lots more internal files>

RUN UV_PROJECT_ENVIRONMENT="/usr/local/" \
    ./uv sync \
    --no-index \
    --find-links=./wheels \
    --offline
#endregion

# Disable official news announcements
RUN jupyter labextension disable --level=system "@jupyterlab/apputils-extension:announcements"

WORKDIR /

ENTRYPOINT ["/bin/bash"]

In our helm chart, we specify that this entrypoint script is run on startup (via singleuser.cmd. This creates a user with non-jovyan name and kicks off a script in parallel with the singleuser server main script to run a bunch of custom setup on their server:

#!/bin/bash
set -e

if [ "$(id -u)" == 0 ]; then
    if [ -z "$JUPYTERHUB_USER" ]; then
        echo "Environment variable JUPYTERHUB_USER is unset"
        exit 1
    fi

    echo "Creating user: $JUPYTERHUB_USER"

    useradd \
        -m \
        -u 1000 \
        -U \
        -s /bin/bash \
        "$JUPYTERHUB_USER"

    cp -r /home/jovyan/. "/home/$JUPYTERHUB_USER"
    chown -R "$JUPYTERHUB_USER:$JUPYTERHUB_USER" "/home/$JUPYTERHUB_USER"

    mkdir -p /scratch/home /scratch/workspaces

    chown -R "$JUPYTERHUB_USER:$JUPYTERHUB_USER" /scratch
    if [ -d /scratch/home/.ssh ]; then
        chmod 700 /scratch/home/.ssh
    fi

    if [ -f /scratch/home/.ssh/id_rsa ]; then
        chmod 600 /scratch/home/.ssh/id_rsa
    fi

    if [ -f /scratch/home/.ssh/id_rsa.pub ]; then
        chmod 644 /scratch/home/.ssh/id_rsa.pub
    fi

    export NB_USER=$JUPYTERHUB_USER
    export NB_UID="1000"
    export HOME="/home/$JUPYTERHUB_USER"

    WHITELIST_ENV=<Lots of env vars>
    
    # Start the setup agent in the background as $JUPYTERHUB_USER
    su \
        --command="/opt/pixelhub/setup/jupyter-proxy-agent --clone-the-source=true &" \
        --whitelist-environment="$WHITELIST_ENV" \
        --login "$JUPYTERHUB_USER"
    echo "Setup running in the background"

    echo "starting Jupyter Lab"
    # Start the main process as $JUPYTERHUB_USER
    su \
        --command="/usr/local/bin/jupyterhub-singleuser $*" \
        --whitelist-environment="$WHITELIST_ENV" \
        --login "$JUPYTERHUB_USER"

else
    echo "Container must be run as root"
    exit 1
fi

We also have lots of python scripts customizing the jupyter config itself, but none of those strike me as suspicious enough to be causing this issue. The only bit that I’ve thought could be relevant is us setting ServerApp.root_dir="/", but from what I understand this just affects what users can see in the UI which, if so, is our intended behavior.

Let me know if any other information would be helpful to you. Greatly appreciate you taking the time

Hey @manics , any thoughts on this one? Let me know if more detail is required of me. Appreciate any advice!