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
- 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. Runpip freeze > requirements.txtto track dependencies. - Configure each Django project’s settings. In each project’s
settings.py, setALLOWED_HOSTSto include your domain(s), e.g.,['site1.com', 'www.site1.com']. Also setDEBUG = Falseand configure static files:STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles'). Runpython manage.py collectstaticfor each project. - 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). Visithttp://your_server_ip:8001to verify it works. Stop the process with Ctrl+C. - 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.targetRepeat for site2 with a different socket path and project name.
- Start and enable the Gunicorn services. Run
sudo systemctl start site1 site2andsudo systemctl enable site1 site2. Check status withsudo systemctl status site1to ensure they’re active. - 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.
- Enable the site configs and test Nginx. Run
sudo ln -s /etc/nginx/sites-available/site1 /etc/nginx/sites-enabled/(repeat for site2). Then runsudo nginx -tto check syntax. If OK, reload Nginx:sudo systemctl reload nginx. - Set up SSL (optional but recommended). Use Let’s Encrypt with
sudo certbot --nginx -d site1.com -d www.site1.comand repeat for site2. Certbot will automatically update your Nginx config. - Test both sites. Visit
http://site1.comandhttp://site2.comin 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
--workersto2 * CPU cores + 1for better performance. - Keep each project’s virtual environment isolated – never install packages globally.
- Use environment variables for secrets like
SECRET_KEYand database passwords. - Monitor logs:
journalctl -u site1andtail -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
- Compressed Zipped Folder Access Denied? Fix It
- IPTV TV Boxes: How to Choose the Right Streaming Device
- Custom Races in Assetto Corsa: Server Setup Guide
- Dueling Grounds Events Schedule: Full Guide
- Evaluating Co-Packaged Optics Switches for AI Data Centers
- RTX 5080 Best Settings for 1440p & 4K Gaming