Deployment#
Deploying with Docker#
Muffin has an official image, horneds/muffin: it carries Python, an ASGI server and Muffin, so a deployment only has to copy the application code.
Write app.py:
from muffin import Application
app = Application()
@app.route("/")
async def index(request):
return "Hello World!"
and a Dockerfile next to it:
FROM horneds/muffin:latest
COPY . /app
Build and run:
$ docker build -t myimage .
$ docker run -d --name mycontainer -p 80:80 myimage
Open http://localhost. Configuration still comes from the environment, so
MUFFIN_* variables (see Configuration) are the way to change behavior
per deployment.
Ways to run it#
Pick the server by what you need:
uvicorn app:app --reload: development, restarts on file changesmuffin app run: the same uvicorn, with the app’sLOG_CONFIGappliedgunicorn app:app -k uvicorn.workers.UvicornWorker -w 4: production, several worker processes in one container
Workers are separate processes with separate memory: application state, plugin
clients and in-memory caches are not shared between them. Anything that must be
shared (sessions, background jobs, rate limits) belongs in Redis or a database, not
in a module-level dict. MUFFIN_AIOLIB applies per process, so set it in the
container environment, not in code.
Safety#
A few settings matter more in production than anywhere else:
Host header: set
USE_HOSTS = Trueand passhosts=to the routes that are only valid for one domain; the router then rejects requests for unknown hosts instead of answering them. See Usage.Form size:
await request.form(max_size=...)raisesASGIRequestEntityTooLargeErrorinstead of buffering whatever arrives. Large uploads needupload_toor a size limit that matches reality.Proxies: behind a proxy, the client address and scheme come from the
X-Forwarded-*headers, which any client can forge. Trust them only from your proxy, and keep the proxy’s own limits (body size, timeouts) as the outer line of defense.Debug:
DEBUG = Trueleaks tracebacks and is never deployed. Set it from the environment so the default is safe.
Deploying behind NGINX#
A reverse proxy in front of the application buys three things: it filters malformed requests, it can run several Muffin workers on all CPU cores, and it serves static files faster than the application does.
http {
server {
listen 80;
client_max_body_size 4G;
server_name example.com;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://muffin;
}
location /static {
root /path/to/app/static;
}
}
upstream muffin {
# Unix sockets are faster and survive port collisions
server unix:/tmp/example_1.sock fail_timeout=0;
server unix:/tmp/example_2.sock fail_timeout=0;
server unix:/tmp/example_3.sock fail_timeout=0;
server unix:/tmp/example_4.sock fail_timeout=0;
# Alternatively, TCP sockets:
# server 127.0.0.1:8081 fail_timeout=0;
# server 127.0.0.1:8082 fail_timeout=0;
# server 127.0.0.1:8083 fail_timeout=0;
# server 127.0.0.1:8084 fail_timeout=0;
}
}
The config listens on port 80 for example.com, proxies requests to the Muffin
workers and serves /path/to/app/static itself. See the nginx proxy module
docs for the rest of
the directives; HAProxy and other proxies work the same way.