Skip to main content

Django Gunicorn

Introduction

When deploying a Django application to production, you need something more powerful and robust than Django's built-in development server. This is where Gunicorn (Green Unicorn) comes in. Gunicorn is a Python Web Server Gateway Interface (WSGI) HTTP server that acts as an intermediary between your Django application and the internet or a web server like Nginx or Apache.

In this tutorial, you'll learn:

  • What Gunicorn is and why it's essential for Django deployments
  • How to install and configure Gunicorn
  • How to run your Django application with Gunicorn
  • Best practices for production deployments
  • How to integrate Gunicorn with Nginx for enhanced performance

What is Gunicorn and Why Use It?

Understanding WSGI

Before diving into Gunicorn, it's important to understand WSGI (Web Server Gateway Interface). WSGI is a specification that describes how a web server communicates with web applications. It's a way for server and application to speak to each other using a common interface.

Django's built-in development server is great for testing but not designed for production because:

  1. It's single-threaded and can't handle multiple requests efficiently
  2. It hasn't been security audited
  3. It doesn't scale well under heavy traffic

Enter Gunicorn

Gunicorn is a pre-fork worker model WSGI server. This means:

  • It creates multiple worker processes to handle requests
  • It's designed for production environments
  • It's fast, light on server resources, and relatively simple to configure
  • It integrates well with various web servers like Nginx or Apache

Installing Gunicorn

Let's start by installing Gunicorn in your Django project's environment:

bash
pip install gunicorn

To keep track of your dependencies, add it to your requirements.txt file:

bash
pip freeze > requirements.txt

Basic Usage with Django

Testing Gunicorn Locally

Before deploying to production, you can test Gunicorn locally to ensure it works properly with your Django application:

bash
gunicorn yourproject.wsgi:application

Replace yourproject with your actual Django project name.

By default, Gunicorn starts on port 8000. You can access your application at http://localhost:8000.

Common Command Options

You can customize Gunicorn's behavior with various command-line options:

bash
gunicorn --workers=3 --bind=0.0.0.0:8000 yourproject.wsgi:application

This command:

  • Starts 3 worker processes (--workers=3)
  • Binds to all network interfaces on port 8000 (--bind=0.0.0.0:8000)
  • Uses your project's WSGI application

Configuration File

For production, it's better to use a configuration file rather than command-line options. Create a file named gunicorn_config.py in your project directory:

python
# gunicorn_config.py

# Server socket
bind = "0.0.0.0:8000"

# Worker processes
workers = 3
worker_class = "sync"
worker_connections = 1000
timeout = 30

# Logging
loglevel = "info"
accesslog = "/var/log/gunicorn/access.log"
errorlog = "/var/log/gunicorn/error.log"

Then run Gunicorn with the config file:

bash
gunicorn -c gunicorn_config.py yourproject.wsgi:application

Determining the Number of Workers

The number of workers depends on your server resources. A good rule of thumb is:

workers = (2 * number_of_cpu_cores) + 1

For example, on a dual-core machine:

python
# (2 * 2) + 1 = 5 workers
workers = 5

Running Gunicorn in Production

For production deployments, you'll want Gunicorn to run as a background service that starts automatically and restarts if it crashes.

Create a systemd service file:

bash
sudo nano /etc/systemd/system/gunicorn.service

Add the following content, adjusting paths and user as needed:

[Unit]
Description=gunicorn daemon for Django application
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/path/to/your/django/project
ExecStart=/path/to/virtualenv/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind unix:/path/to/your/django/project/gunicorn.sock \
yourproject.wsgi:application

[Install]
WantedBy=multi-user.target

Start and enable the service:

bash
sudo systemctl start gunicorn
sudo systemctl enable gunicorn

Check the status to ensure it's running correctly:

bash
sudo systemctl status gunicorn

Using a Unix Socket

Notice in the systemd example we used a Unix socket (unix:/path/to/your/django/project/gunicorn.sock) rather than an IP address and port. This is more efficient when Gunicorn is on the same server as your web server (Nginx/Apache).

Integrating with Nginx

Gunicorn is excellent at running Python applications, but it's not designed to serve static files or handle SSL. This is where Nginx comes in—it acts as a reverse proxy, handling client requests and forwarding them to Gunicorn.

Create a Nginx configuration file:

