Files
responder/deployment.html
T
2026-04-12 22:12:14 +00:00

278 lines
19 KiB
HTML
Raw 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 3.6.2 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=27fed22d" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/design-elements.e5416f61bae5d36adc6d722a2b6f8cff.css?v=452a8e97" />
<script src="_static/documentation_options.js?v=c0c9fa11"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=fd10adb8"></script>
<script>
</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="Feature Tour" href="tour.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>
</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.
Heres 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="n">responder</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, 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 youre 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="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. Dont 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>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>
<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="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">SECRET_KEY</span><span class="o">=</span><span class="n">dev</span><span class="o">-</span><span class="n">secret</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>Responders <code class="docutils literal notranslate"><span class="pre">TrustedHostMiddleware</span></code> and <code class="docutils literal notranslate"><span class="pre">HTTPSRedirectMiddleware</span></code> work
correctly behind proxies that set standard forwarding headers
(<code class="docutils literal notranslate"><span class="pre">X-Forwarded-For</span></code>, <code class="docutils literal notranslate"><span class="pre">X-Forwarded-Proto</span></code>).</p>
<p>That said, uvicorn is production-ready on its own. Many applications run
uvicorn 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 secret key</strong><code class="docutils literal notranslate"><span class="pre">SECRET_KEY</span></code> env var, never the default</p></li>
<li><p><strong>Disable debug mode</strong><code class="docutils literal notranslate"><span class="pre">DEBUG=false</span></code> or omit it entirely</p></li>
<li><p><strong>Set allowed hosts</strong> — restrict to your actual domain names</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</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 uvicorns <code class="docutils literal notranslate"><span class="pre">--ssl-*</span></code> flags</p></li>
<li><p><strong>Set up logging</strong> — uvicorn 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>v3.6.2</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>
<div>
<h3><a href="index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Deployment</a><ul>
<li><a class="reference internal" href="#running-locally">Running Locally</a></li>
<li><a class="reference internal" href="#docker">Docker</a></li>
<li><a class="reference internal" href="#cloud-platforms">Cloud Platforms</a></li>
<li><a class="reference internal" href="#health-check-endpoint">Health Check Endpoint</a></li>
<li><a class="reference internal" href="#uvicorn-directly">Uvicorn Directly</a></li>
<li><a class="reference internal" href="#docker-compose">Docker Compose</a></li>
<li><a class="reference internal" href="#reverse-proxy">Reverse Proxy</a></li>
<li><a class="reference internal" href="#production-checklist">Production Checklist</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>
</body>
</html>