Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

You can host several Django sites on one Linux server by running one public Nginx instance and giving each site its own Gunicorn process, environment, settings, logs, and private Unix socket (or loopback TCP port). Nginx chooses a site from the request hostname, serves that site’s static and media files, and proxies dynamic requests only to the matching Gunicorn upstream.

The isolation matters: a deployment, restart, configuration error, or exhausted worker pool in one project should not automatically take the others offline. The examples below use site1.example and site2.example; replace every domain, path, user, group, module name, and credential with values appropriate to your server.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Architecture: one router, separate Django applications

Each domain must have a complete path through DNS, Nginx, Gunicorn, and Django:

  1. DNS records for each hostname point to the server’s public address.
  2. Nginx receives the request on ports 80 or 443 and selects a server block using the HTTP Host header.
  3. That block serves static and media files from the selected project’s directories.
  4. Requests that need Python are proxied to that project’s Unix socket or loopback port.
  5. Gunicorn loads the project’s WSGI application and returns the response through Nginx.

Do not expose Gunicorn directly on a public interface when Nginx is the intended entry point. Keep sockets under /run, or bind TCP listeners to 127.0.0.1. Forward the original host, client address, and protocol so Django can construct correct URLs and apply its security checks.

#1 Best Overall
Elebase USB to USB C Adapter for iPhone 18 Pro Max,USBC Car Charger Adapter | 4Pack Type C Female to A Male Cable Converter for Apple Duo 17 16 15 14 Plus,iWatch Watch 12 11 10,iPad 9 8 Air,Airpods 5 4
  • Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
  • Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
  • Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
  • Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
  • 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.

Plan the per-site isolation before installing anything

Use a separate project root, virtual environment, settings module, environment file, database credentials, log destination, systemd unit, and upstream endpoint for every site. A practical layout might look like this:

/srv/site1/current/                 # site1 project and manage.py
/srv/site1/venv/                     # site1 Python environment
/srv/site1/.env                      # secrets and production settings
/srv/site1/shared/static/            # STATIC_ROOT
/srv/site1/shared/media/             # MEDIA_ROOT
/run/site1-gunicorn.sock             # private runtime socket

/srv/site2/current/
/srv/site2/venv/
/srv/site2/.env
/srv/site2/shared/static/
/srv/site2/shared/media/
/run/site2-gunicorn.sock

The names are examples, not distribution requirements. Create directories with ownership that lets the relevant service read application code and lets Nginx read the static/media trees. Keep each site’s database user restricted to that site’s database, and do not commit .env files or secret keys to source control.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Prepare the host and each Django project

1. Provision DNS, firewall, and service identities

Create an A or AAAA record for every public hostname and allow only the ports you need. A typical public web server permits TCP 80 and 443; SSH should be restricted to your administration policy. Create a dedicated Unix user (or otherwise enforce a separate service identity) for each project so a process cannot casually write into another project’s files.

2. Create virtual environments and install pinned dependencies

From each project root, create a virtual environment and install the application’s locked requirements. Use the Gunicorn executable from that environment, not a system-wide executable that could load the wrong dependency set.

python3 -m venv /srv/site1/venv
/srv/site1/venv/bin/pip install --upgrade pip
/srv/site1/venv/bin/pip install -r /srv/site1/current/requirements.txt

python3 -m venv /srv/site2/venv
/srv/site2/venv/bin/pip install --upgrade pip
/srv/site2/venv/bin/pip install -r /srv/site2/current/requirements.txt

Confirm the WSGI import manually for each project before involving Nginx. For a project whose WSGI module is config.wsgi, for example:

cd /srv/site1/current
/srv/site1/venv/bin/python -c "import config.wsgi; print('site1 WSGI import OK')"

3. Configure production settings, migrate, and collect assets

Set the production settings module and environment variables for each site. Run migrations against the correct database and collect static files into that site’s STATIC_ROOT:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Sale
Anker USB-C Hub, 5-in-1 USB Hub for Laptops, 4K HDMI Multiport Adapter
  • 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
  • 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
  • Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
  • 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
  • What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.
cd /srv/site1/current
/srv/site1/venv/bin/python manage.py migrate --settings=config.settings.production
/srv/site1/venv/bin/python manage.py collectstatic --noinput --settings=config.settings.production

