JupyterHub DockerSpawner not preserving API token when attempting to spawn container with NB_USER

Hey folks,

I am running into an issue where I am trying to deploy an instance of JupyterHub using DockerSpawner (more specifically, SwarmSpawner) in which I want to launch notebook containers (the Jupyter Docker Stacks in particular) as the user who logs into the Hub, rather than the default jovyan user.

Situation:

Instead of launching each notebook container as jovyan, I wish to spawn each container through JupyterHub as the particular user logging into to the system. Running the container without the hub, this is just:

docker run -it --rm --user root \
    -e NB_USER=user \
    -e CHOWN_HOME=yes \
    -w "/home/${NB_USER}"
    jupyter/base-notebook

Trying to port this logic over to JupyterHub/DockerSpawner, I’ve tried the following:

I created a custom authenticator that implements a pre_spawn_start function that will take the user who has logged in and update the spawner’s container spec to launch as said user:

async def pre_spawn_start(self, user, spawner):
    spawner.extra_container_spec.update(
        {
            "user": "root",
            "workdir": f"/home/{user.name}",
            "env": {
                "NB_USER": user.name,
                "CHOWN_HOME": "yes",
            }
        }
    )

I pass this authenticator in to the jupyterhub_config.py file:

c.JupyterHub.authenticator_class = CustomAuthenticator

What happens next is after launching JupyterHub and logging in, depending on the approach, one of two things will happen:

  1. The default command for the notebook image is start-notebook.sh, in which it will look for the JUPYTERHUB_API_TOKEN environment variable; if it exists, it will attempt to execute start-singleuser.sh to launch the notebook server. Otherwise it will launch a jupyter (lab) instance. When the container is started as jovyan (the default in the stack), this API token is passed without issue and start-singleuser.sh executes and communicates with the Hub with no issue. Changing the user to NB_USER and launching the notebook container, the API token does not seem to be passed as an environment variable to the container, which causes start-notebook.sh to launch a local instance of JupyterLab - this does not communicate back to the Hub and thus when the container spawns, I cannot continue further, getting a 404 error.

  2. If I change the default cmd to something like jupyterhub-singleuser instead, the notebook container will fail to spawn after 30 seconds. Inspecting the logs, it compains with the following error:

JUPYTERHUB_API_TOKEN env is required to run jupyterhub-singleuser. Did you launch the service manually?

The start.sh script in the docker stacks, upon creating the new user based on NB_USER, launches with the --preserve-env option, so I’m a bit stuck here as to how I could proceed. Has anybody tried this particular endeavor?

I suspect this might be overriding the environment:

Have you tried setting the environment using the spawner configuration instead?
https://jupyterhub.readthedocs.io/en/stable/reference/authenticators.html#using-auth-state

I had initially tried setting it through c.DockerSpawner.environment, but it seems that takes effect too late to make a difference. I can confirm that what you proposed does the trick! I was under the assumption that the container_spec var was only for the docker run (or equivalent) command environment variables, but I guess that is the global set instead. Appreciate the help!

1 Like