Waitress
A production-quality, pure-Python WSGI server with zero external dependencies, for Unix and Windows.
Repository Health
Technical Analysis
Waitress is a WSGI server that runs your Python web application in production without asking you to install anything beyond the standard library. It grew out of the Pylons/Pyramid ecosystem and has been maintained continuously since 2011, valued for the fact that it just works the same way on Linux, macOS, and Windows — a rare property among WSGI servers, most of which drop Windows support because they rely on POSIX-only primitives like fork() or select()-adjacent OS calls unavailable there.
Under the hood it pairs a select()/asyncore-style non-blocking network loop (vendored in-tree as wasyncore) with a configurable thread pool that actually executes application code, so slow clients holding open connections never block the app from serving other requests. It speaks HTTP/1.0 and HTTP/1.1, understands X-Forwarded-* and Forwarded proxy headers for deployments behind Nginx or a load balancer, and ships a waitress-serve command as well as a paste.server_runner entry point for Pyramid/Pylons-style .ini configuration.
Because it has no dependencies to audit, compile, or version-pin, teams reach for it when they want a WSGI server they can drop behind any framework — Flask, Django, Pyramid — and not think about again.
What You Get
- A WSGI server with zero third-party dependencies — only the Python standard library, so nothing to audit or version-pin in your deployment
- Genuine cross-platform support for Unix and Windows from one codebase, unlike most WSGI servers that assume POSIX-only primitives
- A tunable thread pool (
threads=) for application dispatch layered on a non-blockingselect()-based network loop, so slow clients don’t starve other requests - Built-in reverse-proxy awareness via
X-Forwarded-*and RFC 7239Forwardedheader handling for deployments behind Nginx, HAProxy, or a cloud load balancer - A
waitress-serveCLI plus apaste.server_runnerentry point for Pyramid/Pylons.ini-style configuration - Unix domain socket support and IPv6 support alongside standard TCP binding
Common Use Cases
- Serving a Flask, Django, or Pyramid application in production without adding Gunicorn/uWSGI’s C-extension or POSIX-only dependencies
- Running a Python web app on Windows in production, where fork-based servers like Gunicorn aren’t an option
- Sitting behind a reverse proxy (Nginx, an ALB, HAProxy) as the application-facing WSGI server, using its proxy-header support to get correct scheme/host/for values
- Deploying inside a minimal container image where every extra dependency is a supply-chain and image-size cost
- Local development and CI environments that want production-like serving behavior without installing platform-specific server packages
Under The Hood
Architecture
Waitress separates network I/O from application execution: server.py’s create_server() wires an HTTPChannel (in channel.py, subclassing the vendored wasyncore.dispatcher) into a select()-driven event loop, while a ThreadedTaskDispatcher (task.py) owns a configurable thread pool that actually calls into the WSGI application. Requests are parsed by a dedicated HTTPRequestParser (parser.py) built on rfc7230.py’s header-token grammar, buffered through OverflowableBuffer/ReadOnlyFileBasedBuffer (buffers.py) so large bodies spill to disk instead of memory, and proxy headers are normalized by a small middleware (proxy_headers.py) before reaching the app. Configuration flows through a single Adjustments object (adjustments.py) that validates and defaults every tunable, which every other module reads from — a clear single point of truth that keeps the rest of the code free of ad-hoc option parsing.
Tech Stack
Pure Python 3.9+ standard library only — no third-party runtime dependencies of any kind. The package layout follows the modern src/-layout convention with a pyproject.toml (setuptools backend) declaring waitress-serve as a console script and a paste.server_runner entry point for WSGI-config-file-style deployment. Optional extras (testing, docs) pull in pytest/coverage and Sphinx respectively, but these never reach installed deployments. CI runs via GitHub Actions across the supported CPython and PyPy matrix.
Code Quality
The tests/ directory mirrors the src/waitress/ module layout file-for-file (test_adjustments.py, test_channel.py, test_parser.py, test_server.py, test_wasyncore.py, etc.) plus a dedicated test_functional.py that spawns real server subprocesses and exercises them over live sockets — a level of end-to-end coverage well beyond typical unit mocking. .coveragerc and pytest’s --cov flag enforce coverage tracking, .flake8 and tox.ini enforce linting/formatting and multi-version testing (Python 3.9 through 3.14, plus PyPy) before merge, and GitHub Actions CI runs the full matrix on every change. Naming and error handling favor explicit, narrowly-scoped exceptions (e.g. ClientDisconnected, AppResolutionError) over broad catches.
API Design
The public surface is deliberately tiny: waitress.serve(app, **kw) for programmatic use, waitress-serve for the command line, and paste.server_runner for .ini-driven Pyramid/Pylons deployments — all three converge on the same Adjustments configuration object, so there’s exactly one place to learn the tunables (host, port, threads, unix_socket, url_scheme, proxy headers) regardless of entry point. --help on the CLI documents every option inline, and the same names work as keyword arguments to serve(), which keeps the learning curve low for anyone who has looked at one entry point.