How to Host Multiple Django Websites on a Single Server using Nginx and Gunicorn
If you are a web developer or a startup founder, managing infrastructure costs is often a top priority. When building web applications—particularly with Python and Django—a natural question arises: Do I need to rent a separate virtual private server (VPS) for every single website I build?
The short answer is: No, absolutely not.
Hosting multiple websites on a single server is not only cost-effective but also entirely professional and scalable when configured correctly. The secret lies in isolating your applications using virtual environments, managing their execution with dedicated Gunicorn service files, and intelligently routing incoming web traffic using Nginx Server Blocks (virtual hosts).
In this comprehensive, step-by-step guide, we will walk you through the exact architecture required to host multiple, completely independent websites on a single Ubuntu server without causing conflicts, downtime, or security breaches.
Understanding the Architecture
Before diving into the terminal commands, it is crucial to understand how multiple apps share the same physical server.
When a user types your domain name into their browser, the request hits your server. If you have five websites hosted there, the server needs a traffic cop to decide which application should handle that request.
- Nginx acts as that traffic cop. It listens on ports 80 (HTTP) and 443 (HTTPS), reads the domain name requested, and routes the traffic to the correct local socket.
- Gunicorn is your application server. Instead of running one giant Gunicorn instance for all your sites, we will create separate Gunicorn systemd services for each website. This ensures that if Website A crashes or experiences a memory leak, Website B remains completely unaffected.
- Virtual Environments (venv) ensure that your Python dependencies (like Django versions) do not conflict between projects.
Let's assume you want to host two applications: ProjectAlpha (alpha.com) and ProjectBeta (beta.com).
Step 1: Establishing the Directory Structure
Security and organization start at the file system level. Never mix project files. Create a dedicated directory structure for your web applications, typically inside /var/www/ or your user's home directory.
# Create directories for both projects
mkdir -p /var/www/project_alpha
mkdir -p /var/www/project_beta
Place your Django codebase inside these respective folders. For instance, /var/www/project_alpha/src/ would contain your manage.py file for Alpha, and /var/www/project_beta/src/ would contain the one for Beta.
Step 2: Isolating Python Dependencies with Virtual Environments
The most common mistake developers make when hosting multiple apps on one server is installing dependencies globally. If Alpha requires Django 4.2 and Beta requires Django 5.0, a global installation will break one of them.
Create a separate virtual environment for each project within its directory:
# Setup for Project Alpha
cd /var/www/project_alpha
python3 -m venv venv
source venv/bin/activate
pip install django gunicorn psycopg2-binary
deactivate
# Setup for Project Beta
cd /var/www/project_beta
python3 -m venv venv
source venv/bin/activate
pip install django gunicorn psycopg2-binary
deactivate
Now, both projects have totally isolated Python interpreters and package environments.
Step 3: Configuring Dedicated Gunicorn Services
This is the hardest but most crucial step for stability. We need to tell the operating system (systemd) how to run and manage Gunicorn for each project separately. We do this by creating two distinct Unix sockets and two service files.
1. Creating Systemd Sockets
Sockets allow Nginx to communicate with Gunicorn securely without using up TCP network ports.
Create the socket for Alpha: sudo nano /etc/systemd/system/gunicorn_alpha.socket
[Unit]
Description=gunicorn socket for Project Alpha
[Socket]
ListenStream=/run/gunicorn_alpha.sock
[Install]
WantedBy=sockets.target
Create the socket for Beta: sudo nano /etc/systemd/system/gunicorn_beta.socket
[Unit]
Description=gunicorn socket for Project Beta
[Socket]
ListenStream=/run/gunicorn_beta.sock
[Install]
WantedBy=sockets.target
2. Creating Systemd Services
Next, create the service files that define how Gunicorn boots up.
Create the service for Alpha: sudo nano /etc/systemd/system/gunicorn_alpha.service
[Unit]
Description=gunicorn daemon for Project Alpha
Requires=gunicorn_alpha.socket
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/project_alpha/src
ExecStart=/var/www/project_alpha/venv/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind unix:/run/gunicorn_alpha.sock \
alpha_config.wsgi:application
[Install]
WantedBy=multi-user.target
Create the service for Beta: sudo nano /etc/systemd/system/gunicorn_beta.service
[Unit]
Description=gunicorn daemon for Project Beta
Requires=gunicorn_beta.socket
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/project_beta/src
ExecStart=/var/www/project_beta/venv/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind unix:/run/gunicorn_beta.sock \
beta_config.wsgi:application
[Install]
WantedBy=multi-user.target
Notice how everything is explicitly separated: different virtual environments (ExecStart), different working directories, and different binding sockets.
Step 4: Activating the Gunicorn Services
With the configuration files written, you must now start and enable both sockets so they automatically boot if the server reboots.
# Reload system daemon to register the new files
sudo systemctl daemon-reload
# Start and enable Alpha
sudo systemctl start gunicorn_alpha.socket
sudo systemctl enable gunicorn_alpha.socket
sudo systemctl start gunicorn_alpha.service
sudo systemctl enable gunicorn_alpha.service
# Start and enable Beta
sudo systemctl start gunicorn_beta.socket
sudo systemctl enable gunicorn_beta.socket
sudo systemctl start gunicorn_beta.service
sudo systemctl enable gunicorn_beta.service
If you check /run/, you should now see two separate socket files: gunicorn_alpha.sock and gunicorn_beta.sock.
Step 5: Configuring Nginx Server Blocks
Gunicorn is now running your applications flawlessly in the background, but the outside world cannot reach them. We use Nginx to listen for internet traffic and proxy it to the correct socket based on the domain name.
1. Nginx Config for Alpha
Create a new file: sudo nano /etc/nginx/sites-available/alpha
server {
listen 80;
server_name alpha.com www.alpha.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /var/www/project_alpha/src;
}
location /media/ {
root /var/www/project_alpha/src;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn_alpha.sock;
}
}
2. Nginx Config for Beta
Create a new file: sudo nano /etc/nginx/sites-available/beta
server {
listen 80;
server_name beta.com www.beta.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /var/www/project_beta/src;
}
location /media/ {
root /var/www/project_beta/src;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn_beta.sock;
}
}
3. Enable the Sites
To enable these configurations, create a symbolic link from sites-available to sites-enabled.
sudo ln -s /etc/nginx/sites-available/alpha /etc/nginx/sites-enabled
sudo ln -s /etc/nginx/sites-available/beta /etc/nginx/sites-enabled
Test your Nginx configuration for syntax errors and restart the service:
sudo nginx -t
sudo systemctl restart nginx
Step 6: Securing with SSL (Certbot)
A modern website must serve traffic over HTTPS. Hosting multiple sites makes no difference to Let's Encrypt; it will happily generate free SSL certificates for both domains.
Simply run Certbot for each domain independently:
sudo certbot --nginx -d alpha.com -d www.alpha.com
sudo certbot --nginx -d beta.com -d www.beta.com
Certbot will automatically modify your Nginx server blocks to include the SSL certificates and redirect all HTTP traffic to HTTPS.
Best Practices for Multi-App Hosting
Now that you have multiple websites thriving on a single server, you must adopt a proactive mindset regarding resource management.
- Monitor RAM Usage: Every Gunicorn worker consumes RAM. If you have 3 workers per site and 5 sites, that’s 15 workers running concurrently. If you have a 2GB RAM server, you may suffer from Out-Of-Memory (OOM) crashes. Adjust your
--workerscount in thegunicorn.servicefiles based on your server capacity. A good baseline formula is(2 * CPU Cores) + 1total workers across the entire server. - Database Segregation: Do not share the same PostgreSQL database for multiple projects. Create a unique Database and a unique Database User for every single website. This ensures database-level security isolation.
- Use
.envfiles: Never hardcode yourSECRET_KEY, database passwords, or debug flags. Usepython-dotenvand keep a strict.envfile in each project directory. - Log Management: By default, logs can pile up and fill your disk space. Ensure that you have log rotation configured for your Nginx and systemd logs.
Conclusion
Hosting multiple Django websites on a single server is a rite of passage for robust web developers. It allows you to maximize your cloud infrastructure investment while maintaining strict, professional isolation between client projects.
By meticulously applying virtual environments, writing dedicated Gunicorn systemd configurations, and leveraging the routing intelligence of Nginx, you have built a scalable environment. If one website goes viral, you can easily migrate just its database and code folder to a new server without breaking the others, making this architectural pattern both cost-effective for today and scalable for tomorrow.
And Finally after deploying your sites, test their security and performance useing our open-source scanners and one of the top scanners open-source on internet.
Discussion 0
No comments yet. Be the first to start the discussion!
Leave a Comment