BLOG POSTS
    MangoHost Blog / How to Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu
How to Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu

How to Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu

Setting up Django with Postgres, Nginx, and Gunicorn on Ubuntu creates a production-ready web application stack that can handle real traffic while maintaining performance and scalability. This configuration combines Django’s powerful web framework with PostgreSQL’s robust database capabilities, Nginx’s efficient reverse proxy handling, and Gunicorn’s reliable WSGI server implementation. You’ll learn how to configure each component, connect them together, and troubleshoot common deployment issues that trip up even experienced developers.

How This Stack Works

The Django-Postgres-Nginx-Gunicorn stack follows a multi-tier architecture where each component handles specific responsibilities. Nginx serves as the front-facing web server, handling static files, SSL termination, and proxying dynamic requests to Gunicorn. Gunicorn acts as the WSGI server, managing Python processes and serving your Django application. PostgreSQL provides the database layer with ACID compliance and advanced querying capabilities.

Here’s the request flow:

  • Client requests hit Nginx first
  • Static files get served directly by Nginx
  • Dynamic requests get proxied to Gunicorn via Unix socket
  • Gunicorn spawns worker processes to handle Django application logic
  • Django communicates with PostgreSQL for data operations
  • Response travels back through the same chain
Component Role Performance Impact Common Issues
Nginx Reverse proxy, static files ~10,000 concurrent connections Proxy timeout, static file paths
Gunicorn WSGI server 2x CPU cores + 1 workers optimal Worker timeout, memory leaks
PostgreSQL Database Connection pooling essential Authentication, encoding issues
Django Web framework Debug=False in production Static files, database connections

Prerequisites and System Setup

Before diving into the installation, ensure your Ubuntu server meets the basic requirements. This guide assumes Ubuntu 20.04 or later with sudo privileges and at least 1GB RAM for a basic setup. For production environments, consider using a VPS or dedicated server with adequate resources.

Update your system and install essential packages:

sudo apt update && sudo apt upgrade -y
sudo apt install python3-pip python3-dev python3-venv libpq-dev postgresql postgresql-contrib nginx curl

PostgreSQL Database Configuration

PostgreSQL installation creates a default user, but you’ll need to configure it properly for Django. Start by securing the installation and creating your application database:

sudo -u postgres psql

Inside the PostgreSQL shell, create your database and user:

CREATE DATABASE djangoapp;
CREATE USER djangouser WITH ENCRYPTED PASSWORD 'your_strong_password_here';
ALTER ROLE djangouser SET client_encoding TO 'utf8';
ALTER ROLE djangouser SET default_transaction_isolation TO 'read committed';
ALTER ROLE djangouser SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE djangoapp TO djangouser;
\q

Test the connection to ensure everything works:

psql -h localhost -U djangouser -d djangoapp

Common PostgreSQL issues include authentication failures and encoding problems. If you encounter “peer authentication failed”, edit the pg_hba.conf file:

sudo nano /etc/postgresql/12/main/pg_hba.conf

Change the authentication method for local connections from “peer” to “md5” and restart PostgreSQL:

sudo systemctl restart postgresql

Django Application Setup

Create a dedicated user for your Django application to improve security:

sudo adduser --system --group --home /opt/django djangoapp
sudo mkdir -p /opt/django/myproject
sudo chown djangoapp:djangoapp /opt/django/myproject

Switch to the Django user and set up the virtual environment:

sudo -u djangoapp bash
cd /opt/django/myproject
python3 -m venv venv
source venv/bin/activate
pip install django gunicorn psycopg2-binary

Create a new Django project or clone your existing one:

django-admin startproject myproject .

Configure your Django settings for production. Create a production settings file:

nano myproject/settings_prod.py
from .settings import *
import os

DEBUG = False
ALLOWED_HOSTS = ['your-domain.com', 'www.your-domain.com', 'server-ip-address']

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'djangoapp',
        'USER': 'djangouser',
        'PASSWORD': 'your_strong_password_here',
        'HOST': 'localhost',
        'PORT': '',
    }
}

STATIC_URL = '/static/'
STATIC_ROOT = '/opt/django/myproject/static/'
MEDIA_URL = '/media/'
MEDIA_ROOT = '/opt/django/myproject/media/'

# Security settings
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'

Run initial migrations and collect static files:

python manage.py migrate --settings=myproject.settings_prod
python manage.py collectstatic --settings=myproject.settings_prod --noinput

Gunicorn Configuration

Gunicorn serves as the bridge between Nginx and Django. Create a Gunicorn configuration file:

nano /opt/django/myproject/gunicorn.conf.py
bind = "unix:/opt/django/myproject/gunicorn.sock"
workers = 3
worker_class = "sync"
worker_connections = 1000
max_requests = 1000
max_requests_jitter = 50
timeout = 30
keepalive = 2
user = "djangoapp"
group = "djangoapp"
tmp_upload_dir = None
errorlog = "/opt/django/myproject/logs/gunicorn_error.log"
accesslog = "/opt/django/myproject/logs/gunicorn_access.log"
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s'

Create the logs directory:

mkdir -p /opt/django/myproject/logs

Test Gunicorn manually before creating the service:

cd /opt/django/myproject
source venv/bin/activate
gunicorn --config gunicorn.conf.py myproject.wsgi:application

