Is there a way to update allowed_images in jupyterhub_config.py without restarting Docker container running JupyterHub?
I had two ideas:
I can make this work maybe with setting remove=False and not removing user servers, but this answer says that it is not a good practice.
to add allowed_images=”*”, but the problem is that service tries to pull jupyterhub/singleuser image into my air-gapped machine without internet access
Here is function that works for my case if anyone needs it:
def dynamic_allowed_images(spawner):
"""Return a list of available Docker image tags.
Optionally filter by prefix via env var `ALLOWED_IMAGE_PREFIX`.
Raises RuntimeError if no images match or discovery fails.
"""
try:
client = docker.from_env()
prefix = os.environ.get("ALLOWED_IMAGE_PREFIX", "")
tags = []
for img in client.images.list():
for tag in img.tags or []:
if not prefix or tag.startswith(prefix):
tags.append(tag)
unique_sorted = sorted(set(tags))
if not unique_sorted:
raise RuntimeError(
"No Docker images found that match the configured prefix."
)
return unique_sorted
except Exception as e: # noqa: BLE001
print(f"[JupyterHub] Error discovering Docker images: {e}", file=sys.stderr)
raise
I can’t share full configuration at the moment but I have c.DockerSpawner.pull_policy = "Skip"set. Not sure if it is causing the issue?
Another thing I observed it that list gets refreshed only when I start and stop some random image/user server. It doesn’t refresh when I click to create new user server and when the page to choose an image pops up.
I can reproduce the behavior with the following minimal JupyterHub configuration using the latest versions.
c.JupyterHub.spawner_class = 'dockerspawner.DockerSpawner'
c.JupyterHub.log_level = "DEBUG"
c.JupyterHub.authenticator_class = "shared-password"
c.SharedPasswordAuthenticator.user_password = "test1234"
c.Authenticator.allow_all = True
import sys
import docker
import os
def dynamic_allowed_images(spawner):
"""Return a list of available Docker image tags.
Optionally filter by prefix via env var `ALLOWED_IMAGE_PREFIX`.
Raises RuntimeError if no images match or discovery fails.
"""
print("\n=== Called allowed_images ===\n")
try:
client = docker.from_env()
prefix = os.environ.get("ALLOWED_IMAGE_PREFIX", "")
tags = []
for img in client.images.list():
for tag in img.tags or []:
if not prefix or tag.startswith(prefix):
tags.append(tag)
unique_sorted = sorted(set(tags))
if not unique_sorted:
raise RuntimeError(
"No Docker images found that match the configured prefix."
)
return unique_sorted
except Exception as e: # noqa: BLE001
print(f"[JupyterHub] Error discovering Docker images: {e}", file=sys.stderr)
raise
c.DockerSpawner.allowed_images = dynamic_allowed_images
Reloading the spawn page does not call the method dynamic_allowed_images again. Thus, the options do not change.