Django 5
Overview
Django is the leading open-source Python web framework. It includes an ORM, an admin UI, authentication, forms, migrations and REST integrations.
The SecureAnyCloud image installs Django 5.2.13 (the current LTS, supported through April 2028) into a Python 3.12 virtualenv. gunicorn 23 (the WSGI app server) and nginx (the reverse proxy and static-file server) front it, and PostgreSQL 16 backs it. A SecureAnyCloud starter project lives at /opt/django/sac with a core app. A Django superuser is created at first boot, so the admin UI is reachable as soon as the VM finishes booting.
What you get:
- Django 5.2.13 LTS (BSD 3-Clause) in a Python 3.12 virtualenv at
/opt/django/venv - gunicorn 23 binding to
/run/gunicorn/sac.sock(3 workers by default) - nginx reverse proxy on TCP
:80(proxy_pass to the gunicorn socket, serving static files at/static/) - PostgreSQL 16 from Ubuntu noble main, with a per-VM SecureAnyCloud role and a SecureAnyCloud database
- psycopg 3.2 (pool + binary)
- SecureAnyCloud starter project at /opt/django/sac with a core app
- A per-VM
SECRET_KEY(50 characters), database password (32 hex characters) and Django superuser password (32 hex characters), all rotated at first boot - Systemd units
gunicorn.serviceanddjango-firstboot.service, plus the standardnginx.serviceandpostgresql.service - 24/7 SecureAnyCloud support
Prerequisites
You need an active Azure subscription, an SSH key, and a VNet and subnet.
Standard_B2s (4 GB RAM) is comfortable for a 3-worker gunicorn plus Postgres.
Configure the NSG to allow inbound 22/tcp from your management CIDR and 80/tcp from any client CIDR that needs the Django UI.
Step 1-3: Deploy and connect over SSH
ssh azureuser@<vm-ip>Check service status and versions
sudo systemctl is-active gunicorn.service nginx.service postgresql.service /opt/django/venv/bin/python -c "import django; print('Django', django.get_version())"Read the per-VM credentials
sudo cat /stage/scripts/django-credentials.logNote DJANGO_URL, DJANGO_ADMIN_URL, DJANGO_ADMIN_USER and DJANGO_ADMIN_PASSWORD. The file also contains the database password and the per-VM SECRET_KEY.
Check HTTP health and static files
curl -sf -o /dev/null -w 'admin login: HTTP %{http_code}\n' http://127.0.0.1/admin/login/
curl -sf -o /dev/null -w 'static base.css: HTTP %{http_code}\n' http://127.0.0.1/static/admin/css/base.cssThe first call reaches gunicorn through nginx. The second call proves that nginx serves collected static files directly, which is faster than going through Django.
Django admin: sign in
Browse to http://<vm-ip>/admin/ and sign in as SecureAnyCloud with the password from Step 5.
Django admin: site administration
After you sign in, the Site Administration page shows the auto-discovered admin models. These are Authentication and Authorization (Users, Groups) and any apps you add to INSTALLED_APPS.
Django admin: users list
Click Users to see the SecureAnyCloud superuser. Add more users from this page, or run manage.py createsuperuser.
Run Django management commands
set -a; . /etc/django/sac.env; export DJANGO_SETTINGS_MODULE=sac.settings; set +a cd /opt/django/sac
sudo -E -u django /opt/django/venv/bin/python manage.py migrate --noinput | tail -3 sudo -E -u django /opt/django/venv/bin/python manage.py showmigrations --plan | tail -10The first command is idempotent, so already-applied migrations are no-ops. The second command prints the migration plan ordering.
Add a Django app
Replace <app-name> with the name of your app:
cd /opt/django/sac
sudo -u django /opt/django/venv/bin/python manage.py startapp <app-name>Edit sac/settings.py to add '<app-name>' to INSTALLED_APPS, and define your models in <app-name>/models.py. Then run makemigrations <app-name> && migrate.
Tune the gunicorn workers
Edit /etc/django/sac.env to raise GUNICORN_WORKERS, and then restart gunicorn:
sudo systemctl restart gunicorn.serviceDefaults on Standard_B2s:
- GUNICORN_WORKERS: 3
- DJANGO_DEBUG: False
DJANGO_ALLOWED_HOSTS:localhost,127.0.0.1,<public-ip>- DB: PostgreSQL 16, role SecureAnyCloud, db SecureAnyCloud
For production raise workers to 2 × CPU + 1 (e.g. 9 on a D4s_v5).
Add your domain to ALLOWED_HOSTS
Edit /etc/django/sac.env and add your domain to the comma-separated list:
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1,<vm-ip>,apps.example.comThen restart gunicorn:
sudo systemctl restart gunicorn.serviceInstall a Let's Encrypt certificate
Point the DNS A record of your domain at this VM, and then run:
sudo apt-get install -y python3-certbot-nginx
sudo certbot --nginx -d apps.example.com -m you@example.com --agree-tos --non-interactive --redirectCertbot edits /etc/nginx/sites-available/django-sac to add the SSL server block and a :80 → :443 redirect, and then reloads nginx. The certbot.timer systemd unit handles renewal and runs twice daily.
Backups
Take a consistent backup while the services keep running. Dump the database with pg_dump, and archive the media/ upload directory:
PASS=$(sudo grep '^DJANGO_DB_PASSWORD=' /stage/scripts/django-credentials.log | cut -d= -f2-) PGPASSWORD="$PASS" pg_dump -h 127.0.0.1 -U sac sac | gzip > /var/backups/sac-db-$(date +%F).sql.gz
sudo tar czf /var/backups/sac-media-$(date +%F).tgz -C /opt/django/sac mediaCopy /var/backups to Azure Blob Storage (az storage blob upload-batch) periodically for off-VM retention.
Read logs and troubleshoot
sudo journalctl -u gunicorn.service --no-pager -n 80
sudo journalctl -u nginx.service --no-pager -n 30
sudo journalctl -u postgresql.service --no-pager -n 30
sudo tail -50 /var/log/nginx/access.log
sudo tail -50 /var/log/nginx/error.logDjango app errors are surfaced through gunicorn's --access-logfile - and --error-logfile -, which both go to the systemd journal. For more verbose logs, set DJANGO_DEBUG=true in /etc/django/sac.env. Use this setting for development only.
Security
DEBUG=Falseby default, so the image is production-safe out of the boxALLOWED_HOSTSauto-populated with the public IP at first bootCSRF_TRUSTED_ORIGINSderived fromALLOWED_HOSTS- Per-VM
SECRET_KEY(50 chars fromopenssl rand) - gunicorn binds to a Unix socket (not exposed to the network)
- PostgreSQL listens on
127.0.0.1:5432(loopback only) - Per-VM database password (32 hex characters)
- Per-VM Django superuser with a 32-hex-character password
- Restrict NSG inbound on
:80to the IP ranges that need the Django app - Install
python3-certbot-nginxfor Let's Encrypt automation (Step 14)
Support
SecureAnyCloud provides 24/7/365 expert technical support. Guaranteed response within 24 hours, one hour average for critical issues. Contact sales@secureanycloud.com.
Deploy on Azure
Launch Django 5.2 LTS on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com