cd /srv/site2/current
/srv/site2/venv/bin/python manage.py migrate --settings=config.settings.production
/srv/site2/venv/bin/python manage.py collectstatic --noinput --settings=config.settings.production

Run the deployment check separately for every project:

/srv/site1/venv/bin/python /srv/site1/current/manage.py check --deploy --settings=config.settings.production
/srv/site2/venv/bin/python /srv/site2/current/manage.py check --deploy --settings=config.settings.production

Create one Gunicorn service per site

A separate service gives each project independent restarts, deploys, logs, resource accounting, and failure boundaries. The unit must use that site’s working directory, virtual environment, WSGI module, and environment file.

Socket-activated pattern

Socket activation gives Nginx a stable path and lets systemd create the socket with explicit ownership and mode. Create /etc/systemd/system/site1-gunicorn.socket:

[Unit]
Description=site1 Gunicorn socket

[Socket]
ListenStream=/run/site1-gunicorn.sock
SocketUser=nginx
SocketGroup=site1
SocketMode=0660

[Install]
WantedBy=sockets.target

Use the account name used by your distribution’s Nginx worker instead of nginx if necessary. The matching service, /etc/systemd/system/site1-gunicorn.service, can be:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
[Unit]
Description=site1 Gunicorn service
Requires=site1-gunicorn.socket
After=network.target

[Service]
Type=notify
User=site1
Group=site1
WorkingDirectory=/srv/site1/current
EnvironmentFile=/srv/site1/.env
ExecStart=/srv/site1/venv/bin/gunicorn 
          --workers 3 
          --bind unix:/run/site1-gunicorn.sock 
          config.wsgi:application
ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-failure

[Install]
WantedBy=multi-user.target

Repeat the pair with unique names, paths, users, and WSGI modules for site2. Do not copy the worker count blindly: the correct number depends on application behavior, available memory, request latency, and background work. The documented primitives do not provide a universal multi-site formula or benchmark.

If you do not want socket activation, run a service that binds only to a unique loopback port, such as 127.0.0.1:8101 for site1 and 127.0.0.1:8102 for site2. Never reuse a port.

Rank #3
Sale
Anker USB C Hub, 7in1 Multi-Port USB Adapter, 4K@60Hz USBC to HDMI Splitter | 85W Max PD, 2 USB 3.0 & 1 USBC Data Ports, SD/TF Card Reader, for Laptops/Mac and Other Type C Devices (Charger Not Included)
  • Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
  • Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
  • Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
  • Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
  • What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.

Enable and test every service

sudo systemctl daemon-reload
sudo systemctl enable --now site1-gunicorn.socket site1-gunicorn.service
sudo systemctl enable --now site2-gunicorn.socket site2-gunicorn.service
sudo systemctl status site1-gunicorn.service site2-gunicorn.service
sudo journalctl -u site1-gunicorn.service -n 100 --no-pager

Before DNS cutover, test from the local machine. For a Unix socket, the exact curl syntax depends on the installed curl build; use a request that targets the socket and supplies the intended host header. For a loopback listener, test each port directly:

curl -i -H 'Host: site1.example' http://127.0.0.1:8101/
curl -i -H 'Host: site2.example' http://127.0.0.1:8102/

A successful local response proves that the WSGI import, environment, database connection, and application process work before Nginx is added.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Configure Nginx server blocks

Reject unknown hostnames

Define a default server that does not route an unrecognized Host header to one of your applications. Returning status 444 is a common Nginx policy; use another explicit rejection response if it better fits your operations.

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 444;
}

Route site1

Create a dedicated file such as /etc/nginx/sites-available/site1.example and enable it using your distribution’s normal sites-enabled mechanism:

server {
    listen 80;
    listen [::]:80;
    server_name site1.example www.site1.example;

    location /static/ {
        alias /srv/site1/shared/static/;
    }

    location /media/ {
        alias /srv/site1/shared/media/;
    }

    location / {
        proxy_pass http://unix:/run/site1-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_redirect off;
    }
}

The trailing slash in each alias value is intentional. It maps the URL prefix to the corresponding filesystem directory. Ensure the Nginx worker can traverse every parent directory and read the files.

Route site2 to its own upstream

