Files
responder/deployment.html
2026-07-17 21:00:53 +00:00

414 lines
31 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en" data-content_root="./">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Deployment &#8212; responder 9.2.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=5ecbeea2" />
<link rel="stylesheet" type="text/css" href="_static/basic.css?v=b08954a9" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css?v=eb4102c5" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/sphinx-design.min.css?v=95c83b7e" />
<link rel="stylesheet" type="text/css" href="_static/design-elements.e5416f61bae5d36adc6d722a2b6f8cff.css?v=452a8e97" />
<script src="_static/documentation_options.js?v=e8b940be"></script>
<script src="_static/doctools.js?v=fd6eb6e6"></script>
<script src="_static/sphinx_highlight.js?v=6ffebe34"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=fd10adb8"></script>
<script>
</script>
<script src="_static/design-tabs.js?v=f930bc37"></script>
<script src="_static/design-elements.bbdccc18c4abea9397628f9fea3d48c2.js?v=03c7770e"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Testing" href="testing.html" />
<link rel="prev" title="Generated Clients" href="clientgen.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="deployment">
<h1>Deployment<a class="headerlink" href="#deployment" title="Link to this heading"></a></h1>
<p>Responder applications are standard <a class="reference external" href="https://asgi.readthedocs.io/">ASGI</a>
apps. ASGI (Asynchronous Server Gateway Interface) is the modern successor
to WSGI — it supports async, WebSockets, and HTTP/2. This means you can
deploy a Responder app anywhere that runs Python, using any ASGI server.</p>
<section id="running-locally">
<h2>Running Locally<a class="headerlink" href="#running-locally" title="Link to this heading"></a></h2>
<p>During development, <code class="docutils literal notranslate"><span class="pre">api.run()</span></code> is all you need:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
<span class="n">api</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
</div>
<p>This starts a <a class="reference external" href="https://www.uvicorn.org/">uvicorn</a> server on
<code class="docutils literal notranslate"><span class="pre">127.0.0.1:5042</span></code>. Uvicorn is a lightning-fast ASGI server built on
<a class="reference external" href="https://uvloop.readthedocs.io/">uvloop</a> — it handles thousands of
concurrent connections efficiently and protects against slowloris attacks,
making a reverse proxy like nginx optional for many deployments.</p>
<p>Responder can also run the current app with Granian when the optional server
extra is installed:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ uv pip install &#39;responder[orjson,server]&#39;
if __name__ == &quot;__main__&quot;:
api.run(server=&quot;granian&quot;)
</pre></div>
</div>
<p>If the extra is missing, Responder raises an error with the install command.
This embedded Granian path is single-process; use Granian directly for
multi-worker production deployments.</p>
</section>
<section id="docker">
<h2>Docker<a class="headerlink" href="#docker" title="Link to this heading"></a></h2>
<p>Docker is the most common way to package and deploy web applications.
Here’s a minimal Dockerfile:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">FROM</span> <span class="n">python</span><span class="p">:</span><span class="mf">3.13</span><span class="o">-</span><span class="n">slim</span>
<span class="n">WORKDIR</span> <span class="o">/</span><span class="n">app</span>
<span class="n">COPY</span> <span class="o">--</span><span class="n">from</span><span class="o">=</span><span class="n">ghcr</span><span class="o">.</span><span class="n">io</span><span class="o">/</span><span class="n">astral</span><span class="o">-</span><span class="n">sh</span><span class="o">/</span><span class="n">uv</span><span class="p">:</span><span class="n">latest</span> <span class="o">/</span><span class="n">uv</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">local</span><span class="o">/</span><span class="nb">bin</span><span class="o">/</span><span class="n">uv</span>
<span class="n">COPY</span> <span class="o">.</span> <span class="o">.</span>
<span class="n">RUN</span> <span class="n">uv</span> <span class="n">pip</span> <span class="n">install</span> <span class="o">--</span><span class="n">system</span> <span class="s1">&#39;responder[orjson]&#39;</span>
<span class="n">ENV</span> <span class="n">PORT</span><span class="o">=</span><span class="mi">80</span>
<span class="n">EXPOSE</span> <span class="mi">80</span>
<span class="n">CMD</span> <span class="p">[</span><span class="s2">&quot;python&quot;</span><span class="p">,</span> <span class="s2">&quot;api.py&quot;</span><span class="p">]</span>
</pre></div>
</div>
<p>Build and run:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ docker build -t myapi .
$ docker run -p 8000:80 myapi
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">python:3.13-slim</span></code> image is about 150MB — small enough for fast
deploys but includes everything you need. Using <code class="docutils literal notranslate"><span class="pre">uv</span></code> for installs
is significantly faster than pip. For even smaller images, you can use
<code class="docutils literal notranslate"><span class="pre">python:3.13-alpine</span></code>, though some packages may need extra build
dependencies.</p>
</section>
<section id="cloud-platforms">
<h2>Cloud Platforms<a class="headerlink" href="#cloud-platforms" title="Link to this heading"></a></h2>
<p>Responder automatically honors the <code class="docutils literal notranslate"><span class="pre">PORT</span></code> environment variable. When
<code class="docutils literal notranslate"><span class="pre">PORT</span></code> is set and you do not pass an explicit <code class="docutils literal notranslate"><span class="pre">port=</span></code>, the server binds
to <code class="docutils literal notranslate"><span class="pre">0.0.0.0</span></code> on that port — this is the convention that virtually every
cloud platform uses.</p>
<p>This means zero configuration on:</p>
<ul class="simple">
<li><p><strong>Fly.io</strong> — <code class="docutils literal notranslate"><span class="pre">fly</span> <span class="pre">launch</span></code> and you’re done</p></li>
<li><p><strong>Railway</strong> — push your code, Railway sets <code class="docutils literal notranslate"><span class="pre">PORT</span></code></p></li>
<li><p><strong>Render</strong> — set start command to <code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">api.py</span></code></p></li>
<li><p><strong>Google Cloud Run</strong> — containerize and deploy</p></li>
<li><p><strong>Azure Container Apps</strong> — same pattern</p></li>
<li><p><strong>AWS App Runner</strong> — and here too</p></li>
</ul>
<p>The pattern is always the same: deploy your code, set the start command
to <code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">api.py</span></code>, and the platform handles the rest.</p>
</section>
<section id="health-check-endpoint">
<h2>Health Check Endpoint<a class="headerlink" href="#health-check-endpoint" title="Link to this heading"></a></h2>
<p>Every production deployment needs a health check — a lightweight endpoint
that monitoring tools, load balancers, and orchestrators can poll to verify
your service is running:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/health&quot;</span><span class="p">,</span> <span class="n">include_in_schema</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">health</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;status&quot;</span><span class="p">:</span> <span class="s2">&quot;healthy&quot;</span><span class="p">}</span>
</pre></div>
</div>
<p>Keep it simple. Don’t query the database or do expensive work — the health
check should return instantly. Cloud platforms, Docker, and Kubernetes all
look for an HTTP 200 to confirm your service is alive.</p>
<p>Responder generates your <a class="reference internal" href="tour.html"><span class="doc">OpenAPI schema</span></a> from route signatures, so
every route is documented automatically. <code class="docutils literal notranslate"><span class="pre">include_in_schema=False</span></code> keeps this
internal endpoint out of the public spec.</p>
<p>For Docker, add a <code class="docutils literal notranslate"><span class="pre">HEALTHCHECK</span></code> instruction:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">HEALTHCHECK</span> <span class="o">--</span><span class="n">interval</span><span class="o">=</span><span class="mi">30</span><span class="n">s</span> <span class="o">--</span><span class="n">timeout</span><span class="o">=</span><span class="mi">3</span><span class="n">s</span> \
<span class="n">CMD</span> <span class="n">curl</span> <span class="o">-</span><span class="n">f</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">localhost</span><span class="o">/</span><span class="n">health</span> <span class="o">||</span> <span class="n">exit</span> <span class="mi">1</span>
</pre></div>
</div>
</section>
<section id="uvicorn-directly">
<h2>Uvicorn Directly<a class="headerlink" href="#uvicorn-directly" title="Link to this heading"></a></h2>
<p>For production deployments where you want more control, bypass
<code class="docutils literal notranslate"><span class="pre">api.run()</span></code> and use uvicorn directly:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ uvicorn api:api --host 0.0.0.0 --port 8000 --workers 4
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">--workers</span></code> flag spawns multiple processes, each handling requests
independently. A good starting point is 2-4 workers per CPU core.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>Each worker is a separate process. With the default <code class="docutils literal notranslate"><span class="pre">sessions=&quot;auto&quot;</span></code> and
no signing key set, every worker mints its <strong>own</strong> random key, so signed
session cookies won’t validate across workers and load-balanced users get
logged out. Before running more than one worker or instance, set a stable
<code class="docutils literal notranslate"><span class="pre">RESPONDER_SECRET_KEY</span></code> (or <code class="docutils literal notranslate"><span class="pre">API(secret_key=...)</span></code>). For sessions that
survive across separate machines, store them server-side with a shared
backend such as <code class="docutils literal notranslate"><span class="pre">AsyncRedisSessionBackend</span></code>. See the
<a class="reference internal" href="guide-config.html"><span class="doc">configuration guide</span></a>.</p>
</div>
<p>Uvicorn supports many options — SSL certificates, access logging, graceful
shutdown timeouts, and more. See the
<a class="reference external" href="https://www.uvicorn.org/deployment/">uvicorn documentation</a> for details.</p>
<p>For platforms like Heroku or Railway that use a <code class="docutils literal notranslate"><span class="pre">Procfile</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>web: uvicorn api:api --host 0.0.0.0 --port $PORT --workers 4
</pre></div>
</div>
</section>
<section id="granian">
<h2>Granian<a class="headerlink" href="#granian" title="Link to this heading"></a></h2>
<p><a class="reference external" href="https://github.com/emmett-framework/granian">Granian</a> is a Rust-based HTTP
server that runs ASGI, WSGI, and RSGI apps from a single dependency. It’s a
strong production peer to uvicorn — native HTTP/2, WebSockets enabled by
default, and no separate worker package to install.</p>
<p>Install it:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ uv pip install &#39;responder[orjson,server]&#39;
</pre></div>
</div>
<p>Responder apps are ASGI, so run them with the <code class="docutils literal notranslate"><span class="pre">asgi</span></code> interface:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ granian --interface asgi api:api
</pre></div>
</div>
<p>The familiar host, port, and worker flags all apply:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ granian --interface asgi --host 0.0.0.0 --port 8000 --workers 4 api:api
</pre></div>
</div>
<p>To serve HTTP/2 in production, add <code class="docutils literal notranslate"><span class="pre">--http</span> <span class="pre">2</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ granian --interface asgi --host 0.0.0.0 --port 8000 --workers 4 --http 2 api:api
</pre></div>
</div>
<p>For a <code class="docutils literal notranslate"><span class="pre">Procfile</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>web: granian --interface asgi --host 0.0.0.0 --port $PORT --workers 4 api:api
</pre></div>
</div>
<p>Like uvicorn’s <code class="docutils literal notranslate"><span class="pre">--workers</span></code>, every Granian worker is a separate process, so the
stable-secret-key note above applies here too. <code class="docutils literal notranslate"><span class="pre">api.run(server=&quot;granian&quot;)</span></code>
uses Granian’s embedded server for the current app object; for workers, reload,
or process supervision, use the Granian CLI shown above.</p>
</section>
<section id="docker-compose">
<h2>Docker Compose<a class="headerlink" href="#docker-compose" title="Link to this heading"></a></h2>
<p>For local development with databases and other services, Docker Compose
ties everything together:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># docker-compose.yml</span>
<span class="n">services</span><span class="p">:</span>
<span class="n">api</span><span class="p">:</span>
<span class="n">build</span><span class="p">:</span> <span class="o">.</span>
<span class="n">ports</span><span class="p">:</span>
<span class="o">-</span> <span class="s2">&quot;5042:80&quot;</span>
<span class="n">environment</span><span class="p">:</span>
<span class="o">-</span> <span class="n">PORT</span><span class="o">=</span><span class="mi">80</span>
<span class="o">-</span> <span class="n">DATABASE_URL</span><span class="o">=</span><span class="n">postgresql</span><span class="o">+</span><span class="n">asyncpg</span><span class="p">:</span><span class="o">//</span><span class="n">user</span><span class="p">:</span><span class="k">pass</span><span class="nd">@db</span><span class="o">/</span><span class="n">myapp</span>
<span class="o">-</span> <span class="n">RESPONDER_SECRET_KEY</span><span class="o">=</span><span class="n">dev</span><span class="o">-</span><span class="n">only</span><span class="o">-</span><span class="ow">not</span><span class="o">-</span><span class="k">for</span><span class="o">-</span><span class="n">production</span><span class="o">-</span><span class="mi">32</span><span class="n">chars</span>
<span class="n">depends_on</span><span class="p">:</span>
<span class="o">-</span> <span class="n">db</span>
<span class="n">db</span><span class="p">:</span>
<span class="n">image</span><span class="p">:</span> <span class="n">docker</span><span class="o">.</span><span class="n">io</span><span class="o">/</span><span class="n">postgres</span><span class="p">:</span><span class="mi">16</span><span class="o">-</span><span class="n">alpine</span>
<span class="n">environment</span><span class="p">:</span>
<span class="n">POSTGRES_USER</span><span class="p">:</span> <span class="n">user</span>
<span class="n">POSTGRES_PASSWORD</span><span class="p">:</span> <span class="k">pass</span>
<span class="n">POSTGRES_DB</span><span class="p">:</span> <span class="n">myapp</span>
<span class="n">volumes</span><span class="p">:</span>
<span class="o">-</span> <span class="n">pgdata</span><span class="p">:</span><span class="o">/</span><span class="n">var</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">postgresql</span><span class="o">/</span><span class="n">data</span>
<span class="n">volumes</span><span class="p">:</span>
<span class="n">pgdata</span><span class="p">:</span>
</pre></div>
</div>
<p>Run with <code class="docutils literal notranslate"><span class="pre">docker</span> <span class="pre">compose</span> <span class="pre">up</span></code>. The API waits for <code class="docutils literal notranslate"><span class="pre">db</span></code> to start, then
connects using the <code class="docutils literal notranslate"><span class="pre">DATABASE_URL</span></code> environment variable.</p>
</section>
<section id="reverse-proxy">
<h2>Reverse Proxy<a class="headerlink" href="#reverse-proxy" title="Link to this heading"></a></h2>
<p>For high-traffic production deployments, you may want a reverse proxy like
<a class="reference external" href="https://nginx.org/">nginx</a> or <a class="reference external" href="https://caddyserver.com/">Caddy</a> in
front of your application for:</p>
<ul class="simple">
<li><p><strong>SSL/TLS termination</strong> — let the proxy handle HTTPS certificates</p></li>
<li><p><strong>Load balancing</strong> — distribute traffic across multiple app instances</p></li>
<li><p><strong>Static asset serving</strong> — offload static files to the proxy</p></li>
<li><p><strong>Rate limiting</strong> — at the infrastructure level</p></li>
</ul>
<p>A minimal Caddy config that handles HTTPS automatically:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Caddyfile</span>
<span class="n">example</span><span class="o">.</span><span class="n">com</span> <span class="p">{</span>
<span class="n">reverse_proxy</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">5042</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Behind a proxy, tell Responder to trust its forwarding headers:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">trust_proxy_headers</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
<p>Responder then honors RFC 7239 <code class="docutils literal notranslate"><span class="pre">Forwarded</span></code> (or <code class="docutils literal notranslate"><span class="pre">X-Forwarded-Proto</span></code> /
<code class="docutils literal notranslate"><span class="pre">X-Forwarded-Host</span></code> / <code class="docutils literal notranslate"><span class="pre">X-Forwarded-For</span></code>) and rewrites the request’s
scheme, host, and client address to what the original client sent — so
HTTPS detection, redirects, URL building, host validation, and logged or
rate-limited client IPs are all correct. Only enable it when every request
reaches Responder through a proxy you control that overwrites those headers;
otherwise clients can spoof them.</p>
<p>Behind a TLS-terminating proxy this is exactly right: in production
(<code class="docutils literal notranslate"><span class="pre">debug=False</span></code>) Responder marks the session cookie <code class="docutils literal notranslate"><span class="pre">Secure</span></code> by default, so
it only travels over HTTPS — no action needed. Only pass
<code class="docutils literal notranslate"><span class="pre">session_https_only=False</span></code> if you genuinely serve plain HTTP. (Browsers reject
<code class="docutils literal notranslate"><span class="pre">SameSite=None</span></code> without a Secure cookie, so Responder rejects that combination
too.)</p>
<p>That said, uvicorn and Granian are both production-ready on their own. Many
applications run the ASGI server directly without a reverse proxy and do just
fine.</p>
</section>
<section id="production-checklist">
<h2>Production Checklist<a class="headerlink" href="#production-checklist" title="Link to this heading"></a></h2>
<p>Before going live:</p>
<ul class="simple">
<li><p><strong>Set a stable secret key</strong> — pass <code class="docutils literal notranslate"><span class="pre">API(secret_key=...)</span></code> or set
<code class="docutils literal notranslate"><span class="pre">RESPONDER_SECRET_KEY</span></code> (16+ chars; generate one with
<code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">-c</span> <span class="pre">&quot;import</span> <span class="pre">secrets;</span> <span class="pre">print(secrets.token_urlsafe(32))&quot;</span></code>). It must be
stable across workers and restarts, or signed sessions stop validating. If
your service is stateless and never touches <code class="docutils literal notranslate"><span class="pre">req.session</span></code>, pass
<code class="docutils literal notranslate"><span class="pre">API(sessions=False)</span></code> instead to skip sessions entirely. See the
<a class="reference internal" href="guide-config.html"><span class="doc">configuration guide</span></a>.</p></li>
<li><p><strong>Disable debug mode</strong> — it’s off by default; never set <code class="docutils literal notranslate"><span class="pre">debug=True</span></code> in production</p></li>
<li><p><strong>Set allowed hosts</strong> — <code class="docutils literal notranslate"><span class="pre">allowed_hosts=[...]</span></code>, restricted to your domains</p></li>
<li><p><strong>Behind a proxy? Trust its headers</strong> — <code class="docutils literal notranslate"><span class="pre">trust_proxy_headers=True</span></code> so
HTTPS detection, redirects, and client IPs reflect the original request
(only when <em>every</em> request comes through your proxy — see <a class="reference internal" href="#reverse-proxy">Reverse Proxy</a>)</p></li>
<li><p><strong>Serving browser forms? Enable CSRF protection</strong> — <code class="docutils literal notranslate"><span class="pre">csrf=True</span></code> pairs
with the default-on cookie sessions; exempt webhook routes with
<code class="docutils literal notranslate"><span class="pre">csrf=False</span></code>. Token-only APIs (no cookies) don’t need it.</p></li>
<li><p><strong>Check the request-body cap fits</strong> — bodies over <code class="docutils literal notranslate"><span class="pre">max_request_size</span></code>
(100 MiB by default) get a <code class="docutils literal notranslate"><span class="pre">413</span></code>; raise it for large uploads (multipart
streams to disk, so a bigger cap doesn’t mean more memory)</p></li>
<li><p><strong>Use multiple workers</strong> — <code class="docutils literal notranslate"><span class="pre">--workers</span> <span class="pre">4</span></code> or more, depending on CPU cores
(set a stable secret key first — see above)</p></li>
<li><p><strong>Add a health check</strong> — <code class="docutils literal notranslate"><span class="pre">/health</span></code> endpoint for monitoring</p></li>
<li><p><strong>Enable HTTPS</strong> — via your proxy, cloud platform, or your ASGI server’s
<code class="docutils literal notranslate"><span class="pre">--ssl-*</span></code> flags; the session cookie is then <code class="docutils literal notranslate"><span class="pre">Secure</span></code> automatically</p></li>
<li><p><strong>Set up logging</strong> — your ASGI server logs requests by default; pipe them to your log aggregator</p></li>
<li><p><strong>Pin your dependencies</strong> — use a lock file or pinned requirements for reproducible deploys</p></li>
</ul>
</section>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="Main">
<div class="sphinxsidebarwrapper"><p class="logo">
<a href="index.html">
<img class="logo" src="_static/responder.png" />
</a>
</p>
<p>
<strong>Responder</strong> — a familiar HTTP service framework for Python.
<br />
<small>v9.2.0</small>
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="https://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
<li><a href="https://pypi.org/project/responder/">Responder @ PyPI</a></li>
<li><a href="https://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
</ul><h3>Navigation</h3>
<p class="caption" role="heading"><span class="caption-text">User Guide</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quick Start</a></li>
<li class="toctree-l1"><a class="reference internal" href="tour.html">Feature Tour</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="routers.html">Composing Apps with Routers</a></li>
<li class="toctree-l1"><a class="reference internal" href="guide-config.html">Configuration</a></li>
<li class="toctree-l1"><a class="reference internal" href="clientgen.html">Generated Clients</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Deployment</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#running-locally">Running Locally</a></li>
<li class="toctree-l2"><a class="reference internal" href="#docker">Docker</a></li>
<li class="toctree-l2"><a class="reference internal" href="#cloud-platforms">Cloud Platforms</a></li>
<li class="toctree-l2"><a class="reference internal" href="#health-check-endpoint">Health Check Endpoint</a></li>
<li class="toctree-l2"><a class="reference internal" href="#uvicorn-directly">Uvicorn Directly</a></li>
<li class="toctree-l2"><a class="reference internal" href="#granian">Granian</a></li>
<li class="toctree-l2"><a class="reference internal" href="#docker-compose">Docker Compose</a></li>
<li class="toctree-l2"><a class="reference internal" href="#reverse-proxy">Reverse Proxy</a></li>
<li class="toctree-l2"><a class="reference internal" href="#production-checklist">Production Checklist</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="testing.html">Testing</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">Tutorials</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="tutorial-rest.html">Building a REST API</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-sqlalchemy.html">Using SQLAlchemy</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-auth.html">Authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-websockets.html">WebSocket Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-middleware.html">Writing Middleware</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-flask.html">Migrating from Flask</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">Reference</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="api.html">API Reference</a></li>
<li class="toctree-l1"><a class="reference internal" href="cli.html">Command Line Interface</a></li>
<li class="toctree-l1"><a class="reference internal" href="runtime-contracts.html">Runtime Contracts</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">Project</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="changes.html">Changelog</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration-v9.html">Migrating to v9</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration-v8.html">Migrating to v8</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration-v7.html">Migrating to v7</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration-v6.html">Migrating to v6</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration-v5.html">Migrating to v5</a></li>
<li class="toctree-l1"><a class="reference internal" href="sandbox.html">Sandbox</a></li>
<li class="toctree-l1"><a class="reference internal" href="backlog.html">Backlog</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
<li>Previous: <a href="clientgen.html" title="previous chapter">Generated Clients</a></li>
<li>Next: <a href="testing.html" title="next chapter">Testing</a></li>
</ul></li>
</ul>
</div>
<search id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</search>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&#169;2018-2026, Kenneth Reitz.
|
<a href="_sources/deployment.rst.txt"
rel="nofollow">Page source</a>
</div>
<a href="https://github.com/kennethreitz/responder" class="github">
<img src="_static/forkme_right_darkblue.png" alt="Fork me on GitHub" class="github"/>
</a>
</body>
</html>