Skip to content

Clients

Kubex separates the API layer (Api[T]) from the HTTP transport layer. You can swap transports without changing any application code.

create_client()

The create_client() factory is the recommended way to get a client. It auto-detects which HTTP library is installed and returns an async context manager — async with ensures the underlying connection pool is closed cleanly:

from kubex.client import create_client

async with await create_client() as client:
    # use the client
    ...

Auto-detection order: aiohttp is tried first, then httpx. If neither is installed, create_client() raises ImportError. aiohttp only runs on asyncio, so under any other runtime (e.g. trio) auto-detection always picks httpx.

To force a specific client:

from kubex.client import create_client, ClientChoice

# Force aiohttp
client = await create_client(client_class=ClientChoice.AIOHTTP)

# Force httpx
client = await create_client(client_class=ClientChoice.HTTPX)

Pass a pre-built ClientConfiguration to skip the auto-loading of kubeconfig / in-cluster credentials:

from kubex.configuration import ClientConfiguration

config = ClientConfiguration(url="https://my-cluster:6443", token="my-token")
client = await create_client(configuration=config)

Pass options= to control how the client behaves at request time (timeouts, proxy, pool size, etc.). See ClientOptions below for details:

from kubex.client import ClientOptions, create_client
from kubex.core.params import Timeout

client = await create_client(
    configuration=config,
    options=ClientOptions(timeout=Timeout(total=30.0), log_api_warnings=False),
)

ClientOptions

ClientOptions carries per-process choices about how the HTTP client behaves. These settings have nothing to do with kubeconfig or the in-cluster environment — connection parameters live on ClientConfiguration.

from kubex.client import ClientOptions
from kubex.core.params import Timeout

options = ClientOptions(
    timeout=Timeout(total=30.0),
    log_api_warnings=True,  # default
)

timeout

Controls the default HTTP timeout applied to every request made by this client. Accepted values:

Value Meaning
... (the default) Use the HTTP library's own default (httpx: 5 s per phase — connect, read, write, pool — with no overall cap; aiohttp: 300 s total, 30 s sock_connect)
None Disable timeouts entirely
int or float Treat as a total timeout in seconds (a wall-clock bound on both backends); coerced to Timeout automatically
Timeout(...) Use as-is for fine-grained per-phase control

Individual calls can override this via the request_timeout= parameter. Streaming calls (watch(), logs.stream()) only inherit the connection-setup part of this default. See Timeouts for the full picture.

log_api_warnings

When True (the default), kubex emits a Python UserWarning for every Warning: HTTP response header returned by the API server. The Kubernetes API server uses these headers to flag deprecated API usage (e.g. calling a removed API version). Set to False to silence them.

proxy

Configures an outbound HTTP proxy for all requests. Accepted values:

Value Meaning
None (the default) No proxy
str Single proxy URL for all requests, e.g. "http://proxy.corp.example.com:8080"
dict[str, str] Per-scheme map with "http" and/or "https" keys
# Single proxy URL (basic auth via userinfo)
options = ClientOptions(proxy="http://user:[email protected]:8080")

# Per-scheme map
options = ClientOptions(proxy={"https": "http://proxy.corp.example.com:8080"})

keep_alive

Whether to reuse idle connections (connection keep-alive). Set to False to close each connection immediately after use.

# Disable keep-alive
options = ClientOptions(keep_alive=False)

keep_alive_timeout

Idle-connection lifetime in seconds. Uses the three-state sentinel pattern:

Value Meaning
... (the default) Library default (httpx: 5 s; aiohttp: 15 s)
None Keep idle connections indefinitely (httpx only; aiohttp warns)
float >= 0 Explicit lifetime in seconds
# Keep idle connections for 60 seconds
options = ClientOptions(keep_alive_timeout=60.0)

# Indefinite (httpx only)
options = ClientOptions(keep_alive_timeout=None)

buffer_size

HTTP-response read buffer size in bytes — how much response data aiohttp buffers before it pauses reading from the socket. It does not limit the length of a streamed line: watch() and logs.stream() accept lines of any size. Uses the three-state sentinel pattern:

Value Meaning
... (the default) Kubex default of 2**21 bytes (2 MiB)
None aiohttp's own default (version-dependent: 64 KiB in aiohttp 3.11, 256 KiB in 3.14)
int > 0 Explicit buffer size in bytes
# Use a 1 MiB read buffer
options = ClientOptions(buffer_size=1024 * 1024)

# Use aiohttp's own default
options = ClientOptions(buffer_size=None)

httpx asymmetry

httpx has no equivalent buffer-size knob. Setting buffer_size to anything other than ... on an httpx-backed client emits a UserWarning and is otherwise ignored.

ws_max_message_size

Maximum WebSocket frame size in bytes for exec, attach, and portforward. Uses the three-state sentinel pattern:

Value Meaning
... (the default) Kubex default of 2**21 bytes (preserves current behavior on both backends)
None No cap on aiohttp (passes 0 on the wire); not supported on httpx — falls back to the httpx-ws default of 65536 bytes, with a UserWarning
int > 0 Explicit cap in bytes
# Allow frames up to 8 MiB (for large exec output)
options = ClientOptions(ws_max_message_size=8 * 1024 * 1024)

# No cap on aiohttp; on httpx this instead falls back to a 64 KiB cap (see above)
options = ClientOptions(ws_max_message_size=None)

pool_size

Total connection pool size (all hosts combined). Uses the three-state sentinel pattern:

Value Meaning
... (the default) Library default (both backends: 100)
None Unlimited
int > 0 Explicit connection limit
# Reduce pool to 10 connections total
options = ClientOptions(pool_size=10)

# Unlimited
options = ClientOptions(pool_size=None)

Long-lived streams hold a pool connection

Every open watch, log stream, exec/attach/portforward session and each connection accepted by api.portforward.listen() holds one connection from this pool for as long as it stays open. With the default of 100, an application that keeps many streams open at once can starve its own regular requests (they wait for a free connection) — raise pool_size accordingly.

pool_size_per_host

Per-host connection pool size. Uses the three-state sentinel pattern:

Value Meaning
... (the default) Library default (aiohttp: 0, meaning no per-host limit)
None Unlimited
int > 0 Explicit per-host limit
# Limit to 5 connections per host
options = ClientOptions(pool_size_per_host=5)

httpx asymmetry

httpx has no per-host pool limit. Setting pool_size_per_host to anything other than ... on an httpx-backed client emits a UserWarning and is otherwise ignored.

trust_env

When True, kubex enables environment-driven proxy configuration on both backends:

Env var Effect
HTTP_PROXY / HTTPS_PROXY / ALL_PROXY Select an outbound proxy for matching requests
NO_PROXY Exclude specific hosts from proxy routing
~/.netrc (or $NETRC) Supply basic-auth credentials for the proxy host when the env proxy URL carries no embedded user:pass

The default is False. This matches aiohttp's library default and overrides httpx's library default of True, making behavior symmetric across both backends out of the box.

# Read HTTPS_PROXY / HTTP_PROXY / NO_PROXY from the environment
options = ClientOptions(trust_env=True)

Canonical flow — HTTPS proxy with netrc credentials:

export HTTPS_PROXY=http://proxy.corp.example.com:3128
# ~/.netrc
# machine proxy.corp.example.com
#   login alice
#   password s3cret
options = ClientOptions(trust_env=True)
# Kubex reads HTTPS_PROXY, finds no user:pass in the URL, looks up
# proxy.corp.example.com in ~/.netrc, and configures the proxy with
# auth=("alice", "s3cret").
async with await create_client(options=options) as client:
    ...

Netrc scope: netrc is used for proxy credentials only. Kubex's per-request Authorization: Bearer … header always takes priority over any netrc-derived target-host Basic auth.

Conflict with options.proxy: when both trust_env=True and options.proxy are set, the explicit options.proxy wins and a UserWarning is emitted at client construction. The effective behavior is trust_env=False passed to the backend, so NO_PROXY cannot silently exempt the explicit proxy.

Backend asymmetries

  • WS_PROXY / WSS_PROXY env vars are read by aiohttp only. On httpx, wss:// upgrades use HTTPS_PROXY.
  • SSL_CERT_FILE / SSL_CERT_DIR env vars are read by httpx only (when no custom CA is configured). aiohttp does not read these.

Snapshot semantics (httpx only)

When a proxy env var (HTTPS_PROXY, HTTP_PROXY, ALL_PROXY, etc.) is set at construction time, Kubex's proxy-netrc resolver materializes it into an explicit httpx.Proxy(auth=...). Mutations to those env vars after create_client() returns are not reflected on the httpx backend.