server {
    listen 80;
    listen [::]:80;
    server_name site2.example www.site2.example;

    location /static/ {
        alias /srv/site2/shared/static/;
    }

    location /media/ {
        alias /srv/site2/shared/media/;
    }

    location / {
        proxy_pass http://unix:/run/site2-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_redirect off;
    }
}

If you use loopback TCP instead, replace the corresponding proxy_pass value with http://127.0.0.1:8101 or http://127.0.0.1:8102. Keep the mapping one-to-one: a site1 server block must never point at site2’s endpoint.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Sale
UGREEN USB to USB C Adapter Combo 4-Pack, 10Gbps USB C Converter Space Gray
  • Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
  • Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
  • Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
  • Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
  • Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft

Validate before reloading

sudo nginx -t
sudo systemctl reload nginx

Always run the syntax test first. A reload applies the new configuration without unnecessarily terminating existing connections; if validation fails, keep the running configuration and inspect the reported file and line.

Add HTTPS for every public domain

Obtain a certificate covering each hostname, then create TLS server blocks on port 443. Redirect the corresponding port-80 block to HTTPS after certificate validation:

server {
    listen 80;
    listen [::]:80;
    server_name site1.example www.site1.example;
    return 301 https://$host$request_uri;
}

The HTTPS block retains site1’s static/media locations and socket, but adds the certificate paths and listens on 443:

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name site1.example www.site1.example;

    ssl_certificate     /etc/ssl/site1/fullchain.pem;
    ssl_certificate_key /etc/ssl/site1/privkey.pem;

    location /static/ { alias /srv/site1/shared/static/; }
    location /media/  { alias /srv/site1/shared/media/; }

    location / {
        proxy_pass http://unix:/run/site1-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_redirect off;
    }
}

Repeat with site2’s certificate and upstream. If TLS terminates at another trusted proxy before this Nginx instance, establish a single, well-defined termination layer and restrict which proxy addresses Django trusts. Do not blindly trust client-supplied forwarded headers from the public internet.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Set Django’s production security settings per project

Hosts and CSRF origins

With DEBUG = False, Django requires an explicit ALLOWED_HOSTS list. Configure only the domains that belong to that project:

DEBUG = False
ALLOWED_HOSTS = ["site1.example", "www.site1.example"]
CSRF_TRUSTED_ORIGINS = ["https://site1.example", "https://www.site1.example"]

Use the equivalent list for site2. These settings prevent an arbitrary Host header from being treated as a valid site and make cross-site form protection match the actual HTTPS origins.

Best Value
Sale
Anker USB C Hub, 5-in-1 USBC to HDMI Splitter with 4K Display | 85W Max PD, 1 Powered USB-C 5Gbps & 2 Powered USB-A 3.0 5Gbps Data Ports, for MacBook Pro, MacBook Air, Dell and More (Black)
  • 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
  • Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
  • Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
  • HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
  • What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.

Secrets, cookies, and proxy-aware HTTPS

Load SECRET_KEY, database passwords, email credentials, and other secrets from the per-site environment or a secrets manager. Keep them out of source control. When Nginx terminates TLS and sends X-Forwarded-Proto, configure Django consistently:

SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = True

Only set SECURE_PROXY_SSL_HEADER when the proxy is configured to overwrite the header and the network path is trusted. Otherwise an attacker may be able to spoof the scheme. Sites that support login should use HTTPS site-wide because passwords, session cookies, and password-reset tokens are credentials.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Static and media paths

Each project needs distinct roots:

STATIC_URL = "/static/"
STATIC_ROOT = "/srv/site1/shared/static"
MEDIA_URL = "/media/"
MEDIA_ROOT = "/srv/site1/shared/media"

Set site2’s values to its own directories, then rerun collectstatic after deployments that change assets. Do not point two projects at one writable media directory unless sharing is an intentional, separately secured design.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Unix sockets or loopback TCP ports?

Both approaches keep Gunicorn private. Choose according to your operational needs rather than assuming one is universally faster.

