Configuration

STARIO_* variables configure the server, not the app.

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#

VariableDefaultPurpose
STARIO_HOST127.0.0.1TCP bind address (ignored when a Unix socket is set).
STARIO_PORT8000TCP port.
STARIO_UNIX_SOCKET(empty)Unix socket path; empty means TCP.
STARIO_UNIX_SOCKET_MODE660 (octal)File mode applied after bind on a Unix socket.
STARIO_BACKLOG2048Socket listen backlog.
STARIO_REUSE_ADDR1TCP SO_REUSEADDR (0 disables).
STARIO_GRACEFUL_SHUTDOWN_TIMEOUT5Graceful drain window in seconds.
STARIO_THREADS1Worker loops with SO_REUSEPORT. >1 requires free-threaded Python 3.14t, or STARIO_THREADS_ALLOW_GIL=1.
STARIO_PIN_THREADS0Pin 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#

VariableDefaultPurpose
STARIO_REQUESTS_MAX_HEADER_BYTES65536Total request-line and header bytes before 431.
STARIO_REQUESTS_MAX_BODY_BYTES10485760Default limit for one body read; max_size= on the read overrides it (413 when exceeded).
STARIO_REQUESTS_HEADER_TIMEOUT5Seconds to receive the request line and headers.
STARIO_REQUESTS_BODY_TIMEOUT30Seconds of idle time between body chunks.
STARIO_REQUESTS_KEEP_ALIVE_TIMEOUT5Idle time before closing a keep-alive connection.
STARIO_REQUESTS_MAX_PIPELINED_REQUESTS8Maximum queued pipelined requests per connection (excludes the in-flight handler; default allows 1 active + 8 queued).
STARIO_REQUESTS_WRITE_TIMEOUT30Seconds 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 with 429 and closes the connection.

  • keep_alive_timeout closes idle connections with no in-flight work.

  • header_timeout and body_timeout bound 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#

VariableDefaultPurpose
STARIO_COMPRESS_MIN_SIZE512Minimum body size to attempt compression.
STARIO_COMPRESS_ZSTD_LEVEL3zstd level (-1 disables zstd).
STARIO_COMPRESS_ZSTD_WINDOW_LOG(unset)Optional zstd window log.
STARIO_COMPRESS_BROTLI_LEVEL4Brotli level (-1 disables brotli).
STARIO_COMPRESS_BROTLI_WINDOW_LOG(unset)Optional Brotli window log.
STARIO_COMPRESS_GZIP_LEVEL1gzip level (-1 disables gzip).
STARIO_COMPRESS_GZIP_WINDOW_BITS(unset)Optional gzip window bits.
STARIO_COMPRESS_PREFERENCEzstd,br,gzipTie-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.main
STARIO_PORT=8001 uv run watchfiles 'python -m app.main' app/