keepcalculator

06 Numbers and Powers

Host Multiple Django Sites with Nginx and Gunicorn

Card No. 06·65591 · Updated · Standard formula, shown below

This guide shows you how to run multiple Django projects on a single server using Nginx as a reverse proxy and Gunicorn as the application server. You’ll end up with each site accessible via its own domain or subdomain, with isolated virtual environments and separate processes. Before starting, you should have a Linux server (Ubuntu/Debian assumed), root or sudo access, and your Django projects already created locally or in a repository.

What You’ll Need

  • A Linux server (Ubuntu 20.04+ recommended) with SSH access
  • Python 3.6+ and pip installed
  • Nginx installed (sudo apt install nginx)
  • Gunicorn installed (pip install gunicorn)
  • Two or more Django projects, each with its own virtual environment
  • Domain names or subdomains pointing to your server’s IP

Step-by-Step

  1. Set up your Django projects. For each site, create a separate directory (e.g., /var/www/site1, /var/www/site2) and place your Django code there. Create a virtual environment for each: python3 -m venv venv. Activate it and install Django and Gunicorn: pip install django gunicorn. Run pip freeze > requirements.txt to track dependencies.
  2. Configure each Django project’s settings. In each project’s settings.py, set ALLOWED_HOSTS to include your domain(s), e.g., ['site1.com', 'www.site1.com']. Also set DEBUG = False and configure static files: STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles'). Run python manage.py collectstatic for each project.
  3. Test Gunicorn manually. For each project, activate its virtual environment and run: gunicorn --bind 0.0.0.0:8001 yourproject.wsgi (use different ports for each site, e.g., 8001, 8002). Visit http://your_server_ip:8001 to verify it works. Stop the process with Ctrl+C.
  4. Create Gunicorn systemd service files. For each site, create a service file: sudo nano /etc/systemd/system/site1.service. Add the following, adjusting paths and user:
    [Unit]
    Description=Gunicorn for site1
    After=network.target
    
    [Service]
    User=www-data
    Group=www-data
    WorkingDirectory=/var/www/site1
    ExecStart=/var/www/site1/venv/bin/gunicorn --workers 3 --bind unix:/var/www/site1/site1.sock yourproject.wsgi:application
    
    [Install]
    WantedBy=multi-user.target
        

    Repeat for site2 with a different socket path and project name.

  5. Start and enable the Gunicorn services. Run sudo systemctl start site1 site2 and sudo systemctl enable site1 site2. Check status with sudo systemctl status site1 to ensure they’re active.
  6. Configure Nginx server blocks. Create a config file for each site in /etc/nginx/sites-available/. Example for site1:
    server {
        listen 80;
        server_name site1.com www.site1.com;
    
        location = /favicon.ico { access_log off; log_not_found off; }
        location /static/ {
            alias /var/www/site1/staticfiles/;
        }
    
        location / {
            include proxy_params;
            proxy_pass http://unix:/var/www/site1/site1.sock;
        }
    }
        

    Create a similar block for site2 with its domain and socket.

  7. Enable the site configs and test Nginx. Run sudo ln -s /etc/nginx/sites-available/site1 /etc/nginx/sites-enabled/ (repeat for site2). Then run sudo nginx -t to check syntax. If OK, reload Nginx: sudo systemctl reload nginx.
  8. Set up SSL (optional but recommended). Use Let’s Encrypt with sudo certbot --nginx -d site1.com -d www.site1.com and repeat for site2. Certbot will automatically update your Nginx config.
  9. Test both sites. Visit http://site1.com and http://site2.com in a browser. They should each load their respective Django app.

Common Mistakes

  • Forgetting to set ALLOWED_HOSTS – Django will return a 400 Bad Request if your domain isn’t listed.
  • Using the same port for both Gunicorn services – always use different ports or Unix sockets.
  • Not running collectstatic – static files won’t be served, causing missing CSS/JS.
  • Incorrect socket path in Nginx – ensure the path matches the one in the systemd service file.
  • Not enabling the site in Nginx (missing symlink) – Nginx won’t pick up the config.

Tips

  • Use Unix sockets instead of TCP ports – they’re faster and more secure.
  • Set --workers to 2 * CPU cores + 1 for better performance.
  • Keep each project’s virtual environment isolated – never install packages globally.
  • Use environment variables for secrets like SECRET_KEY and database passwords.
  • Monitor logs: journalctl -u site1 and tail -f /var/log/nginx/error.log.

Troubleshooting

If a site returns a 502 Bad Gateway, your Gunicorn socket isn’t being reached. Check that the Gunicorn service is running (sudo systemctl status site1) and that the socket file exists (ls -l /var/www/site1/site1.sock). Verify the Nginx proxy_pass path matches exactly.

If you get a 404 on static files, ensure STATIC_ROOT is set and you ran collectstatic. Check the alias path in Nginx – it must point to the directory where collectstatic placed files.

If Django shows a 400 Bad Request, check ALLOWED_HOSTS – it must include the exact domain you’re using, including subdomains.

If Nginx fails to start after config changes, run sudo nginx -t to find syntax errors. Common issues include missing semicolons or duplicate server names.

If Gunicorn fails to start, check the service logs: journalctl -u site1 -n 50. Often it’s a Python path issue – make sure the ExecStart points to the correct virtual environment’s gunicorn binary.

Frequently Asked Questions

Can I use different Python versions for each site?

Yes, each virtual environment can have its own Python version. Just create the venv with the desired interpreter, e.g., python3.9 -m venv venv.

Do I need a separate Nginx server block for each site?

Yes, each domain or subdomain needs its own server block so Nginx knows which site to serve based on the Host header.

Can I run both sites on the same port?

No, each Gunicorn instance must use a unique port or Unix socket. Nginx listens on port 80/443 and routes to the correct backend based on the domain.

What if I only have one IP address?

That’s fine. Nginx uses the domain name in the request to choose the correct server block, so multiple sites can share the same IP.

How do I update my code after changes?

Pull the new code to the server, run migrations and collectstatic, then restart the Gunicorn service: sudo systemctl restart site1.

Is it better to use Docker instead?

Docker is an alternative that provides more isolation, but this guide’s method is simpler for small to medium projects and uses fewer resources.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →