Applies to: LumaDock VPS • Ubuntu 22.04 LTS (Jammy)

This guide shows how to put an existing Django project folder (with manage.py and requirements.txt) online. You’ll upload/clone your code, set environment variables, install dependencies in a virtualenv, run Django via Gunicorn, and serve it publicly with Nginx—plus optional HTTPS (Let’s Encrypt) and PostgreSQL.

Requirements

  • A LumaDock VPS running Ubuntu 22.04 LTS
  • Root SSH access (or a sudo-enabled user)
  • An existing Django project with requirements.txt (and optional .env)
  • (Recommended) A domain pointed to your VPS IP

1) Prepare the server

ssh root@YOUR_SERVER_IP
apt update && apt -y upgrade
apt install -y python3 python3-venv python3-pip python3-dev build-essential nginx git

Optional: create a non-root user: adduser admin && usermod -aG sudo admin then ssh admin@YOUR_SERVER_IP.

2) Upload or clone your project

We’ll place the app under /opt/djangoapp (adjust paths to your preference).

mkdir -p /opt/djangoapp
cd /opt/djangoapp

# Option A: clone from Git
git clone https://YOUR_REPO_URL.git .
# Option B: upload via SFTP/rsync into this folder

Ensure your manage.py lives at /opt/djangoapp/manage.py.

3) Create a virtualenv & install dependencies

cd /opt/djangoapp
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt

If your project uses a specific Gunicorn / ASGI server, ensure it’s listed in requirements.txt (e.g., gunicorn).

4) Configure environment variables & Django settings

Set a few production basics:

  • ALLOWED_HOSTS → add your domain and/or server IP
  • DEBUGFalse in production
  • SECRET_KEY → secure random string (store in environment, not in Git)
  • DATABASE_URL or DB settings (if not using SQLite)
# Example: /opt/djangoapp/.env (if using python-dotenv/django-environ)
DJANGO_DEBUG=False
DJANGO_SECRET_KEY=change_me_to_a_strong_secret
DJANGO_ALLOWED_HOSTS=yourdomain.com, YOUR_SERVER_IP

Load your .env in settings.py (via django-environ or similar) or set environment variables in the systemd unit (see Gunicorn step).

5) Database & migrations

If you use SQLite, you can skip installation. For PostgreSQL/MySQL, install and configure first, then:

cd /opt/djangoapp
source venv/bin/activate
python manage.py migrate
python manage.py createsuperuser  # optional

If using PostgreSQL, make sure psycopg2-binary (or psycopg) is installed and your DB settings are correct.

6) Static & media files

# settings.py (example)
STATIC_URL = "static/"
STATIC_ROOT = "/opt/djangoapp/static"
MEDIA_URL  = "media/"
MEDIA_ROOT = "/opt/djangoapp/media"
python manage.py collectstatic --noinput

We’ll configure Nginx to serve /static/ and /media/ directly.

7) Run with Gunicorn (systemd service)

Create a systemd unit to keep Gunicorn running in the background.

nano /etc/systemd/system/gunicorn-djangoapp.service
[Unit]
Description=Gunicorn for Django (djangoapp)
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/opt/djangoapp
Environment="DJANGO_SETTINGS_MODULE=yourproject.settings"
Environment="DJANGO_SECRET_KEY=change_me"
Environment="DJANGO_DEBUG=False"
Environment="DJANGO_ALLOWED_HOSTS=yourdomain.com,YOUR_SERVER_IP"
Environment="PATH=/opt/djangoapp/venv/bin"
ExecStart=/opt/djangoapp/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 yourproject.wsgi:application
Restart=always

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now gunicorn-djangoapp
systemctl status gunicorn-djangoapp

8) Nginx reverse proxy

  1. Create a server block:
    nano /etc/nginx/sites-available/djangoapp
    server {
        listen 80;
        server_name YOUR_DOMAIN_OR_IP;
    
        client_max_body_size 20M;
    
        location /static/ {
            alias /opt/djangoapp/static/;
        }
        location /media/ {
            alias /opt/djangoapp/media/;
        }
    
        location / {
            proxy_pass http://127.0.0.1:8000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
  2. Enable and reload:
    ln -s /etc/nginx/sites-available/djangoapp /etc/nginx/sites-enabled/
    nginx -t && systemctl restart nginx

Open http://YOUR_DOMAIN_OR_IP — your app should be live.

9) (Optional) HTTPS with Let’s Encrypt

  1. Install Certbot:
    apt install -y certbot python3-certbot-nginx
  2. Issue and auto-configure:
    certbot --nginx -d YOUR_DOMAIN -m you@example.com --agree-tos --redirect
  3. Verify auto-renew:
    systemctl list-timers | grep certbot

10) Updating your app (zero-downtime-ish)

  1. Pull code / upload new release:
    cd /opt/djangoapp
    git pull  # or upload new files
  2. Reinstall deps if changed:
    source venv/bin/activate
    pip install -r requirements.txt
  3. Run migrations & collectstatic:
    python manage.py migrate
    python manage.py collectstatic --noinput
  4. Graceful reload:
    systemctl reload nginx
    systemctl restart gunicorn-djangoapp

Troubleshooting

Symptom Likely cause Fix
400 Bad Request from Django ALLOWED_HOSTS missing domain/IP Add domain/IP to ALLOWED_HOSTS and restart Gunicorn.
403/404 for static Static not collected or wrong Nginx alias Set STATIC_ROOT, run collectstatic, verify location /static/ alias path.
502 Bad Gateway Gunicorn down or wrong bind/port Check systemctl status gunicorn-djangoapp, logs, ensure proxy_pass → 127.0.0.1:8000.
Permission denied on static/media Ownership/permissions Set proper owner (e.g., www-data) and safe permissions—see Appendix.
No HTTPS Cert not issued / DNS not pointing Point domain to VPS IP, open 80/443, rerun certbot --nginx.

Appendix: Useful commands & permissions

Service & logs
journalctl -u gunicorn-djangoapp -e
journalctl -u nginx -e
systemctl restart gunicorn-djangoapp nginx
Ownership & permissions (example)
# App directory owned by web user/group
chown -R www-data:www-data /opt/djangoapp

# Directories readable/executable
find /opt/djangoapp -type d -exec chmod 755 {} \;

# Files readable
find /opt/djangoapp -type f -exec chmod 644 {} \;

# Allow manage.py to execute (if needed)
chmod +x /opt/djangoapp/manage.py
Was this answer helpful? 0 Users Found This Useful (0 Votes)