1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
|
# Deploying Gunicorn
We strongly recommend running Gunicorn behind a proxy server.
## Nginx configuration
Although many HTTP proxies exist, we recommend [Nginx](https://nginx.org/).
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](https://github.com/rakyll/hey) to verify proxy behaviour.
An example configuration for fast clients with Nginx
([source](https://github.com/benoitc/gunicorn/blob/master/examples/nginx.conf)):
```nginx title="nginx.conf"
--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:
```nginx
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`](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ignore_client_abort):
```nginx
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:
```nginx
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:
```bash
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](https://www.rfc-editor.org/rfc/rfc3875), meaning it now records the
proxy IP rather than the upstream client. To log the real client address, set
[`access_log_format`](reference/settings.md#access_log_format) to include `X-Forwarded-For`:
```text
%({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.
## PROXY Protocol
The [PROXY protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)
allows load balancers and reverse proxies to pass original client connection
information (IP address, port) to backend servers. This is especially useful
when TLS termination happens at the proxy layer.
Gunicorn supports both PROXY protocol v1 (text format) and v2 (binary format).
### Configuration
Enable PROXY protocol with the `--proxy-protocol` option:
```bash
# Auto-detect v1 or v2 (recommended)
gunicorn --proxy-protocol auto app:app
# Force v1 only (text format)
gunicorn --proxy-protocol v1 app:app
# Force v2 only (binary format, more efficient)
gunicorn --proxy-protocol v2 app:app
```
Using `--proxy-protocol` without a value is equivalent to `auto`.
!!! warning "Security"
Only enable PROXY protocol when Gunicorn is behind a trusted proxy that sends
PROXY headers. Configure [`--proxy-allow-from`](reference/settings.md#proxy_allow_ips)
to restrict which IPs can send PROXY protocol headers.
### HAProxy
HAProxy can send PROXY protocol headers to backends. Example configuration:
```haproxy
frontend https_front
bind *:443 ssl crt /etc/ssl/certs/site.pem
default_backend gunicorn_back
backend gunicorn_back
# Send PROXY protocol v2 (binary, more efficient)
server gunicorn 127.0.0.1:8000 send-proxy-v2
# Or use v1 (text format)
# server gunicorn 127.0.0.1:8000 send-proxy
```
Start Gunicorn to accept PROXY protocol:
```bash
gunicorn -b 127.0.0.1:8000 --proxy-protocol v2 --proxy-allow-from 127.0.0.1 app:app
```
### stunnel
[stunnel](https://www.stunnel.org/) can terminate TLS and forward connections
with PROXY protocol headers:
```ini
# /etc/stunnel/stunnel.conf
[https]
accept = 443
connect = 127.0.0.1:8000
cert = /etc/ssl/certs/stunnel.pem
key = /etc/ssl/certs/stunnel.key
protocol = proxy
```
The `protocol = proxy` directive tells stunnel to prepend PROXY protocol v1
headers to forwarded connections.
### AWS/ELB
AWS Network Load Balancers (NLB) and Application Load Balancers (ALB) support
PROXY protocol v2. Enable it in the target group settings, then configure
Gunicorn:
```bash
gunicorn --proxy-protocol v2 --proxy-allow-from '*' app:app
```
!!! note
When using `--proxy-allow-from '*'` ensure Gunicorn is not directly
accessible from the internet—only through the load balancer.
## Using virtual environments
Install Gunicorn inside your project
[virtual environment](https://pypi.python.org/pypi/virtualenv) to keep versions
isolated:
```bash
mkdir ~/venvs/
virtualenv ~/venvs/webapp
source ~/venvs/webapp/bin/activate
pip install gunicorn
deactivate
```
Force installation into the active virtual environment with `--ignore-installed`:
```bash
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](https://gaffer.readthedocs.io/) with *gafferd* to manage Gunicorn:
```ini
[process:gunicorn]
cmd = gunicorn -w 3 test:app
cwd = /path/to/project
```
Create a `Procfile` if you prefer:
```procfile
gunicorn = gunicorn -w 3 test:app
```
Start Gunicorn via Gaffer:
```bash
gaffer start
```
Or load it into a running *gafferd* instance:
```bash
gaffer load
```
### runit
[runit](http://smarden.org/runit/) is a popular supervisor. A sample service
script (see the
[full example](https://github.com/benoitc/gunicorn/blob/master/examples/gunicorn_rc)):
```bash
#!/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](http://supervisord.org/) configuration example (adapted from
[examples/supervisor.conf](https://github.com/benoitc/gunicorn/blob/master/examples/supervisor.conf)):
```ini
[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`):
```upstart
# /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](https://www.freedesktop.org/wiki/Software/systemd/) can create a UNIX
socket and launch Gunicorn on demand.
Service file:
```ini
# /etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
Type=notify
NotifyAccess=main
User=someuser
Group=someuser
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:
```ini
# /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:
```bash
systemctl enable --now gunicorn.socket
```
Test connectivity from the nginx user (Debian defaults to `www-data`):
```bash
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:
```nginx
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:
```bash
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](reference/settings.md#logging) or via a
[logging configuration file](https://github.com/benoitc/gunicorn/blob/master/examples/logging.conf).
Rotate logs with `logrotate` by sending `SIGUSR1`:
```bash
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.
|