server {
listen 80;
server_name example.com www.example.com;

location /static/ {
root /path/to/your/django/project;
}

location /media/ {
root /path/to/your/django/project;
}

location / {
proxy_pass http://unix:/path/to/your/django/project/gunicorn.sock;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}

This configuration:

  1. Serves static and media files directly through Nginx
  2. Forwards other requests to Gunicorn via the Unix socket
  3. Passes important headers for proper request handling

After creating the configuration, enable it and restart Nginx:

bash
sudo ln -s /etc/nginx/sites-available/yourproject /etc/nginx/sites-enabled
sudo nginx -t # Test the configuration
sudo systemctl restart nginx

Best Practices

1. Always Run Behind a Reverse Proxy

Always place Gunicorn behind a web server like Nginx or Apache. This setup provides:

  • Better static file handling
  • Security benefits
  • Load balancing
  • SSL termination

2. Worker Timeouts

Set appropriate timeouts to prevent hanging processes:

python
# In gunicorn_config.py
timeout = 30 # seconds

3. Keep-Alive Settings

For better performance with long-lived connections:

python
# In gunicorn_config.py
keepalive = 2 # seconds

4. Worker Class Selection

Gunicorn supports different worker types:

  • sync (default): Standard synchronous workers
  • eventlet: For async I/O operations
  • gevent: Another async option, often with better performance
python
# In gunicorn_config.py
worker_class = "gevent"

Note: To use eventlet or gevent, you'll need to install these packages:

bash
pip install gevent  # or eventlet

5. Health Checks

Implement regular health checks for your Gunicorn workers:

python
# In gunicorn_config.py
check_client_connection = True

Troubleshooting Common Issues

1. Gunicorn Not Starting

Check for syntax errors in your Django code or in the Gunicorn configuration:

bash
python manage.py check

2. Connection Refused Errors

If you get "Connection refused" errors:

  • Ensure Gunicorn is running (systemctl status gunicorn)
  • Verify the socket or port configuration is correct
  • Check firewall settings

3. Permission Issues

If you encounter permission errors with the socket:

bash
sudo chown www-data:www-data /path/to/your/django/project/gunicorn.sock
sudo chmod 660 /path/to/your/django/project/gunicorn.sock

4. Viewing Logs

Always check the logs when troubleshooting:

bash
sudo journalctl -u gunicorn  # For systemd logs
tail -f /var/log/gunicorn/error.log # If you've configured custom logs

Real-World Example: Complete Deployment Flow

Let's put everything together in a realistic deployment scenario:

  1. Prepare your Django project:

    bash
    # Make sure DEBUG is False
    # Configure static files
    python manage.py collectstatic
  2. Install and configure Gunicorn:

    bash
    pip install gunicorn
    # Create gunicorn_config.py (as shown earlier)
  3. Set up systemd service:

    bash
    # Create and enable gunicorn.service (as shown earlier)
  4. Configure Nginx:

    bash
    # Create and enable Nginx site config (as shown earlier)
  5. Secure with SSL (optional but recommended):

    bash
    # Install certbot
    sudo apt install certbot python3-certbot-nginx

    # Obtain and configure SSL certificate
    sudo certbot --nginx -d example.com -d www.example.com
  6. Start everything up:

    bash
    sudo systemctl start gunicorn
    sudo systemctl enable gunicorn
    sudo systemctl restart nginx

Summary

In this guide, we've covered:

  • What Gunicorn is and why it's important for Django deployments
  • How to install and configure Gunicorn for your Django application
  • Best practices for production deployments
  • How to integrate Gunicorn with Nginx
  • Troubleshooting common issues

Gunicorn is an essential tool in the Django deployment toolkit. When properly configured and paired with a web server like Nginx, it provides a robust, efficient, and scalable solution for serving your Django applications in production environments.

Additional Resources

Exercises

  1. Basic Setup: Deploy a simple Django application using Gunicorn locally and test its performance.
  2. Configuration Practice: Create a custom Gunicorn configuration file that optimizes for a server with 4 CPU cores.
  3. Systemd Integration: Create a systemd service file for your Gunicorn application and test starting/stopping the service.
  4. Nginx Integration: Configure Nginx as a reverse proxy for your Gunicorn server, including proper static file handling.
  5. Performance Testing: Use a tool like Apache Bench or wrk to compare the performance of the Django development server versus Gunicorn with different worker configurations.

With Gunicorn in your deployment arsenal, you're well-equipped to run Django applications that can handle real-world traffic efficiently and reliably.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)