diff --git a/kubespawner/objects.py b/kubespawner/objects.py
index ae0d42e..2c8201c 100644
--- a/kubespawner/objects.py
+++ b/kubespawner/objects.py
@@ -250,7 +250,8 @@ def make_pvc(
     storage_class,
     access_modes,
     storage,
-    labels
+    labels,
+    annotations={}
     ):
     """
     Make a k8s pvc specification for running a user notebook.
@@ -272,9 +273,7 @@ def make_pvc(
     pvc.api_version = "v1"
     pvc.metadata = V1ObjectMeta()
     pvc.metadata.name = name
-    pvc.metadata.annotations = {}
-    if storage_class:
-        pvc.metadata.annotations.update({"volume.beta.kubernetes.io/storage-class": storage_class})
+    pvc.metadata.annotations = annotations
     pvc.metadata.labels = {}
     pvc.metadata.labels.update(labels)
     pvc.spec = V1PersistentVolumeClaimSpec()
@@ -282,6 +281,10 @@ def make_pvc(
     pvc.spec.resources = V1ResourceRequirements()
     pvc.spec.resources.requests = {"storage": storage}
 
+    if storage_class:
+        pvc.metadata.annotations.update({"volume.beta.kubernetes.io/storage-class": storage_class})
+        pvc.spec.storage_class_name = storage_class
+
     return pvc
 
 def make_ingress(
diff --git a/kubespawner/spawner.py b/kubespawner/spawner.py
index e3da4d9..163303a 100644
--- a/kubespawner/spawner.py
+++ b/kubespawner/spawner.py
@@ -783,12 +783,8 @@ class KubeSpawner(Spawner):
         labels = {
             'heritage': 'jupyterhub',
             'app': 'jupyterhub',
-            'hub.jupyter.org/username': escapism.escape(self.user.name)
         }
 
-        if self.name:
-            # FIXME: Make sure this is dns safe?
-            labels['hub.jupyter.org/servername'] = self.name
         labels.update(extra_labels)
         return labels
 
@@ -801,6 +797,17 @@ class KubeSpawner(Spawner):
         labels.update(self.pod_reflector.labels)
         return self._build_common_labels(labels)
 
+    def _build_common_annotations(self, extra_annotations):
+        # Annotations don't need to be escaped
+        annotations = {
+            'hub.jupyter.org/username': self.user.name
+        }
+        if self.name:
+            annotations['hub.jupyter.org/servername'] = self.name
+
+        annotations.update(extra_annotations)
+        return annotations
+
     @gen.coroutine
     def get_pod_manifest(self):
         """
@@ -822,7 +829,7 @@ class KubeSpawner(Spawner):
             real_cmd = None
 
         labels = self._build_pod_labels(self._expand_all(self.singleuser_extra_labels))
