Python Gunicorn Service
Set up a systemd service for a Python web application served by Gunicorn. Covers the notify service type, Unix socket binding, virtual environments, and environment file management.
Detailed Explanation
Serving Python Apps with Gunicorn and systemd
Gunicorn (Green Unicorn) is the standard WSGI HTTP server for Python web applications. When combined with systemd, it provides a robust production deployment. Gunicorn supports the Type=notify protocol for reliable startup detection.
[Unit]
Description=Gunicorn WSGI Application Server
After=network.target
[Service]
Type=notify
ExecStart=/opt/myapp/venv/bin/gunicorn \
--workers 3 \
--bind unix:/run/myapp/gunicorn.sock \
--access-logfile - \
--error-logfile - \
myapp.wsgi:application
Restart=on-failure
RestartSec=5
User=www-data
Group=www-data
WorkingDirectory=/opt/myapp
EnvironmentFile=/opt/myapp/.env
RuntimeDirectory=myapp
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
The notify Service Type
Gunicorn supports the sd_notify() protocol, which means it tells systemd exactly when it's ready to serve requests. This is more reliable than Type=simple because:
- Systemd starts the service
- Gunicorn initializes, loads the application, starts workers
- Gunicorn sends
READY=1via sd_notify - Now systemd considers the service active
This prevents dependent services from starting before Gunicorn is truly ready.
Unix Socket Binding
Binding to a Unix socket instead of a TCP port is preferred when Nginx sits in front as a reverse proxy:
--bind unix:/run/myapp/gunicorn.sock
Benefits:
- Performance: Unix sockets are faster than TCP loopback
- Security: No exposed port; only the socket file is accessible
- Simplicity: No port conflict concerns
RuntimeDirectory
The RuntimeDirectory=myapp directive tells systemd to create /run/myapp/ with correct ownership before the service starts. This is where the Unix socket lives. The directory is automatically cleaned up when the service stops.
Virtual Environment
Note that ExecStart uses the full path to the Gunicorn binary inside the virtual environment:
/opt/myapp/venv/bin/gunicorn
This ensures the correct Python interpreter and dependencies are used without needing to activate the virtual environment.
Worker Configuration
The number of workers depends on your server's CPU cores. A common formula is:
workers = (2 * CPU_CORES) + 1
For a 2-core server, --workers 5 is a good starting point. Adjust based on your application's memory usage per worker.
Use Case
Deploying a Django or Flask web application behind Nginx, using Gunicorn as the WSGI server, with proper process management, environment configuration from files, and Unix socket communication.