Create a systemd service file for Gunicorn:

sudo nano /etc/systemd/system/gunicorn.service
[Unit]
Description=Gunicorn instance to serve Django
After=network.target

[Service]
User=djangoapp
Group=www-data
WorkingDirectory=/opt/django/myproject
Environment="PATH=/opt/django/myproject/venv/bin"
ExecStart=/opt/django/myproject/venv/bin/gunicorn --config gunicorn.conf.py myproject.wsgi:application
ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-failure

[Install]
WantedBy=multi-user.target

Enable and start the Gunicorn service:

sudo systemctl daemon-reload
sudo systemctl start gunicorn
sudo systemctl enable gunicorn
sudo systemctl status gunicorn

Nginx Configuration

Nginx handles incoming requests and serves static files efficiently. Create a site configuration:

sudo nano /etc/nginx/sites-available/djangoapp
server {
    listen 80;
    server_name your-domain.com www.your-domain.com;
    
    client_max_body_size 20M;
    
    location = /favicon.ico { access_log off; log_not_found off; }
    
    location /static/ {
        alias /opt/django/myproject/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
    
    location /media/ {
        alias /opt/django/myproject/media/;
        expires 7d;
    }
    
    location / {
        include proxy_params;
        proxy_pass http://unix:/opt/django/myproject/gunicorn.sock;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_connect_timeout 30s;
        proxy_send_timeout 30s;
        proxy_read_timeout 30s;
    }
}

Enable the site and test the configuration:

sudo ln -s /etc/nginx/sites-available/djangoapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

Adjust file permissions for the socket file:

sudo usermod -a -G djangoapp www-data
sudo chmod 710 /opt/django/myproject/

SSL Configuration with Let’s Encrypt

Secure your application with free SSL certificates from Let’s Encrypt:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com -d www.your-domain.com

Set up automatic renewal:

sudo crontab -e

Add this line:

0 12 * * * /usr/bin/certbot renew --quiet

Performance Optimization and Monitoring

Monitor your stack performance with these essential commands:

# Check Gunicorn workers
sudo systemctl status gunicorn
ps aux | grep gunicorn

# Monitor Nginx connections
sudo nginx -t
tail -f /var/log/nginx/access.log

# PostgreSQL performance
sudo -u postgres psql -c "SELECT * FROM pg_stat_activity;"

Optimize PostgreSQL for better performance:

sudo nano /etc/postgresql/12/main/postgresql.conf

Key settings to adjust based on your server specs:

shared_buffers = 256MB
effective_cache_size = 1GB
work_mem = 4MB
maintenance_work_mem = 64MB
checkpoint_completion_target = 0.9
wal_buffers = 16MB
Configuration Development Small Production Large Production
Gunicorn Workers 1 3-5 (2 x CPU cores) + 1
PostgreSQL shared_buffers 128MB 256MB 25% of RAM
Nginx worker_processes 1 auto auto
Max connections 100 200 500+

Common Issues and Troubleshooting

Here are the most frequent problems you’ll encounter and their solutions:

502 Bad Gateway Error:

  • Check if Gunicorn service is running: sudo systemctl status gunicorn
  • Verify socket file permissions and Nginx user group membership
  • Check Gunicorn error logs: tail -f /opt/django/myproject/logs/gunicorn_error.log

Static Files Not Loading:

  • Ensure collectstatic was run successfully
  • Check Nginx static file location configuration
  • Verify file permissions on static directory

Database Connection Issues:

  • Test PostgreSQL connection manually
  • Check pg_hba.conf authentication settings
  • Verify database credentials in Django settings

High Memory Usage:

  • Reduce Gunicorn workers if memory-constrained
  • Enable Gunicorn max_requests to recycle workers
  • Monitor for memory leaks in Django application

Essential debugging commands:

# Check all services status
sudo systemctl status nginx gunicorn postgresql

# Monitor real-time logs
sudo journalctl -u gunicorn -f
tail -f /var/log/nginx/error.log

# Test database connectivity
sudo -u djangoapp psql -h localhost -U djangouser -d djangoapp -c "SELECT version();"

# Check socket file
ls -la /opt/django/myproject/gunicorn.sock

Real-World Use Cases and Best Practices

This stack configuration excels in several scenarios:

  • E-commerce platforms: Handle high traffic with PostgreSQL’s transaction safety
  • Content management systems: Nginx efficiently serves media files while Django handles dynamic content
  • API backends: Gunicorn’s worker model scales well for API-heavy applications
  • Data-driven applications: PostgreSQL’s advanced querying supports complex analytics

Production best practices include:

  • Use environment variables for sensitive configuration
  • Implement proper logging and monitoring
  • Set up database backups with pg_dump automation
  • Configure logrotate for application logs
  • Use connection pooling for high-traffic applications
  • Implement health checks for all services

For comparison with alternatives, this stack offers better performance than Apache + mod_wsgi for most Django applications, while being more straightforward than containerized deployments. The combination provides excellent stability and is widely supported in production environments.

Additional resources for deeper configuration can be found in the Django deployment documentation, Gunicorn configuration guide, and Nginx documentation.



This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.

This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.

Leave a reply

Your email address will not be published. Required fields are marked