7.3 KiB
Deploying Gunicorn
We strongly recommend running Gunicorn behind a proxy server.
Nginx configuration
Although many HTTP proxies exist, we recommend Nginx. When using the default synchronous workers you must ensure the proxy buffers slow clients; otherwise Gunicorn becomes vulnerable to denial-of-service attacks. Use Hey to verify proxy behaviour.
An example configuration for fast clients with Nginx (source):
--8<-- "examples/nginx.conf"
To support streaming requests/responses or patterns such as Comet, long polling, or WebSockets, disable proxy buffering and run Gunicorn with an async worker class:
location @proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://app_server;
}
To ignore aborted requests (for example, health checks that close connections
prematurely) enable
proxy_ignore_client_abort:
proxy_ignore_client_abort on;
!!! note
The default value for proxy_ignore_client_abort is off. If it remains off
Nginx logs will report error 499 and Gunicorn may log Ignoring EPIPE when the
log level is debug.
Pass protocol information to Gunicorn so applications can generate correct
URLs. Add this header to your location block:
proxy_set_header X-Forwarded-Proto $scheme;
If Nginx runs on a different host, tell Gunicorn which proxies are trusted so it
accepts the X-Forwarded-* headers:
gunicorn -w 3 --forwarded-allow-ips="10.170.3.217,10.170.3.220" test:app
When all traffic comes from trusted proxies (for example Heroku) you can set
--forwarded-allow-ips='*'. This is dangerous if untrusted clients can
reach Gunicorn directly, because forged headers could make your application
serve secure content over plain HTTP.
Gunicorn 19 changed the handling of REMOTE_ADDR to conform to
RFC 3875, meaning it now records the
proxy IP rather than the upstream client. To log the real client address, set
access_log_format to include X-Forwarded-For:
%({x-forwarded-for}i)s %(l.md)s %(u.md)s %(t.md)s "%(r.md)s" %(s.md)s %(b.md)s "%(f.md)s" "%(a.md)s"
When binding Gunicorn to a UNIX socket REMOTE_ADDR will be empty.
Using virtual environments
Install Gunicorn inside your project virtual environment to keep versions isolated:
mkdir ~/venvs/
virtualenv ~/venvs/webapp
source ~/venvs/webapp/bin/activate
pip install gunicorn
deactivate
Force installation into the active virtual environment with --ignore-installed:
source ~/venvs/webapp/bin/activate
pip install -I gunicorn
Monitoring
!!! note Do not enable Gunicorn's daemon mode when using process monitors. These supervisors expect to manage the direct child process.
Gaffer
Use Gaffer with gafferd to manage Gunicorn:
[process:gunicorn]
cmd = gunicorn -w 3 test:app
cwd = /path/to/project
Create a Procfile if you prefer:
gunicorn = gunicorn -w 3 test:app
Start Gunicorn via Gaffer:
gaffer start
Or load it into a running gafferd instance:
gaffer load
runit
runit is a popular supervisor. A sample service script (see the full example):
#!/bin/sh
GUNICORN=/usr/local/bin/gunicorn
ROOT=/path/to/project
PID=/var/run/gunicorn.pid
APP=main:application
if [ -f $PID ]; then rm $PID; fi
cd $ROOT
exec $GUNICORN -c $ROOT/gunicorn.conf.py --pid=$PID $APP
Save as /etc/sv/<app_name>/run, make it executable, and symlink into
/etc/service/<app_name>. runit will then supervise Gunicorn.
Supervisor
Supervisor configuration example (adapted from examples/supervisor.conf):
[program:gunicorn]
command=/path/to/gunicorn main:application -c /path/to/gunicorn.conf.py
directory=/path/to/project
user=nobody
autostart=true
autorestart=true
redirect_stderr=true
Upstart
Sample Upstart config (logs go to /var/log/upstart/myapp.log):
# /etc/init/myapp.conf
description "myapp"
start on (filesystem.md)
stop on runlevel [016]
respawn
setuid nobody
setgid nogroup
chdir /path/to/app/directory
exec /path/to/virtualenv/bin/gunicorn myapp:app
systemd
systemd can create a UNIX socket and launch Gunicorn on demand.
Service file:
# /etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
Type=notify
NotifyAccess=main
User=someuser
Group=someuser
RuntimeDirectory=gunicorn
WorkingDirectory=/home/someuser/applicationroot
ExecStart=/usr/bin/gunicorn applicationname.wsgi
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Type=notify lets Gunicorn report readiness to systemd. If the service should
run under a transient user consider adding DynamicUser=true. Tighten
permissions further with ProtectSystem=strict if the app permits.
Socket activation file:
# /etc/systemd/system/gunicorn.socket
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
SocketUser=www-data
SocketGroup=www-data
SocketMode=0660
[Install]
WantedBy=sockets.target
Enable and start the socket so it begins listening immediately and on reboot:
systemctl enable --now gunicorn.socket
Test connectivity from the nginx user (Debian defaults to www-data):
sudo -u www-data curl --unix-socket /run/gunicorn.sock http
!!! note
Use systemctl show --value -p MainPID gunicorn.service to retrieve the main
process ID or systemctl kill -s HUP gunicorn.service to send signals.
Configure Nginx to proxy to the new socket:
user www-data;
...
http {
server {
listen 8000;
server_name 127.0.0.1;
location / {
proxy_pass http://unix:/run/gunicorn.sock;
}
}
}
...
!!! note
Adjust listen and server_name for production (typically port 80 and your
site's domain).
Ensure nginx starts automatically:
systemctl enable nginx.service
systemctl start nginx
Browse to http://127.0.0.1:8000/ to verify Gunicorn + Nginx + systemd.
Logging
Configure logging through the CLI flags described in the
settings documentation or via a
logging configuration file.
Rotate logs with logrotate by sending SIGUSR1:
kill -USR1 $(cat /var/run/gunicorn.pid)
!!! note
If you override the LOGGING dictionary, set disable_existing_loggers to
False so Gunicorn's loggers remain active.
!!! warning Gunicorn's error log should capture Gunicorn-related messages only. Route your application logs separately.