-        annotations = self._expand_all(self.singleuser_extra_annotations)
+        annotations = self._build_common_annotations(self._expand_all(self.singleuser_extra_annotations))
 
         return make_pod(
             name=self.pod_name,
@@ -861,12 +868,15 @@ class KubeSpawner(Spawner):
         """
         labels = self._build_common_labels(self._expand_all(self.user_storage_extra_labels))
 
+        annotations = self._build_common_annotations({})
+
         return make_pvc(
             name=self.pvc_name,
             storage_class=self.user_storage_class,
             access_modes=self.user_storage_access_modes,
             storage=self.user_storage_capacity,
-            labels=labels
+            labels=labels,
+            annotations=annotations
         )
 
     def is_pod_running(self, pod):

Long user names used in pod and pvc labels not allowed by k8s
The `hub.jupyter.org/username` label in pod or pvc manifests (e.g. https://github.com/jupyterhub/kubespawner/blob/master/kubespawner/spawner.py#L663) when user name has more than 63 characters will make the creation of the pod fail with a error like this:

error: invalid label value: "hub.jupyter.org/username=notebook-a8e986f5974c1ee896fa7717cad98511be0388b3d667535d9af7fe18feab7e3d": must be no more than 63 characters

The username I'm using comes from an external OAuth2 IdP and has a fixed length of 64 characters so any pod creation will  always fail. As a workaround I have changed the label to `hub.jupyter.org/userid` and use the id which should be small enough. However I'm not sure if that's the right approach. 


diff --git a/tests/test_objects.py b/tests/test_objects.py
index a9f0311..d69b8d3 100644
--- a/tests/test_objects.py
+++ b/tests/test_objects.py
@@ -787,6 +787,7 @@ def test_make_resources_all():
             }
         },
         'spec': {
+            'storageClassName': 'gce-standard-storage',
             'accessModes': ['ReadWriteOnce'],
             'resources': {
                 'requests': {

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest configurable-http-proxy
apt-get update apt-get install -y gcc
3.9
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alembic==1.15.2
annotated-types==0.7.0
arrow==1.3.0
async-generator==1.10
attrs==25.3.0
cachetools==5.5.2
certifi==2025.1.31
certipy==0.2.2
cffi==1.17.1
charset-normalizer==3.4.1
click==8.1.8
configurable-http-proxy==0.3.0
cryptography==44.0.2
escapism==1.0.1
exceptiongroup==1.2.2
fqdn==1.5.1
google-auth==2.38.0
greenlet==3.1.1
idna==3.10
importlib_metadata==8.6.1
iniconfig==2.1.0
ipaddress==1.0.23
isoduration==20.11.0
Jinja2==3.1.6
jsonpointer==3.0.0
jsonschema==4.23.0
jsonschema-specifications==2024.10.1
jupyter-events==0.12.0
jupyterhub==5.2.1
-e git+https://github.com/jupyterhub/kubespawner.git@5c9bf38731e8f5b04b5001d78987b7408ed1ae00#egg=jupyterhub_kubespawner
kubernetes==3.0.0
Mako==1.3.9
MarkupSafe==3.0.2
oauthlib==3.2.2
packaging==24.2
pamela==1.2.0
pluggy==1.5.0
prometheus_client==0.21.1
pyasn1==0.6.1
pyasn1_modules==0.4.2
pycparser==2.22
pydantic==2.11.1
pydantic_core==2.33.0
pytest==8.3.5
python-dateutil==2.9.0.post0
python-json-logger==3.3.0
PyYAML==6.0.2
referencing==0.36.2
requests==2.32.3
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
rpds-py==0.24.0
rsa==4.9
six==1.17.0
SQLAlchemy==2.0.40
tomli==2.2.1
tornado==6.4.2
traitlets==5.14.3
types-python-dateutil==2.9.0.20241206
typing-inspection==0.4.0
typing_extensions==4.13.0
uri-template==1.3.0
urllib3==2.3.0
webcolors==24.11.1
websocket_client==0.40.0
zipp==3.21.0

name: kubespawner
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alembic==1.15.2
      - annotated-types==0.7.0
      - arrow==1.3.0
      - async-generator==1.10
      - attrs==25.3.0
      - cachetools==5.5.2
      - certifi==2025.1.31
      - certipy==0.2.2
      - cffi==1.17.1
      - charset-normalizer==3.4.1
      - click==8.1.8
      - configurable-http-proxy==0.3.0
      - cryptography==44.0.2
      - escapism==1.0.1
      - exceptiongroup==1.2.2
      - fqdn==1.5.1
      - google-auth==2.38.0
      - greenlet==3.1.1
      - idna==3.10
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - ipaddress==1.0.23
      - isoduration==20.11.0
      - jinja2==3.1.6
      - jsonpointer==3.0.0
      - jsonschema==4.23.0
      - jsonschema-specifications==2024.10.1
      - jupyter-events==0.12.0
      - jupyterhub==5.2.1
      - kubernetes==3.0.0
      - mako==1.3.9
      - markupsafe==3.0.2
      - oauthlib==3.2.2
      - packaging==24.2
      - pamela==1.2.0
      - pluggy==1.5.0
      - prometheus-client==0.21.1
      - pyasn1==0.6.1
      - pyasn1-modules==0.4.2
      - pycparser==2.22
      - pydantic==2.11.1
      - pydantic-core==2.33.0
      - pytest==8.3.5
      - python-dateutil==2.9.0.post0
      - python-json-logger==3.3.0
      - pyyaml==6.0.2
      - referencing==0.36.2
      - requests==2.32.3
      - rfc3339-validator==0.1.4
      - rfc3986-validator==0.1.1
      - rpds-py==0.24.0
      - rsa==4.9
      - six==1.17.0
      - sqlalchemy==2.0.40
      - tomli==2.2.1
      - tornado==6.4.2
      - traitlets==5.14.3
      - types-python-dateutil==2.9.0.20241206
      - typing-extensions==4.13.0
      - typing-inspection==0.4.0
      - uri-template==1.3.0
      - urllib3==2.3.0
      - webcolors==24.11.1
      - websocket-client==0.40.0
      - zipp==3.21.0
prefix: /opt/conda/envs/kubespawner

tests/test_objects.py::test_make_resources_all
tests/test_objects.py::test_make_simplest_pod tests/test_objects.py::test_make_labeled_pod tests/test_objects.py::test_make_annotated_pod tests/test_objects.py::test_make_pod_with_image_pull_secrets tests/test_objects.py::test_set_pod_uid_fs_gid tests/test_objects.py::test_run_privileged_container tests/test_objects.py::test_make_pod_resources_all tests/test_objects.py::test_make_pod_with_env tests/test_objects.py::test_make_pod_with_lifecycle tests/test_objects.py::test_make_pod_with_init_containers tests/test_objects.py::test_make_pod_with_extra_container_config tests/test_objects.py::test_make_pod_with_extra_pod_config tests/test_objects.py::test_make_pod_with_extra_containers tests/test_objects.py::test_make_pod_with_extra_resources tests/test_objects.py::test_make_pvc_simple tests/test_objects.py::test_make_pod_with_service_account
kubespawner/objects.py kubespawner/spawner.py
kubespawner/objects.py kubespawner/spawner.py