KubeSpawner

Contents

KubeSpawner#

JupyterHub Spawner to spawn user notebooks on a Kubernetes cluster.

After installation, you can enable it by adding:

c.JupyterHub.spawner_class = 'kubespawner.KubeSpawner'

in your jupyterhub_config.py file.

class kubespawner.KubeSpawner(**kwargs: Any)#

A JupyterHub spawner that spawn pods in a Kubernetes Cluster. Each server spawned by a user will have its own KubeSpawner instance.

additional_profile_form_template_paths c.KubeSpawner.additional_profile_form_template_paths = List()#

Additional paths to search for jinja2 templates when rendering profile_form.

These directories will be searched before the default templates/ directory shipped with kubespawner with the default template.

Any file named form.html in these directories will be used to render the profile options form.

after_pod_created_hook c.KubeSpawner.after_pod_created_hook = Callable(None)#

Callable to augment the Pod object after launching.

Expects a callable that takes two parameters:

  1. The spawner object that is doing the spawning

  2. The Pod object that was launched

This can be a coroutine if necessary. When set to none, no augmenting is done.

This is very useful if you want to add some services or ingress to the pod after it is launched. Note that the spawner object can change between versions of KubeSpawner and JupyterHub, so be careful relying on this!

allow_privilege_escalation c.KubeSpawner.allow_privilege_escalation = Bool(False)#

Controls whether a process can gain more privileges than its parent process.

When set to False (the default), the primary user visible effect is that setuid binaries (like sudo) will no longer work.

When set to None, the defaults for the cluster are respected.

This bool directly controls whether the no_new_privs flag gets set on the container

AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged OR 2) has CAP_SYS_ADMIN.

args c.KubeSpawner.args = List()#

Extra arguments to be passed to the single-user server.

Some spawners allow shell-style expansion here, allowing you to use environment variables here. Most, including the default, do not. Consult the documentation for your spawner to verify!

auth_state_hook c.KubeSpawner.auth_state_hook = Any(None)#

An optional hook function that you can implement to pass auth_state to the spawner after it has been initialized but before it starts. The auth_state dictionary may be set by the .authenticate() method of the authenticator. This hook enables you to pass some or all of that information to your spawner.

Example:

def userdata_hook(spawner, auth_state):
    spawner.userdata = auth_state["userdata"]

c.Spawner.auth_state_hook = userdata_hook
automount_service_account_token c.KubeSpawner.automount_service_account_token = Bool(None)#

Whether to mount the service account token in the spawned user pod.

The default value is None, which mounts the token if the service account is explicitly set, but doesn’t mount it if not.

WARNING: Be careful with this configuration! Make sure the service account being mounted has the minimal permissions needed, and nothing more. When misconfigured, this can easily give arbitrary users root over your entire cluster.

cmd c.KubeSpawner.cmd = Command(None)#

The command used to start the single-user server.

Either
  • a string containing a single command or path to a startup script

  • a list of the command and arguments

  • None (default) to use the Docker image’s CMD

