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.

Application Services

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:

  1. Systemd starts the service
  2. Gunicorn initializes, loads the application, starts workers
  3. Gunicorn sends READY=1 via sd_notify
  4. 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.

Try It — Systemd Unit File Generator

Open full tool