When no proxy env var is set at construction time, httpx receives trust_env=True directly and re-reads env vars on every request (httpx's own default behavior).

The aiohttp backend always retains its native per-request env lookup regardless of whether a proxy was found at construction.

If snapshot behavior is unwanted on httpx, use the Custom underlying HTTP client escape hatch.

Backend asymmetries

Some ClientOptions fields behave differently (or are unsupported) depending on which HTTP backend is in use. A UserWarning is emitted on first use when a field has no effect.

Field httpx aiohttp
proxy=str proxy=str on AsyncClient proxy=str on ClientSession
proxy=dict All entries applied via mounts= Only the entry matching the API server's URL scheme is used; other entries are dropped with a warning
keep_alive=False Limits(max_keepalive_connections=0) TCPConnector(force_close=True)
keep_alive_timeout Limits(keepalive_expiry=float\|None) TCPConnector(keepalive_timeout=float) — None is unsupported; warning emitted
buffer_size Ignored — warning emitted ClientSession(read_bufsize=int)
ws_max_message_size aconnect_ws(max_message_size_bytes=int) — None is unsupported: falls back to the httpx-ws default (65536 bytes), warning emitted ws_connect(max_msg_size=int) — None maps to 0 (no cap)
pool_size Limits(max_connections=int\|None) TCPConnector(limit=int) — None maps to 0 (unlimited)
pool_size_per_host Ignored — warning emitted TCPConnector(limit_per_host=int) — None maps to 0 (unlimited)
trust_env=True If a proxy env var is found at construction, it is materialized into httpx.Proxy(auth=...) (snapshot). If none is found, httpx receives trust_env=True and re-reads env vars per-request. Env vars read per-request (aiohttp native behavior)

Cross-reference: see Timeouts for the note on Timeout.write and Timeout.pool being httpx-only fields.

Connection behavior

These apply to both backends:

  • Errors are backend-independent. Connection failures (refused, DNS, TLS, the connection dropping mid-response) raise KubexConnectionError; client-side timeouts raise KubexTimeoutError (also a TimeoutError). The original httpx/aiohttp exception is kept as __cause__. See Exceptions.
  • TLS material is loaded once. The CA bundle and client certificate/key files are read when the client is constructed. If they are rotated on disk (e.g. a renewed client certificate), create a new client to pick them up.
  • tls-server-name from the kubeconfig (ClientConfiguration(tls_server_name=...)) is used both as the SNI name and as the name the server certificate is verified against, for REST and WebSocket connections. Exception: the httpx backend cannot apply it through an HTTP proxy (explicit proxy or one picked up with trust_env=True) and warns at construction; certificate verification then uses the host in the server URL. Use aiohttp for that setup.
  • Impersonation headers (Impersonate-User, Impersonate-Group, …) from the configuration are sent on every REST and WebSocket request; repeated headers such as several Impersonate-Group values are preserved.
  • WebSocket keepalive. exec, attach and portforward connections send a ping every 20 seconds so idle connections survive load balancers. No pong deadline is enforced: a consumer that pauses reading (backpressure) is not disconnected.

AioHttpClient

Requires: pip install "kubex[aiohttp]"

aiohttp has WebSocket support built in — no extra package needed for exec, attach, and portforward. Supports asyncio only.

HttpxClient

Requires: pip install "kubex[httpx]"

WebSocket support requires the extra httpx-ws package: pip install "kubex[httpx-ws]" (includes httpx).

The httpx client is the only client that supports trio (in addition to asyncio).

Client selection trade-offs

Feature aiohttp httpx
asyncio support yes yes
trio support no yes
WebSocket (exec/attach/portforward) built-in requires httpx-ws extra
Auto-detection priority 1st 2nd

For detailed guidance on choosing a client and runtime, see Clients & Runtimes.

Custom underlying HTTP client

Kubex prioritizes predictable defaults over knob coverage. When the built-in ClientOptions wiring is insufficient — for example, you need target-host Basic auth from netrc, a custom retry policy, a custom TLS transport, or per-request env reads on httpx — you can subclass HttpxClient or AioHttpClient and override _create_inner_client().

When this escape hatch is used, the subclass is fully responsible for mapping all options and configuration. ClientOptions proxy, timeout, pool, and trust_env fields are no longer applied automatically.

httpx example:

import httpx
from kubex.client.httpx import HttpxClient
from kubex.client import ClientOptions


class MyHttpxClient(HttpxClient):
    def _create_inner_client(self) -> httpx.AsyncClient:
        # Full control: target-host NetRCAuth, custom transport, retry policy, etc.
        return httpx.AsyncClient(
            base_url=str(self.configuration.base_url),
            verify=True,
            trust_env=True,  # per-request env reads
            auth=httpx.NetRCAuth(file=None),  # target-host Basic auth from netrc
        )


client = MyHttpxClient(configuration=cfg, options=ClientOptions())

aiohttp example:

import ssl
import aiohttp
from kubex.client.aiohttp import AioHttpClient
from kubex.client import ClientOptions


class MyAioHttpClient(AioHttpClient):
    def _create_inner_client(self) -> aiohttp.ClientSession:
        ssl_context = ssl.create_default_context()
        connector = aiohttp.TCPConnector(ssl=ssl_context)
        return aiohttp.ClientSession(
            base_url=str(self.configuration.base_url),
            connector=connector,
            trust_env=True,
        )


client = MyAioHttpClient(configuration=cfg, options=ClientOptions())

Warning

Overriding _create_inner_client() means ClientOptions proxy, timeout, pool, and trust_env fields are no longer applied automatically. The subclass is fully responsible for all HTTP client configuration.