If cmd is set, it will be augmented with spawner.get_args(). This will override the `CMD specified in the Docker image.

common_labels c.KubeSpawner.common_labels = Dict()#

Kubernetes labels that both spawned singleuser server pods and created user PVCs will get.

Note that these are only set when the Pods and PVCs are created, not later when this setting is updated.

component_label c.KubeSpawner.component_label = Unicode('singleuser-server')#

The component label used to tag the user pods. This can be used to override the spawner behavior when dealing with multiple hub instances in the same namespace. Usually helpful for CI workflows.

consecutive_failure_limit c.KubeSpawner.consecutive_failure_limit = Int(0)#

Maximum number of consecutive failures to allow before shutting down JupyterHub.

This helps JupyterHub recover from a certain class of problem preventing launch in contexts where the Hub is automatically restarted (e.g. systemd, docker, kubernetes).

A limit of 0 means no limit and consecutive failures will not be tracked.

container_security_context c.KubeSpawner.container_security_context = Union()#

A Kubernetes security context for the container. Note that all configuration options within here should be camelCased.

What is configured here has the highest priority, so the alternative configuration uid, gid, privileged, and allow_privilege_escalation will be overridden by this.

Rely on the Kubernetes reference for details on allowed configuration.

cpu_guarantee c.KubeSpawner.cpu_guarantee = Float(None)#

Minimum number of cpu-cores a single-user notebook server is guaranteed to have available.

If this value is set to 0.5, allows use of 50% of one CPU. If this value is set to 2, allows use of up to 2 CPUs.

This is a configuration setting. Your spawner must implement support for the limit to work. The default spawner, LocalProcessSpawner, does not implement this support. A custom spawner must add support for this setting for it to be enforced.

cpu_limit c.KubeSpawner.cpu_limit = Float(None)#

Maximum number of cpu-cores a single-user notebook server is allowed to use.

If this value is set to 0.5, allows use of 50% of one CPU. If this value is set to 2, allows use of up to 2 CPUs.

The single-user notebook server will never be scheduled by the kernel to use more cpu-cores than this. There is no guarantee that it can access this many cpu-cores.

This is a configuration setting. Your spawner must implement support for the limit to work. The default spawner, LocalProcessSpawner, does not implement this support. A custom spawner must add support for this setting for it to be enforced.

debug c.KubeSpawner.debug = Bool(False)#

Enable debug-logging of the single-user server

default_url c.KubeSpawner.default_url = Unicode('')#

The URL the single-user server should start in.

{username} will be expanded to the user’s username

Example uses:

  • You can set notebook_dir to / and default_url to /tree/home/{username} to allow people to navigate the whole filesystem from their notebook server, but still start in their home directory.

  • Start with /notebooks instead of /tree if default_url points to a notebook instead of a directory.

  • You can set this to /lab to have JupyterLab start by default, rather than Jupyter Notebook.

delete_grace_period c.KubeSpawner.delete_grace_period = Int(1)#

Time in seconds for the pod to be in terminating state before is forcefully killed.

Increase this if you need more time to execute a preStop lifecycle hook.

See https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods for more information on how pod termination works.

Defaults to 1.

delete_pvc c.KubeSpawner.delete_pvc = Bool(True)#

Delete PVCs when deleting Spawners.

When a Spawner is deleted (not just stopped), delete its associated PVC.

This occurs when a named server is deleted, or when the user itself is deleted for the default Spawner.

Requires JupyterHub 1.4.1 for Spawner.delete_forever support.

delete_stopped_pods c.KubeSpawner.delete_stopped_pods = Bool(True)#

Whether to delete pods that have stopped themselves. Set to False to leave stopped pods in the completed state, allowing for easier debugging of why they may have stopped.

disable_user_config c.KubeSpawner.disable_user_config = Bool(False)#

Disable per-user configuration of single-user servers.

When starting the user’s single-user server, any config file found in the user’s $HOME directory will be ignored.

Note: a user could circumvent this if the user modifies their Python environment, such as when they have their own conda environments / virtualenvs / containers.

dns_name_template c.KubeSpawner.dns_name_template = Unicode('{name}.{namespace}.svc.cluster.local')#

Template to use to form the dns name for the pod.

enable_user_namespaces c.KubeSpawner.enable_user_namespaces = Bool(False)#

Cause each user to be spawned into an individual namespace.

This comes with some caveats. The Hub must run with significantly more privilege (must have ClusterRoles analogous to its usual Roles) and can therefore do heinous things to the entire cluster.

It will also make the Reflectors aware of pods and events across all namespaces. This will have performance implications, although using labels to restrict resource selection helps somewhat.

If you use this, consider cleaning up the user namespace in your post_stop_hook.

env_keep c.KubeSpawner.env_keep = List()#

List of environment variables for the single-user server to inherit from the JupyterHub process.

This list is used to ensure that sensitive information in the JupyterHub process’s environment (such as CONFIGPROXY_AUTH_TOKEN) is not passed to the single-user server’s process.

environment c.KubeSpawner.environment = Dict()#

Extra environment variables to set for the single-user server’s process.

Environment variables that end up in the single-user server’s process come from 3 sources:
  • This environment configurable

  • The JupyterHub process’ environment variables that are listed in env_keep

  • Variables to establish contact between the single-user notebook and the hub (such as JUPYTERHUB_API_TOKEN)

The environment configurable should be set by JupyterHub administrators to add installation specific environment variables. It is a dict where the key is the name of the environment variable, and the value can be a string or a callable. If it is a callable, it will be called with one parameter (the spawner instance), and should return a string fairly quickly (no blocking operations please!).

Note that the spawner class’ interface is not guaranteed to be exactly same across upgrades, so if you are using the callable take care to verify it continues to work after upgrades!

Changed in version 1.2: environment from this configuration has highest priority, allowing override of ‘default’ env variables, such as JUPYTERHUB_API_URL.

events_enabled c.KubeSpawner.events_enabled = Bool(True)#

Enable event-watching for progress-reports to the user spawn page.

Disable if these events are not desirable or to save some performance cost.

extra_annotations c.KubeSpawner.extra_annotations = Dict()#

Extra Kubernetes annotations to set on the spawned single-user pods, as well as on the pods’ associated k8s Service and k8s Secret if internal_ssl is enabled.

The keys and values specified here are added as annotations on the spawned single-user kubernetes pods. The keys and values must both be strings.

See the Kubernetes documentation for more info on what annotations are and why you might want to use them!

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration.

extra_container_config c.KubeSpawner.extra_container_config = Dict()#

Extra configuration (e.g. envFrom) for notebook container which is not covered by other attributes.

This dict will be directly merge into container of notebook server, so you should use the same structure. Each item in the dict must a field of the V1Container specification.

One usage is set envFrom on notebook container with configuration below:

c.KubeSpawner.extra_container_config = {
    "envFrom": [{
        "configMapRef": {
            "name": "special-config"
        }
    }]
}

The key could be either a camelCase word (used by Kubernetes yaml, e.g. envFrom) or a snake_case word (used by Kubernetes Python client, e.g. env_from).

extra_containers c.KubeSpawner.extra_containers = List()#

List of containers belonging to the pod which besides to the container generated for notebook server.

This list will be directly appended under containers in the kubernetes pod spec, so you should use the same structure. Each item in the list is container configuration which follows spec at https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#container-v1-core

One usage is setting crontab in a container to clean sensitive data with configuration below:

c.KubeSpawner.extra_containers = [{
    "name": "crontab",
    "image": "supercronic",
    "command": ["/usr/local/bin/supercronic", "/etc/crontab"]
}]

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration. The username and servername come escaped to follow the DNS label standard.

extra_labels c.KubeSpawner.extra_labels = Dict()#

Extra kubernetes labels to set on the spawned single-user pods, as well as on the pods’ associated k8s Service and k8s Secret if internal_ssl is enabled.

The keys and values specified here would be set as labels on the spawned single-user kubernetes pods. The keys and values must both be strings that match the kubernetes label key / value constraints.

See the Kubernetes documentation for more info on what labels are and why you might want to use them!

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration. The username and servername come escaped to follow the DNS label standard.

extra_pod_config c.KubeSpawner.extra_pod_config = Dict()#

Extra configuration for the pod which is not covered by other attributes.

This dict will be directly merge into pod,so you should use the same structure. Each item in the dict is field of pod configuration which follows spec at https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#podspec-v1-core

One usage is set restartPolicy and dnsPolicy with configuration below:

c.KubeSpawner.extra_pod_config = {
    "restartPolicy": "OnFailure",
    "dns_policy": "ClusterFirstWithHostNet"
}

The key could be either a camelCase word (used by Kubernetes yaml, e.g. restartPolicy) or a snake_case word (used by Kubernetes Python client, e.g. dns_policy).

extra_resource_guarantees c.KubeSpawner.extra_resource_guarantees = Dict()#

The dictionary used to request arbitrary resources. Default is None and means no additional resources are requested. For example, to request 1 Nvidia GPUs:

c.KubeSpawner.extra_resource_guarantees = {"nvidia.com/gpu": "1"}
extra_resource_limits c.KubeSpawner.extra_resource_limits = Dict()#

The dictionary used to limit arbitrary resources. Default is None and means no additional resources are limited. For example, to add a limit of 3 Nvidia GPUs:

c.KubeSpawner.extra_resource_limits = {"nvidia.com/gpu": "3"}
fs_gid c.KubeSpawner.fs_gid = Union(None)#

The GID of the group that should own any volumes that are created & mounted.

A special supplemental group that applies primarily to the volumes mounted in the single-user server. In volumes from supported providers, the following things happen:

  1. The owning GID will be the this GID

  2. The setgid bit is set (new files created in the volume will be owned by this GID)

  3. The permission bits are OR’d with rw-rw

The single-user server will also be run with this gid as part of its supplemental groups.

Instead of an integer, this could also be a callable that takes as one parameter the current spawner instance and returns an integer. The callable will be called asynchronously if it returns a future, rather than an int. Note that the interface of the spawner class is not deemed stable across versions, so using this functionality might cause your JupyterHub or kubespawner upgrades to break.

You’ll have to set this if you are using auto-provisioned volumes with most cloud providers. See fsGroup for more details.

get_pod_url c.KubeSpawner.get_pod_url = Callable(None)#

Callable to retrieve pod url

Called with (spawner, pod)

Must not be async

gid c.KubeSpawner.gid = Union(None)#

The GID to run the single-user server containers as.

This GID should ideally map to a group that already exists in the container image being used. Running as root is discouraged.

Instead of an integer, this could also be a callable that takes as one parameter the current spawner instance and returns an integer. The callable will be called asynchronously if it returns a future. Note that the interface of the spawner class is not deemed stable across versions, so using this functionality might cause your JupyterHub or kubespawner upgrades to break.

If set to None, the group of the user specified with the USER directive in the container metadata is used.

http_timeout c.KubeSpawner.http_timeout = Int(30)#

Timeout (in seconds) before giving up on a spawned HTTP server

Once a server has successfully been spawned, this is the amount of time we wait before assuming that the server is unable to accept connections.

hub_connect_ip c.KubeSpawner.hub_connect_ip = Unicode('')#

DEPRECATED. Use c.JupyterHub.hub_connect_ip

hub_connect_port c.KubeSpawner.hub_connect_port = Int(0)#

DEPRECATED. Use c.JupyterHub.hub_connect_url

hub_connect_url c.KubeSpawner.hub_connect_url = Unicode(None)#

The URL the single-user server should connect to the Hub.

If the Hub URL set in your JupyterHub config is not reachable from spawned notebooks, you can set differnt URL by this config.

Is None if you don’t need to change the URL.

image c.KubeSpawner.image = Unicode('quay.io/jupyterhub/singleuser:latest')#

Docker image to use for spawning user’s containers.

Defaults to quay.io/jupyterhub/singleuser:latest

Name of the container + a tag, same as would be used with a docker pull command. If tag is set to latest, kubernetes will check the registry each time a new user is spawned to see if there is a newer image available. If available, new image will be pulled. Note that this could cause long delays when spawning, especially if the image is large. If you do not specify a tag, whatever version of the image is first pulled on the node will be used, thus possibly leading to inconsistent images on different nodes. For all these reasons, it is recommended to specify a specific immutable tag for the image.

If your image is very large, you might need to increase the timeout for starting the single user container from the default. You can set this with:

c.KubeSpawner.start_timeout = 60 * 5  # Up to 5 minutes
image_pull_policy c.KubeSpawner.image_pull_policy = Unicode(None)#

The image pull policy of the docker container specified in image.

Defaults to None, which means it is omitted. This leads to it behaving like ‘Always’ when a tag is absent or ‘latest’, and ‘IfNotPresent’ when the tag is specified to be something else, per https://kubernetes.io/docs/concepts/containers/images/#imagepullpolicy-defaulting.

image_pull_secrets c.KubeSpawner.image_pull_secrets = Union()#

A list of references to Kubernetes Secret resources with credentials to pull images from image registries. This list can either have strings in it or objects with the string value nested under a name field.

Passing a single string is still supported, but deprecated as of KubeSpawner 0.14.0.

See the Kubernetes documentation for more information on when and why this might need to be set, and what it should be set to.

init_containers c.KubeSpawner.init_containers = List()#

List of initialization containers belonging to the pod.

This list will be directly added under initContainers in the kubernetes pod spec, so you should use the same structure. Each item in the dict must a field of the V1Container specification

One usage is disabling access to metadata service from single-user notebook server with configuration below:

c.KubeSpawner.init_containers = [{
    "name": "init-iptables",
    "image": "<image with iptables installed>",
    "command": ["iptables", "-A", "OUTPUT", "-p", "tcp", "--dport", "80", "-d", "169.254.169.254", "-j", "DROP"],
    "securityContext": {
        "capabilities": {
            "add": ["NET_ADMIN"]
        }
    }
}]

See the Kubernetes documentation for more info on what init containers are and why you might want to use them!

To user this feature, Kubernetes version must greater than 1.6.

ip c.KubeSpawner.ip = Unicode('0.0.0.0')#

The IP address (or hostname) the single-user server should listen on. We override this from the parent so we can set a more sane default for the Kubernetes setup.

k8s_api_host c.KubeSpawner.k8s_api_host = Unicode('')#

Full host name of the k8s API server (”https://hostname:port”).

Typically this is unnecessary, the hostname is picked up by config.load_incluster_config() or config.load_kube_config.

k8s_api_request_retry_timeout c.KubeSpawner.k8s_api_request_retry_timeout = Int(30)#

Total timeout, including retry timeout, for kubernetes API calls

When a k8s API request connection times out, we retry it while backing off exponentially. This lets you configure the total amount of time we will spend trying an API request - including retries - before giving up.

k8s_api_request_timeout c.KubeSpawner.k8s_api_request_timeout = Int(3)#

API request timeout (in seconds) for all k8s API calls.

This is the total amount of time a request might take before the connection is killed. This includes connection time and reading the response.

NOTE: This is currently only implemented for creation and deletion of pods, and creation of PVCs.

k8s_api_ssl_ca_cert c.KubeSpawner.k8s_api_ssl_ca_cert = Unicode('')#

Location (absolute filepath) for CA certs of the k8s API server.

Typically this is unnecessary, CA certs are picked up by config.load_incluster_config() or config.load_kube_config.

In rare non-standard cases, such as using custom intermediate CA for your cluster, you may need to mount root CA’s elsewhere in your Pod/Container and point this variable to that filepath

k8s_api_threadpool_workers c.KubeSpawner.k8s_api_threadpool_workers = Int(0)#

DEPRECATED in KubeSpawner 3.0.0.

No longer has any effect, as there is no threadpool anymore.

k8s_api_verify_ssl c.KubeSpawner.k8s_api_verify_ssl = Bool(None)#

Verify TLS certificates when connecting to the k8s master.

Set this to false to skip verifying SSL certificate when calling API from https server.

lifecycle_hooks c.KubeSpawner.lifecycle_hooks = Dict()#

Kubernetes lifecycle hooks to set on the spawned single-user pods.

The keys is name of hooks and there are only two hooks, postStart and preStop. The values are handler of hook which executes by Kubernetes management system when hook is called.

Below is an sample copied from the Kubernetes documentation:

c.KubeSpawner.lifecycle_hooks = {
    "postStart": {
        "exec": {
            "command": ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
        }
    },
    "preStop": {
        "exec": {
            "command": ["/usr/sbin/nginx", "-s", "quit"]
        }
    }
}

See the Kubernetes documentation for more info on what lifecycle hooks are and why you might want to use them!

mem_guarantee c.KubeSpawner.mem_guarantee = ByteSpecification(None)#

Minimum number of bytes a single-user notebook server is guaranteed to have available.

Allows the following suffixes:
  • K -> Kilobytes

  • M -> Megabytes

  • G -> Gigabytes

  • T -> Terabytes

This is a configuration setting. Your spawner must implement support for the limit to work. The default spawner, LocalProcessSpawner, does not implement this support. A custom spawner must add support for this setting for it to be enforced.

mem_limit c.KubeSpawner.mem_limit = ByteSpecification(None)#

Maximum number of bytes a single-user notebook server is allowed to use.

Allows the following suffixes:
  • K -> Kilobytes

  • M -> Megabytes

  • G -> Gigabytes

  • T -> Terabytes

If the single user server tries to allocate more memory than this, it will fail. There is no guarantee that the single-user notebook server will be able to allocate this much memory - only that it can not allocate more than this.

This is a configuration setting. Your spawner must implement support for the limit to work. The default spawner, LocalProcessSpawner, does not implement this support. A custom spawner must add support for this setting for it to be enforced.

modify_pod_hook c.KubeSpawner.modify_pod_hook = Callable(None)#

Callable to augment the Pod object before launching.

Expects a callable that takes two parameters:

  1. The spawner object that is doing the spawning

  2. The Pod object that is to be launched

You should modify the Pod object and return it.

This can be a coroutine if necessary. When set to none, no augmenting is done.

This is very useful if you want to modify the pod being launched dynamically. Note that the spawner object can change between versions of KubeSpawner and JupyterHub, so be careful relying on this!

namespace c.KubeSpawner.namespace = Unicode('')#

Kubernetes namespace to spawn user pods in.

Assuming that you are not running with enable_user_namespaces turned on, if running inside a kubernetes cluster with service accounts enabled, defaults to the current namespace, and if not, defaults to default.

If you are running with enable_user_namespaces, this parameter is ignored in favor of the user_namespace_template template resolved with the hub namespace and the user name, with the caveat that if the hub namespace is default the user namespace will have the prefix user rather than default.

node_affinity_preferred c.KubeSpawner.node_affinity_preferred = List()#

Affinities describe where pods prefer or require to be scheduled, they may prefer or require a node to have a certain label or be in proximity / remoteness to another pod. To learn more visit https://kubernetes.io/docs/concepts/configuration/assign-pod-node/

Pass this field an array of “PreferredSchedulingTerm” objects.* * https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#preferredschedulingterm-v1-core

node_affinity_required c.KubeSpawner.node_affinity_required = List()#

Affinities describe where pods prefer or require to be scheduled, they may prefer or require a node to have a certain label or be in proximity / remoteness to another pod. To learn more visit https://kubernetes.io/docs/concepts/configuration/assign-pod-node/

Pass this field an array of “NodeSelectorTerm” objects.* * https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#nodeselectorterm-v1-core

node_selector c.KubeSpawner.node_selector = Dict()#

The dictionary Selector labels used to match the Nodes where Pods will be launched.

Default is None and means it will be launched in any available Node.

For example to match the Nodes that have a label of disktype: ssd use:

c.KubeSpawner.node_selector = {'disktype': 'ssd'}
notebook_dir c.KubeSpawner.notebook_dir = Unicode('')#

Path to the notebook directory for the single-user server.

The user sees a file listing of this directory when the notebook interface is started. The current interface does not easily allow browsing beyond the subdirectories in this directory’s tree.

~ will be expanded to the home directory of the user, and {username} will be replaced with the name of the user.

Note that this does not prevent users from accessing files outside of this path! They can do so with many other means.

oauth_client_allowed_scopes c.KubeSpawner.oauth_client_allowed_scopes = Union()#

Allowed scopes for oauth tokens issued by this server’s oauth client.

This sets the maximum and default scopes assigned to oauth tokens issued by a single-user server’s oauth client (i.e. tokens stored in browsers after authenticating with the server), defining what actions the server can take on behalf of logged-in users.

Default is an empty list, meaning minimal permissions to identify users, no actions can be taken on their behalf.

If callable, will be called with the Spawner as a single argument. Callables may be async.

oauth_roles c.KubeSpawner.oauth_roles = Union()#

Allowed roles for oauth tokens.

Deprecated in 3.0: use oauth_client_allowed_scopes

This sets the maximum and default roles assigned to oauth tokens issued by a single-user server’s oauth client (i.e. tokens stored in browsers after authenticating with the server), defining what actions the server can take on behalf of logged-in users.

Default is an empty list, meaning minimal permissions to identify users, no actions can be taken on their behalf.

options_form c.KubeSpawner.options_form = Union()#

An HTML form for options a user can specify on launching their server.

The surrounding <form> element and the submit button are already provided.

For example:

Set your key:
<input name="key" val="default_key"></input>
<br>
Choose a letter:
<select name="letter" multiple="true">
  <option value="A">The letter A</option>
  <option value="B">The letter B</option>
</select>

The data from this form submission will be passed on to your spawner in self.user_options

Instead of a form snippet string, this could also be a callable that takes as one parameter the current spawner instance and returns a string. The callable will be called asynchronously if it returns a future, rather than a str. Note that the interface of the spawner class is not deemed stable across versions, so using this functionality might cause your JupyterHub upgrades to break.

options_from_form c.KubeSpawner.options_from_form = Callable()#

Interpret HTTP form data

Form data will always arrive as a dict of lists of strings. Override this function to understand single-values, numbers, etc.

This should coerce form data into the structure expected by self.user_options, which must be a dict, and should be JSON-serializeable, though it can contain bytes in addition to standard JSON data types.

This method should not have any side effects. Any handling of user_options should be done in .start() to ensure consistent behavior across servers spawned via the API and form submission page.

Instances will receive this data on self.user_options, after passing through this function, prior to Spawner.start.

Changed in version 1.0: user_options are persisted in the JupyterHub database to be reused on subsequent spawns if no options are given. user_options is serialized to JSON as part of this persistence (with additional support for bytes in case of uploaded file data), and any non-bytes non-jsonable values will be replaced with None if the user_options are re-used.

pod_affinity_preferred c.KubeSpawner.pod_affinity_preferred = List()#

Affinities describe where pods prefer or require to be scheduled, they may prefer or require a node to have a certain label or be in proximity / remoteness to another pod. To learn more visit https://kubernetes.io/docs/concepts/configuration/assign-pod-node/

Pass this field an array of “WeightedPodAffinityTerm” objects.* * https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#weightedpodaffinityterm-v1-core

pod_affinity_required c.KubeSpawner.pod_affinity_required = List()#

Affinities describe where pods prefer or require to be scheduled, they may prefer or require a node to have a certain label or be in proximity / remoteness to another pod. To learn more visit https://kubernetes.io/docs/concepts/configuration/assign-pod-node/

Pass this field an array of “PodAffinityTerm” objects.* * https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#podaffinityterm-v1-core

pod_anti_affinity_preferred c.KubeSpawner.pod_anti_affinity_preferred = List()#

Affinities describe where pods prefer or require to be scheduled, they may prefer or require a node to have a certain label or be in proximity / remoteness to another pod. To learn more visit https://kubernetes.io/docs/concepts/configuration/assign-pod-node/

Pass this field an array of “WeightedPodAffinityTerm” objects.* * https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#weightedpodaffinityterm-v1-core

pod_anti_affinity_required c.KubeSpawner.pod_anti_affinity_required = List()#

Affinities describe where pods prefer or require to be scheduled, they may prefer or require a node to have a certain label or be in proximity / remoteness to another pod. To learn more visit https://kubernetes.io/docs/concepts/configuration/assign-pod-node/

Pass this field an array of “PodAffinityTerm” objects.* * https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#podaffinityterm-v1-core

pod_connect_ip c.KubeSpawner.pod_connect_ip = Unicode('')#

The IP address (or hostname) of user’s pods which KubeSpawner connects to. If you do not specify the value, KubeSpawner will use the pod IP.

e.g. jupyter-{username}--{servername}.notebooks.jupyterhub.svc.cluster.local,

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration. The username and servername come escaped to follow the DNS label standard.

Trailing - characters in each domain level are stripped for safe handling of empty server names (user default servers).

This must be unique within the namespace the pods are being spawned in, so if you are running multiple jupyterhubs spawning in the same namespace, consider setting this to be something more unique.

pod_name_template c.KubeSpawner.pod_name_template = Unicode('jupyter-{username}--{servername}')#

Template to use to form the name of user’s pods.

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration. The username and servername come escaped to follow the DNS label standard.

Trailing - characters are stripped for safe handling of empty server names (user default servers).

This must be unique within the namespace the pods are being spawned in, so if you are running multiple jupyterhubs spawning in the same namespace, consider setting this to be something more unique.

Changed in version 0.12: -- delimiter added to the template, where it was implicitly added to the servername field before. Additionally, username--servername delimiter was - instead of --, allowing collisions in certain circumstances.

pod_security_context c.KubeSpawner.pod_security_context = Union()#

A Kubernetes security context for the pod. Note that all configuration options within here should be camelCased.

What is configured here has higher priority than fs_gid and supplemental_gids, but lower priority than what is set in the container_security_context.

Note that anything configured on the Pod level will influence all containers, including init containers and sidecar containers.

Rely on the Kubernetes reference for details on allowed configuration.

poll_interval c.KubeSpawner.poll_interval = Int(30)#

Interval (in seconds) on which to poll the spawner for single-user server’s status.

At every poll interval, each spawner’s .poll method is called, which checks if the single-user server is still running. If it isn’t running, then JupyterHub modifies its own state accordingly and removes appropriate routes from the configurable proxy.

port c.KubeSpawner.port = Int(0)#

The port for single-user servers to listen on.

Defaults to 0, which uses a randomly allocated port number each time.

If set to a non-zero value, all Spawners will use the same port, which only makes sense if each server is on a different address, e.g. in containers.

New in version 0.7.

post_stop_hook c.KubeSpawner.post_stop_hook = Any(None)#

An optional hook function that you can implement to do work after the spawner stops.

This can be set independent of any concrete spawner implementation.

pre_spawn_hook c.KubeSpawner.pre_spawn_hook = Any(None)#

An optional hook function that you can implement to do some bootstrapping work before the spawner starts. For example, create a directory for your user or load initial content.

This can be set independent of any concrete spawner implementation.

This maybe a coroutine.

Example:

from subprocess import check_call
def my_hook(spawner):
    username = spawner.user.name
    check_call(['./examples/bootstrap-script/bootstrap.sh', username])

c.Spawner.pre_spawn_hook = my_hook
priority_class_name c.KubeSpawner.priority_class_name = Unicode('')#

The priority class that the pods will use.

See https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption for more information on how pod priority works.

privileged c.KubeSpawner.privileged = Bool(False)#

Whether to run the pod with a privileged security context.

profile_form_template c.KubeSpawner.profile_form_template = Unicode('')#

Literal Jinja2 template for constructing profile list shown to user.

Used when profile_list is set.

The contents of profile_list are passed in to the template. This should be used to construct the contents of a HTML form. When posted, this form is expected to have an item with name profile and the value the index of the profile in profile_list.

When this traitlet is not set, the default template form.html from the directory kubespawner/templates is used. Admins can override this by setting the additional_profile_form_template_paths config to a directory with jinja2 templates, and any file named form.html in there will be used instead of the default.

Using additional_profile_form_template_paths is recommended instead of this.

profile_list c.KubeSpawner.profile_list = Union()#

List of profiles to offer for selection by the user.

Signature is: List(Dict()), where each item is a dictionary that has two keys:

  • display_name: the human readable display name (should be HTML safe)

  • default: (Optional Bool) True if this is the default selected option

  • description: Optional description of this profile displayed to the user.

  • slug: (Optional) the machine readable string to identify the profile (missing slugs are generated from display_name)

  • kubespawner_override: a dictionary with overrides to apply to the KubeSpawner settings. Each value can be either the final value to change or a callable that take the KubeSpawner instance as parameter and return the final value. This can be further overridden by ‘profile_options’ If the traitlet being overriden is a dictionary, the dictionary will be recursively updated, rather than overriden. If you want to remove a key, set its value to None

  • profile_options: A dictionary of sub-options that allow users to further customize the selected profile. By default, these are rendered as a dropdown with the label provided by display_name. Items should have a unique key representing the customization, and the value is a dictionary with the following keys:

    • display_name: Name used to identify this particular option

    • unlisted_choice: Object to specify if there should be a free-form field if the user selected “Other” as a choice:

      • enabled: Boolean, whether the free form input should be enabled

      • display_name: String, label for input field

      • display_name_in_choices: Optional, display name for the choice to specify an unlisted choice in the dropdown list of pre-defined choices. Defaults to “Other…”.

      • validation_regex: Optional, regex that the free form input should match, eg. ^pangeo/.*$.

      • validation_message: Optional, validation message for the regex. Should describe the required input format in a human-readable way.

      • kubespawner_override: a dictionary with overrides to apply to the KubeSpawner settings, where the string {value} will be substituted with what was filled in by the user if its found in string values anywhere in the dictionary. As an example, if the choice made is about an image tag for an image only to be used with JupyterLab, it could look like this:

        {
            "image_spec": "jupyter/datascience-notebook:{value}",
            "default_url": "/lab",
            "extra_labels: {
               "user-specified-image-tag": "{value}",
            },
        }
        
    • choices: A dictionary containing list of choices for the user to choose from to set the value for this particular option. The key is an identifier for this choice, and the value is a dictionary with the following possible keys:

      • display_name: Human readable display name for this choice.

      • default: (optional Bool) True if this is the default selected choice

      • kubespawner_override: A dictionary with overrides to apply to the KubeSpawner settings, on top of whatever was applied with the kubespawner_override key for the profile itself. The key should be the name of the kubespawner setting, and value can be either the final value or a callable that returns the final value when called with the spawner instance as the only parameter. The callable may be async. If the traitlet being overriden is a dictionary, the dictionary will be recursively updated, rather than overriden. If you want to remove a key, set its value to None

kubespawner setting overrides work in the following manner, with items further in the list replacing (not merging with) items earlier in the list:

  1. Settings directly set on KubeSpawner, via c.KubeSpawner.<traitlet_name>

  2. kubespawner_override in the profile the user has chosen

  3. kubespawner_override in the specific choices the user has made within the profile, applied linearly based on the ordering of the option in the profile definition configuration

Example:

c.KubeSpawner.profile_list = [
    {
        'display_name': 'Demo - profile_list entry 1',
        'description': 'Demo description for profile_list entry 1, and it should look good even though it is a bit lengthy.',
        'slug': 'demo-1',
        'default': True,
        'profile_options': {
            'image': {
                'display_name': 'Image',
                'choices': {
                    'base': {
                        'display_name': 'jupyter/base-notebook:latest',
                        'kubespawner_override': {
                            'image': 'jupyter/base-notebook:latest'
                        },
                    },
                    'minimal': {
                        'display_name': 'jupyter/minimal-notebook:latest',
                        'default': True,
                        'kubespawner_override': {
                            'image': 'jupyter/minimal-notebook:latest'
                        },
                    },
                },
                'unlisted_choice': {
                    'enabled': True,
                    'display_name': 'Other image',
                    'display_name_in_choices': 'Enter image manually',
                    'validation_regex': '^jupyter/.+:.+$',
                    'validation_message': 'Must be an image matching ^jupyter/<name>:<tag>$',
                    'kubespawner_override': {'image': '{value}'},
                },
            },
        },
        'kubespawner_override': {
            'default_url': '/lab',
        },
    },
    {
        'display_name': 'Demo - profile_list entry 2',
        'slug': 'demo-2',
        'kubespawner_override': {
            'extra_resource_guarantees': {"nvidia.com/gpu": "1"},
        },
    },
]

Instead of a list of dictionaries, this could also be a callable that takes as one parameter the current spawner instance and returns a list of dictionaries. The callable will be called asynchronously if it returns a future, rather than a list. Note that the interface of the spawner class is not deemed stable across versions, so using this functionality might cause your JupyterHub or kubespawner upgrades to break.

pvc_name_template c.KubeSpawner.pvc_name_template = Unicode('claim-{username}--{servername}')#

Template to use to form the name of user’s pvc.

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration. The username and servername come escaped to follow the DNS label standard.

Trailing - characters are stripped for safe handling of empty server names (user default servers).

This must be unique within the namespace the pvc are being spawned in, so if you are running multiple jupyterhubs spawning in the same namespace, consider setting this to be something more unique.

Changed in version 0.12: -- delimiter added to the template, where it was implicitly added to the servername field before. Additionally, username--servername delimiter was - instead of --, allowing collisions in certain circumstances.

scheduler_name c.KubeSpawner.scheduler_name = Unicode(None)#

Set the pod’s scheduler explicitly by name. See the Kubernetes documentation for more information.

secret_mount_path c.KubeSpawner.secret_mount_path = Unicode('/etc/jupyterhub/ssl/')#

Location to mount the spawned pod’s certificates needed for internal_ssl functionality.

secret_name_template c.KubeSpawner.secret_name_template = Unicode('jupyter-{username}{servername}')#

Template to use to form the name of user’s secret.

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration. The username and servername come escaped to follow the DNS label standard.

This must be unique within the namespace the pvc are being spawned in, so if you are running multiple jupyterhubs spawning in the same namespace, consider setting this to be something more unique.

server_token_scopes c.KubeSpawner.server_token_scopes = Union()#

The list of scopes to request for $JUPYTERHUB_API_TOKEN

If not specified, the scopes in the server role will be used (unchanged from pre-4.0).

If callable, will be called with the Spawner instance as its sole argument (JupyterHub user available as spawner.user).

JUPYTERHUB_API_TOKEN will be assigned the _subset_ of these scopes that are held by the user (as in oauth_client_allowed_scopes).

New in version 4.0.

service_account c.KubeSpawner.service_account = Unicode(None)#

The service account to be mounted in the spawned user pod.

The token of the service account is NOT mounted by default. This makes sure that we don’t accidentally give access to the whole kubernetes API to the users in the spawned pods. Set automount_service_account_token True to mount it.

This serviceaccount must already exist in the namespace the user pod is being spawned in.

services_enabled c.KubeSpawner.services_enabled = Bool(False)#

Enable fronting the user pods with a kubernetes service.

This is useful in cases when network rules don’t allow direct traffic routing to pods in a cluster. Should be enabled when using jupyterhub with a service mesh like istio with mTLS enabled.

ssl_alt_names c.KubeSpawner.ssl_alt_names = List()#

List of SSL alt names

May be set in config if all spawners should have the same value(s), or set at runtime by Spawner that know their names.

ssl_alt_names_include_local c.KubeSpawner.ssl_alt_names_include_local = Bool(True)#

Whether to include DNS:localhost, IP:127.0.0.1 in alt names

start_timeout c.KubeSpawner.start_timeout = Int(60)#

Timeout (in seconds) before giving up on starting of single-user server.

This is the timeout for start to return, not the timeout for the server to respond. Callers of spawner.start will assume that startup has failed if it takes longer than this. start should return when the server process is started and its location is known.

storage_access_modes c.KubeSpawner.storage_access_modes = List()#

List of access modes the user has for the pvc.

The access modes are:

  • ReadWriteOnce : the volume can be mounted as read-write by a single node

  • ReadOnlyMany : the volume can be mounted read-only by many nodes

  • ReadWriteMany : the volume can be mounted as read-write by many nodes

See the Kubernetes documentation for more information on how access modes work.

storage_capacity c.KubeSpawner.storage_capacity = Unicode(None)#

The amount of storage space to request from the volume that the pvc will mount to. This amount will be the amount of storage space the user has to work with on their notebook. If left blank, the kubespawner will not create a pvc for the pod.

This will be added to the resources: requests: storage: in the k8s pod spec.

See the Kubernetes documentation

for more information on how storage works.

Quantities can be represented externally as unadorned integers, or as fixed-point integers with one of these SI suffices (E, P, T, G, M, K, m) or their power-of-two equivalents (Ei, Pi, Ti, Gi, Mi, Ki). For example, the following represent roughly the same value: 128974848, 129e6, 129M, 123Mi.

storage_class c.KubeSpawner.storage_class = Unicode(None)#

The storage class that the pvc will use.

This will be added to the annotations: volume.beta.kubernetes.io/storage-class: in the pvc metadata.

This will determine what type of volume the pvc will request to use. If one exists that matches the criteria of the StorageClass, the pvc will mount to that. Otherwise, b/c it has a storage class, k8s will dynamically spawn a pv for the pvc to bind to and a machine in the cluster for the pv to bind to.

Note that an empty string is a valid value and is always interpreted to be requesting a pv with no class.

See the Kubernetes documentation for more information on how StorageClasses work.

storage_extra_annotations c.KubeSpawner.storage_extra_annotations = Dict()#

Extra kubernetes annotations to set on the user PVCs.

The keys and values specified here would be set as annotations on the PVCs created by kubespawner for the user. Note that these are only set when the PVC is created, not later when this setting is updated.

See the Kubernetes documentation for more info on what annotations are and why you might want to use them!

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration.

storage_extra_labels c.KubeSpawner.storage_extra_labels = Dict()#

Extra kubernetes labels to set on the user PVCs.

The keys and values specified here would be set as labels on the PVCs created by kubespawner for the user. Note that these are only set when the PVC is created, not later when this setting is updated.

See the Kubernetes documentation for more info on what labels are and why you might want to use them!

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration. The username and servername come escaped to follow the DNS label standard.

storage_pvc_ensure c.KubeSpawner.storage_pvc_ensure = Bool(False)#

Ensure that a PVC exists for each user before spawning.

Set to true to create a PVC named with pvc_name_template if it does not exist for the user when their pod is spawning.

storage_selector c.KubeSpawner.storage_selector = Dict()#

The dictionary Selector labels used to match a PersistentVolumeClaim to a PersistentVolume.

Default is None and means it will match based only on other storage criteria.

For example to match the Nodes that have a label of content: jupyter use:

c.KubeSpawner.storage_selector = {'matchLabels':{'content': 'jupyter'}}

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration. The username and servername come escaped to follow the DNS label standard.

supplemental_gids c.KubeSpawner.supplemental_gids = Union()#

A list of GIDs that should be set as additional supplemental groups to the user that the container runs as.

Instead of a list of integers, this could also be a callable that takes as one parameter the current spawner instance and returns a list of integers. The callable will be called asynchronously if it returns a future, rather than a list. Note that the interface of the spawner class is not deemed stable across versions, so using this functionality might cause your JupyterHub or kubespawner upgrades to break.

You may have to set this if you are deploying to an environment with RBAC/SCC enforced and pods run with a ‘restricted’ SCC which results in the image being run as an assigned user ID. The supplemental group IDs would need to include the corresponding group ID of the user ID the image normally would run as. The image must setup all directories/files any application needs access to, as group writable.

tolerations c.KubeSpawner.tolerations = List()#

List of tolerations that are to be assigned to the pod in order to be able to schedule the pod on a node with the corresponding taints. See the official Kubernetes documentation for additional details https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/

Pass this field an array of “Toleration” objects * https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#toleration-v1-core

Example:

[
    {
        'key': 'key',
        'operator': 'Equal',
        'value': 'value',
        'effect': 'NoSchedule'
    },
    {
        'key': 'key',
        'operator': 'Exists',
        'effect': 'NoSchedule'
    }
]
uid c.KubeSpawner.uid = Union(None)#

The UID to run the single-user server containers as.

This UID should ideally map to a user that already exists in the container image being used. Running as root is discouraged.

Instead of an integer, this could also be a callable that takes as one parameter the current spawner instance and returns an integer. The callable will be called asynchronously if it returns a future. Note that the interface of the spawner class is not deemed stable across versions, so using this functionality might cause your JupyterHub or kubespawner upgrades to break.

If set to None, the user specified with the USER directive in the container metadata is used.

user_namespace_annotations c.KubeSpawner.user_namespace_annotations = Dict()#

Kubernetes annotations that user namespaces will get (only if enable_user_namespaces is True).

Note that these are only set when the namespaces are created, not later when this setting is updated.

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration. The username and servername come escaped to follow the DNS label standard.

user_namespace_labels c.KubeSpawner.user_namespace_labels = Dict()#

Kubernetes labels that user namespaces will get (only if enable_user_namespaces is True).

Note that these are only set when the namespaces are created, not later when this setting is updated.

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration. The username and servername come escaped to follow the DNS label standard.

user_namespace_template c.KubeSpawner.user_namespace_template = Unicode('{hubnamespace}-{username}')#

Template to use to form the namespace of user’s pods (only if enable_user_namespaces is True).

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration. The username and servername come escaped to follow the DNS label standard.

volume_mounts c.KubeSpawner.volume_mounts = List()#

List of paths on which to mount volumes in the user notebook’s pod.

This list will be added to the values of the volumeMounts key under the user’s container in the kubernetes pod spec, so you should use the same structure as that. Each item in the list should be a dictionary with at least these two keys:

  • mountPath The path on the container in which we want to mount the volume.

  • name The name of the volume we want to mount, as specified in the volumes config.

See the Kubernetes documentation for more information on how the volumeMount item works.

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration. The username and servername come escaped to follow the DNS label standard.

volumes c.KubeSpawner.volumes = List()#

List of Kubernetes Volume specifications that will be mounted in the user pod.

This list will be directly added under volumes in the kubernetes pod spec, so you should use the same structure. Each item in the list must have the following two keys:

  • name Name that’ll be later used in the volume_mounts config to mount this volume at a specific path.

  • <name-of-a-supported-volume-type> (such as hostPath, persistentVolumeClaim, etc) The key name determines the type of volume to mount, and the value should be an object specifying the various options available for that kind of volume.

See the Kubernetes documentation for more information on the various kinds of volumes available and their options. Your kubernetes cluster must already be configured to support the volume types you want to use.

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration. The username and servername come escaped to follow the DNS label standard.

working_dir c.KubeSpawner.working_dir = Unicode(None)#

The working directory where the Notebook server will be started inside the container. Defaults to None so the working directory will be the one defined in the Dockerfile.

{username}, {userid}, {servername}, {hubnamespace}, {unescaped_username}, and {unescaped_servername} will be expanded if found within strings of this configuration. The username and servername come escaped to follow the DNS label standard.