Consideration Unix socket Loopback TCP
Permission management Requires correct socket user, group, mode, and directory access. Uses local network binding and avoids filesystem socket ACLs.
Observability Socket existence and ownership are useful diagnostics, but tooling is less familiar to some teams. Standard port checks and local HTTP tools make endpoint testing straightforward.
Portability Convenient when Nginx and Gunicorn share one host. Easier to move the application to another host or container without changing the protocol.
Cross-host support Not usable across machines. Can support a separate host when protected by a private network and appropriate access controls.
Failure diagnosis Check the path, service state, ownership, group, and mode. Check that the process listens on the expected address and that no other service owns the port.

One shared Gunicorn service or one per site?

A single service can reduce unit-file overhead, but it couples applications operationally. Separate services are usually the safer default for unrelated sites.

Axis Shared service Separate service per site
Isolation One process configuration and lifecycle may affect several projects. Each project has its own environment, workers, logs, and restart boundary.
Deployments Coordinating code and dependency changes is more complex. Deploy and restart one site without touching the others.
Blast radius A crash, bad import, or resource spike can affect all applications in the service. A failing site normally leaves the other services running.
Resource accounting Usage is harder to attribute to a particular project. Per-unit status, logs, limits, and alerts are clearer.
Overhead Fewer units and endpoints to maintain. More configuration files, service names, sockets, and health checks.

Separate services do not remove host-level limits. Monitor memory, CPU, file descriptors, database connections, and disk space across the machine, and set resource limits only after measuring each workload.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Deployment sequence that minimizes surprises

  1. Provision the Linux host, firewall rules, DNS records, and per-site users or service identities.
  2. Create each project’s directory tree and virtual environment; install pinned dependencies.
  3. Set production secrets and settings, then run migrations and collectstatic for each database and project.
  4. Add and validate each Gunicorn service and socket (or loopback port); enable them with systemd.
  5. Verify socket ownership or port bindings and test each WSGI application locally.
  6. Add Nginx’s default rejection server and one server block per domain.
  7. Run nginx -t, reload Nginx, and test each hostname with an explicit Host header.
  8. Install certificates, add HTTPS blocks and HTTP-to-HTTPS redirects, then reload again.
  9. Run check --deploy for every site, inspect application and Nginx logs, and verify static files, media access, login, redirects, and error pages.
  10. Restart one site deliberately and confirm the other domains remain available before declaring the setup operational.

Troubleshoot by following the request path

Symptom Checks and corrective action
502 Bad Gateway Check that the matching Gunicorn unit is active, the socket or port exists, the WSGI module imports under that site’s virtual environment, and Nginx can connect. For sockets, inspect user, group, mode, and parent-directory permissions.
The wrong site appears Inspect DNS resolution, the request’s Host header, exact server_name values, the default server, and the upstream path in the selected block. Confirm that no site points at another project’s socket or port.
Static files return 404 Check that STATIC_ROOT matches the Nginx alias, rerun collectstatic, verify URL prefixes, and grant Nginx read and directory-traverse permission.
Media files fail Verify MEDIA_ROOT, the matching /media/ alias, filesystem permissions, and whether the application actually wrote the file under that site’s media directory.
Redirect loop or insecure URL Ensure Nginx sends X-Forwarded-Proto, Django’s proxy-SSL setting matches the trusted TLS topology, and only the intended layer performs HTTPS redirects.
Permission denied on socket Align SocketUser, SocketGroup, SocketMode, the Gunicorn service identity, and Nginx’s worker account. Recreate the socket after changing the unit and reload systemd.
Changes do not appear Confirm that the deployment updated the directory named by WorkingDirectory, restarted the correct unit, and did not leave an old virtual environment or process serving the request.

Operational checks after launch

  • Keep Nginx access/error logs and each Gunicorn service log separated by site.
  • Alert on repeated Gunicorn restarts, rising 5xx responses, socket disappearance, disk exhaustion, and memory pressure.
  • Test every hostname after certificate renewal, Nginx changes, Django upgrades, and dependency updates.
  • Back up each database and media directory independently, and document how to restore one site without restoring all sites.
  • Review forwarded-header trust whenever the proxy topology changes.
  • Run the Django deployment check again after changing security, host, cookie, database, or email settings.

The Bottom Line

The reliable multi-site pattern is simple: Nginx is the only public router, every hostname has its own server block, and every Django project has an independent Gunicorn service and private endpoint. Correct host validation, HTTPS, forwarded headers, static-file ownership, and socket permissions determine whether that design remains secure and supportable in production.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.