Process-level HTTP settings live in ServerConfig and nested RequestPolicy.
serve() reads STARIO_* variables through ServerConfig.from_env() when no config is passed. Stario does not load dotenv files — set variables in the shell, the process manager, or your own bootstrap.
For embedding, pass a ServerConfig to serve(bootstrap, config=…) or Server. Handlers do not read this object directly; limits apply at the protocol layer on each connection.
ServerConfig#
ServerConfig groups listen address, Unix socket options, graceful shutdown window, socket backlog, TCP SO_REUSEADDR, a live-connection cap, nested request policy, and live-response compression defaults.
When unix_socket is set, host, port, and reuse_addr are ignored; backlog and unix_socket_mode apply to the Unix listener.
max_connections caps live connections process-wide, split evenly across worker loops. Over the cap, a cleartext request gets 503 and a TLS connection is closed. The default is unset — no cap.
graceful_shutdown_timeout is the phase-one drain window: in-flight handlers and connections from app.create_task may finish before force-close. Shutdown may continue up to about one additional second in a force-close loop before orphaned tasks are cancelled.
class ServerConfig(*, host='127.0.0.1', port=8000, unix_socket=None, unix_socket_mode=432, requests=None, compression=None, graceful_shutdown_timeout=5.0, backlog=2048, reuse_addr=True, threads=1, pin_threads=False, max_connections=None, ssl=None, ssl_certfile=None, ssl_keyfile=None)#
Listen address, transport policy, request policy, compression, and shutdown.
When unix_socket is set, host, port, and reuse_addr are ignored; only backlog and unix_socket_mode apply to the Unix listener.
Nested requests and compression objects are stored by reference; treat as immutable after construction.
graceful_shutdown_timeout is the phase-1 drain window (handlers finish, idle connections close). Shutdown may continue up to one additional second in a force-close loop before orphaned tasks are cancelled (see Server._drain_listener).
ServerConfig.from_env(cls)#
Read STARIO_* listen, limit, compression, and shutdown settings.
server_config_from_env()#
Read STARIO_* listen, limit, compression, and shutdown settings.
RequestPolicy#
RequestPolicy caps header size, sets the default body limit for one read, limits HTTP pipelining depth, and sets read timeouts for headers, body chunks, and keep-alive idle time. Header and body sizes and the body read timeout default in stario.http.request (64 KiB headers, 10 MiB body, 30 s idle between body chunks). Header read timeout, keep-alive idle timeout, and pipelining depth default in stario.http.config (5 s, 5 s, 8).
max_body_bytes is the default limit for one body() or stream() read, not a cap for the server. A handler passes max_size= to accept more or less on one read: await c.req.body(max_size=6_000_000) on an upload route. The read raises RequestBodyError(413) past the limit. The server answers 413 before the handler only for a Content-Length above 2^31-1 bytes. See Body rules.
class RequestPolicy(*, max_header_bytes=65536, max_body_bytes=10485760, header_timeout=5.0, body_timeout=30.0, keep_alive_timeout=5.0, max_pipelined_requests=8, write_timeout=30.0)#
Header size cap, body read default, pipelining limit, and timeouts.
max_body_bytes is the default limit for body() and stream(). A handler passes max_size= to accept more or less on one read.
request_policy_from_env()#
Read STARIO_REQUESTS_* header cap, body read default, and timeouts.
Compression for live responses#
ServerConfig.compression is a CompressionConfig used by Writer on dynamic responses. Negotiation honors Accept-Encoding quality values; among ties Stario prefers zstd, then brotli, then gzip — zstd costs less CPU on live responses at near-equal wire size. Set CompressionConfig(preference=…) or STARIO_COMPRESS_PREFERENCE to change the tie-break order; Assets/Files replies keep brotli first regardless, since precompressed bytes cost no request CPU. Bodies below min_size or incompressible types may skip compression on whole-body (respond) paths; chunked streams negotiate without a minimum body size.
CompressionConfig(stream_window_log=…) gives streamed write() responses a smaller encoder — a small window and capped match tables instead of the one-shot footprint. The default None gives streams the one-shot settings; a value such as 16 cuts an idle stream to a fraction of that memory, but a repaint larger than the window can no longer reference the previous patch, so whole-document repaints send far more bytes. respond() output never depends on it.
Assets and Files use a separate pre-compression profile from live Writer responses — see Files and assets.
class CompressionConfig(*, min_size=512, zstd_level=3, zstd_window_log=None, brotli_level=4, brotli_window_log=None, gzip_level=1, gzip_window_bits=None, stream_window_log=None, preference=None)#
Policy for picking br / zstd / gzip from Accept-Encoding and for streaming vs whole-body responses.
Configure only via the constructor; fields are not meant for post-init mutation.
stream_window_log is the window bound for streamed (write()) responses only — a live stream’s encoder then holds a small window and capped match tables (brotli also drops to quality 3) instead of the one-shot memory footprint. None (the default) gives streams the one-shot settings exactly; respond() output never depends on it. A small window saves memory per idle stream but loses the previous repaint once a document outgrows it, so whole-document SSE repaints send far more bytes and cost more encode CPU.
CompressionConfig.enabled_encodings()#
Enabled encodings in preference order.
Environment variables#
Export variables before starting the process, or construct ServerConfig and RequestPolicy and pass them to serve().
Listen and shutdown#
| Variable | Default | Purpose |
|---|---|---|
STARIO_HOST | 127.0.0.1 | TCP bind address (ignored when a Unix socket is set). |
STARIO_PORT | 8000 | TCP port. |
STARIO_UNIX_SOCKET | (empty) | Unix socket path; empty means TCP. |
STARIO_UNIX_SOCKET_MODE | 660 (octal) | File mode applied after bind on a Unix socket. |
STARIO_BACKLOG | 2048 | Socket listen backlog. |
STARIO_REUSE_ADDR | 1 | TCP SO_REUSEADDR (0 disables). |
STARIO_GRACEFUL_SHUTDOWN_TIMEOUT | 5 | Graceful drain window in seconds. |
STARIO_THREADS | 1 | Worker loops with SO_REUSEPORT. >1 requires free-threaded Python 3.14t, or STARIO_THREADS_ALLOW_GIL=1. |
STARIO_PIN_THREADS | 0 | Pin each worker thread to one CPU (1 enables). |
STARIO_SSL_CERTFILE | (empty) | TLS certificate chain; serve HTTPS when set. |
STARIO_SSL_KEYFILE | (empty) | TLS private key; required with STARIO_SSL_CERTFILE. |
STARIO_MAX_CONNECTIONS | (unset) | Live-connection cap; over it, cleartext gets 503 and TLS is closed. |
Request limits#
| Variable | Default | Purpose |
|---|---|---|
STARIO_REQUESTS_MAX_HEADER_BYTES | 65536 | Total request-line and header bytes before 431. |
STARIO_REQUESTS_MAX_BODY_BYTES | 10485760 | Default limit for one body read; max_size= on the read overrides it (413 when exceeded). |
STARIO_REQUESTS_HEADER_TIMEOUT | 5 | Seconds to receive the request line and headers. |
STARIO_REQUESTS_BODY_TIMEOUT | 30 | Seconds of idle time between body chunks. |
STARIO_REQUESTS_KEEP_ALIVE_TIMEOUT | 5 | Idle time before closing a keep-alive connection. |
STARIO_REQUESTS_MAX_PIPELINED_REQUESTS | 8 | Maximum queued pipelined requests per connection (excludes the in-flight handler; default allows 1 active + 8 queued). |
STARIO_REQUESTS_WRITE_TIMEOUT | 30 | Seconds a response write may stall before the connection closes. |
HTTP/1.1 connections#
RequestPolicy applies per TCP or Unix connection. The protocol may queue multiple requests on one socket (pipelining) but runs one handler at a time until the current response finishes — after writer.respond(), writer.end(), or equivalent completion.
While a response is in progress, additional pipelined requests wait in a queue on that connection.
When the queue exceeds
max_pipelined_requests, the server responds with429and closes the connection.keep_alive_timeoutcloses idle connections with no in-flight work.header_timeoutandbody_timeoutbound slow clients during read phases.
Each connection shares one client-disconnect future across pipelined exchanges — c.alive() and c.disconnect reflect the peer, not a single request in isolation. Handlers and the writer shows how this interacts with long-lived streams.
Compression variables#
| Variable | Default | Purpose |
|---|---|---|
STARIO_COMPRESS_MIN_SIZE | 512 | Minimum body size to attempt compression. |
STARIO_COMPRESS_ZSTD_LEVEL | 3 | zstd level (-1 disables zstd). |
STARIO_COMPRESS_ZSTD_WINDOW_LOG | (unset) | Optional zstd window log. |
STARIO_COMPRESS_BROTLI_LEVEL | 4 | Brotli level (-1 disables brotli). |
STARIO_COMPRESS_BROTLI_WINDOW_LOG | (unset) | Optional Brotli window log. |
STARIO_COMPRESS_GZIP_LEVEL | 1 | gzip level (-1 disables gzip). |
STARIO_COMPRESS_GZIP_WINDOW_BITS | (unset) | Optional gzip window bits. |
STARIO_COMPRESS_PREFERENCE | zstd,br,gzip | Tie-break order for equally weighted encodings, comma-separated. Unlisted names fill in the default order. |
STARIO_COMPRESS_STREAM_WINDOW_LOG | (unset) | Window bound for streamed responses; unset gives streams the one-shot settings. |
Telemetry#
Tracer selection and sink tuning use STARIO_TRACER and STARIO_TRACERS_* variables. See Telemetry.
Examples#
STARIO_PORT=9000 STARIO_TRACER=json python -m app.mainSTARIO_PORT=8001 uv run watchfiles 'python -m app.main' app/