This guide explains how to deploy an existing Django project on a LumaDock VPS using Ubuntu 22.04 LTS. You will upload your code, install dependencies in a virtual environment, configure environment variables, and run Django with Gunicorn behind Nginx. Optional sections cover HTTPS and PostgreSQL.
Firewall note: if you enable the cloud firewall, allow inbound 22/tcp, 80/tcp, and 443/tcp.
Requirements
A complete deployment requires:
A LumaDock VPS running Ubuntu 22.04 LTS
SSH access as root or a sudo-enabled user
A Django project containing manage.py and requirements.txt
A domain pointed to your VPS (recommended for HTTPS)
1) Prepare the server
Connect:
ssh root@YOUR_SERVER_IP
Update the system:
apt update && apt -y upgrade
Install required packages:
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 ssh admin@YOUR_SERVER_IP
2) Upload or clone your project
Create a directory:
mkdir -p /opt/djangoapp
cd /opt/djangoapp
You can now load your project into this folder:
Clone from Git
Upload your local folder via SFTP or rsync
Your manage.py file should end up at:
/opt/djangoapp/manage.py
3) Create a virtualenv and install dependencies
Set up your environment:
cd /opt/djangoapp \
python3 -m venv venv \
source venv/bin/activate \
pip install --upgrade pip \
pip install -r requirements.txt
If your project uses Gunicorn or another WSGI/ASGI server, make sure it is listed in requirements.txt.
4) Configure environment variables and Django settings
For production you should set:
ALLOWED_HOSTS
DEBUG=False
A secure SECRET_KEY stored outside Git
Database configuration if not using SQLite
You can store values in an .env file or load them via django-environ.
Example /opt/djangoapp/.env:
DJANGO_DEBUG=False \
DJANGO_SECRET_KEY=change_me_to_a_strong_secret \
DJANGO_ALLOWED_HOSTS=yourdomain.com,YOUR_SERVER_IP
Update your settings.py to load the environment file or rely on environment variables that will be passed in the systemd unit.
5) Database and migrations
If using SQLite you can skip installation. For PostgreSQL or MySQL, install and configure those first.
Run migrations:
cd /opt/djangoapp \
source venv/bin/activate \
python manage.py migrate
Create a superuser if needed:
python manage.py createsuperuser
6) Static and media files
Example settings:
STATIC_URL = "static/"
STATIC_ROOT = "/opt/djangoapp/static"
MEDIA_URL = "media/"
MEDIA_ROOT = "/opt/djangoapp/media"
Collect files:
python manage.py collectstatic --noinput
These paths will be served directly by Nginx.
7) Run with Gunicorn (systemd service)
Create a systemd unit:
nano /etc/systemd/system/gunicorn-djangoapp.service
Insert:
[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
Enable and start:
systemctl daemon-reload \
systemctl enable --now gunicorn-djangoapp \
systemctl status gunicorn-djangoapp
8) Nginx reverse proxy
Create an Nginx server block:
nano /etc/nginx/sites-available/djangoapp
Paste:
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;
}
}Enable it:
ln -s /etc/nginx/sites-available/djangoapp /etc/nginx/sites-enabled/ \
nginx -t && \
systemctl restart nginx
Visit:
http://YOUR_DOMAIN_OR_IP
9) Optional — HTTPS with Let’s Encrypt
Install Certbot:
apt install -y certbot python3-certbot-nginx
Issue a certificate:
certbot --nginx -d YOUR_DOMAIN -m you@example.com --agree-tos --redirect
Check auto-renew:
systemctl list-timers | grep certbot
10) Updating your app
To update your deployed application:
Pull new code or upload a new release
Reinstall updated dependencies
Apply migrations and collect static files
Reload Nginx and restart Gunicorn
Example:
cd /opt/djangoapp \
git pull \
source venv/bin/activate \
pip install -r requirements.txt \
python manage.py migrate \
python manage.py collectstatic --noinput \
systemctl reload nginx \
systemctl restart gunicorn-djangoapp
Troubleshooting
Common issues include:
400 Bad Request: add your domain/IP to ALLOWED_HOSTS
Static files missing: verify STATIC_ROOT and run collectstatic
502 Bad Gateway: Gunicorn not running or wrong proxy_pass target
Permission errors: adjust ownership for static and media directories
Let’s Encrypt failure: domain not pointing to VPS or port 80 blocked
Appendix: useful commands and permissions
Logs and restarts:
journalctl -u gunicorn-djangoapp -e \
journalctl -u nginx -e \
systemctl restart gunicorn-djangoapp nginx
Permissions:
chown -R www-data:www-data /opt/djangoapp \
find /opt/djangoapp -type d -exec chmod 755 {} \; \
find /opt/djangoapp -type f -exec chmod 644 {} \; \
chmod +x /opt/djangoapp/manage.py
Your existing Django project is now deployed behind Gunicorn and Nginx on your LumaDock VPS. You can continue iterating on your code, automate deployments, or add monitoring as needed.
