Kubernetes Python Client
Official Python client for the Kubernetes API. Current version: 35.0.0 (Mar 2026). Client major version maps to Kubernetes server minor version (client 35.x ≈ k8s 1.35). Versions jumped from 12.x to 17.x — not a mistake. Two config methods: load_kube_config() for local (~/.kube/config) and load_incluster_config() for pods. Must use stream() module for exec/attach — direct call removed in v4. Still pre-1.0 in practice despite high version numbers.
Common errors
-
kubernetes.config.config_exception.ConfigException: Invalid kube-config file. No configuration found.
cause The Python client cannot find or parse the kube-config file, typically located at `~/.kube/config`, or the file is malformed or inaccessible. This often happens when running locally without a properly configured `kubeconfig` or when a container tries to load local config but no file is mounted.fixEnsure `~/.kube/config` exists and is valid, or explicitly pass the path to `config.load_kube_config(config_file='/path/to/kubeconfig')`. If running inside a Kubernetes cluster, use `config.load_incluster_config()` instead. -
kubernetes.client.rest.ApiException: (401) Reason: Unauthorized
cause The authenticated user or service account lacks the necessary Role-Based Access Control (RBAC) permissions to perform the requested operation on the Kubernetes API server.fixCheck the RBAC roles and role bindings for the identity used by your client (e.g., service account, user) and ensure it has the appropriate verbs (get, list, create, delete, etc.) for the target resources (pods, deployments, etc.) in the relevant namespaces. -
kubernetes.config.config_exception.ConfigException: Service host/port is not set.
cause This error occurs when `config.load_incluster_config()` is called, but the environment variables `KUBERNETES_SERVICE_HOST` and `KUBERNETES_SERVICE_PORT` are not set. These variables are automatically injected into pods running within a Kubernetes cluster.fixEnsure your Python application is running inside a Kubernetes pod when calling `config.load_incluster_config()`. If testing outside a cluster, use `config.load_kube_config()` instead. -
api.connect_get_namespaced_pod_exec(...) (direct call for exec/attach)
cause Starting from `kubernetes` Python client version 4.0, direct calls to `connect_get_namespaced_pod_exec` or `connect_post_namespaced_pod_exec` for interactive `exec` or `attach` are deprecated and will not work correctly, often resulting in 'Upgrade required' messages or hangs. The `stream` module must be used.fixUse the `stream` module to wrap the API call. For example: `from kubernetes.stream import stream; resp = stream(api.connect_get_namespaced_pod_exec, name, namespace, command=command, ...)` -
ModuleNotFoundError: No module named 'kubernetes' (or 'kubernetes.dynamic')
cause The `kubernetes` Python client library is not installed in the active Python environment, or a specific sub-module like `kubernetes.dynamic` is missing, possibly due to an incomplete installation or an old version of the client.fixInstall the library using pip: `pip install kubernetes`. If `kubernetes.dynamic` is missing, ensure you have a recent version installed, as it's part of the standard package. `pip install --upgrade kubernetes` might resolve it.
Warnings
- breaking Client major version must match Kubernetes server minor version. Client 35.x works with k8s 1.35. Mismatched versions cause ApiException or missing API objects.
- breaking Version numbers jumped from 12.x to 17.x — not a typo. Versions 13-16 were skipped to align with Kubernetes minor versions.
- breaking Direct exec/attach calls (v1.connect_get_namespaced_pod_exec()) removed in v4. Must use stream.stream() wrapper.
- gotcha load_kube_config() fails inside Kubernetes pods. load_incluster_config() fails outside pods. Code using only one will break in the other environment.
- gotcha load_incluster_config() raises ConfigException 'Service host/port is not set' when KUBERNETES_SERVICE_HOST env var is missing — i.e. when not running inside a pod.
- gotcha list_* API calls return a V1PodList with .items — not a plain list. Must iterate .items not the response object itself.
- gotcha Watch API streams indefinitely unless stopped or _request_timeout is set. Omitting timeout in production causes memory leaks.
Install
-
pip install kubernetes
Imports
- config loading (local + in-cluster)
from kubernetes import client, config # Wrong: only works locally — fails inside pods config.load_kube_config() # Wrong: only works in pods — fails locally config.load_incluster_config()
from kubernetes import client, config # Correct: handle both local and in-cluster try: config.load_incluster_config() # running inside a pod except config.ConfigException: config.load_kube_config() # running locally v1 = client.CoreV1Api() pods = v1.list_pod_for_all_namespaces(watch=False) for pod in pods.items: print(f'{pod.metadata.namespace}/{pod.metadata.name}') - exec into pod (stream module)
# Direct call removed in v4 — raises TypeError or ApiException resp = v1.connect_get_namespaced_pod_exec( 'my-pod', 'default', command=['ls', '/'], stdout=True )from kubernetes import client, config, stream config.load_kube_config() v1 = client.CoreV1Api() # Must use stream() wrapper for exec/attach resp = stream.stream( v1.connect_get_namespaced_pod_exec, 'my-pod', 'default', command=['ls', '/'], stderr=True, stdin=False, stdout=True, tty=False ) print(resp)
Quickstart
# pip install kubernetes
from kubernetes import client, config
# Load config — handles both local and in-pod
try:
config.load_incluster_config()
except config.ConfigException:
config.load_kube_config()
# Core API — pods, namespaces, nodes
v1 = client.CoreV1Api()
# List pods in all namespaces
pods = v1.list_pod_for_all_namespaces(watch=False)
for pod in pods.items:
print(f'{pod.metadata.namespace}/{pod.metadata.name}: {pod.status.phase}')
# Apps API — deployments
apps_v1 = client.AppsV1Api()
deployments = apps_v1.list_deployment_for_all_namespaces()
for d in deployments.items:
print(f'{d.metadata.name}: {d.spec.replicas} replicas')
# Get specific pod
pod = v1.read_namespaced_pod(name='my-pod', namespace='default')
print(pod.status.pod_ip)