Django
Overview
Django is a high-level Python web framework for building database-backed applications. This image runs Django 5.2 LTS in an isolated Python 3.12 virtual environment, served by gunicorn behind nginx, with PostgreSQL 16 on the same instance. A SecureAnyCloud starter project is preinstalled at /opt/django/sac, ready for you to customize or replace with your own application.
What you get
- Django 5.2 LTS in an isolated Python 3.12 virtual environment, running behind gunicorn and nginx.
- PostgreSQL 16 on the same instance.
- Dedicated, independently resizable EBS volumes for the Django application code and for the PostgreSQL data directory.
- A starter Django project at /opt/django/sac.
- Secrets generated on the first boot of every instance: the Django
SECRET_KEY, the PostgreSQL role password and a Django admin superuser. Two instances launched from the same Amazon Machine Image never share secrets. - Credentials written to
/stage/scripts/django-credentials.logwith mode 0600, so that only root can read them. ALLOWED_HOSTSpopulated on first boot with the instance public IPv4 address, retrieved from the Amazon EC2 Instance Metadata Service (IMDSv2).- 24/7 SecureAnyCloud support.
If you deploy behind an Application Load Balancer or a custom domain, add the public hostname to DJANGO_ALLOWED_HOSTS in /etc/django/sac.env and restart gunicorn.
Prerequisites
Before you deploy, make sure you have:
- An Amazon Web Services account where you can launch EC2 instances.
- IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products.
- An EC2 key pair in the target Region for SSH access to the instance.
- A VPC and subnet in the target Region, with a security group that allows inbound port 22 from your management network and inbound port 80 from the networks your visitors will use.
- The AWS CLI (version 2) installed locally, if you plan to deploy from the command line.
Launch the instance from the AWS Marketplace
Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images, choose AWS Marketplace AMIs and search for Django by SecureAnyCloud. Select the SecureAnyCloud listing and choose Select, then Continue on the subscription summary.
Pick an instance type of m5.large or larger for production workloads. Choose your EC2 key pair under Key pair (login).
Under Network settings, select your VPC and subnet. Create or select a security group that allows inbound port 22 from your management network and inbound port 80 from the networks your visitors use. Leave the root volume at the default size and let the dedicated PostgreSQL and Django application volumes attach as configured by the AMI.
Select Launch instance. First-boot initialisation takes approximately one minute after the instance state becomes Running and the status checks pass.
Launch the instance from the AWS CLI
Run the following command to launch an instance from the SecureAnyCloud Django Marketplace AMI into an existing subnet and security group. Replace <ami-id> with the AMI ID shown on the Marketplace listing and <key-name> with your EC2 key pair name. Replace <subnet-id> with your subnet ID, and <security-group-id> with a security group that opens ports 22 and 80 as described above.
aws ec2 run-instances \
--image-id <ami-id> \
--instance-type m5.large \
--key-name <key-name> \
--subnet-id <subnet-id> \
--security-group-ids <security-group-id> \
--block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":12,"VolumeType":"gp3"}},{"DeviceName":"/dev/sdf","Ebs":{"VolumeSize":20,"VolumeType":"gp3"}},{"DeviceName":"/dev/sdg","Ebs":{"VolumeSize":10,"VolumeType":"gp3"}}]' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=django-app-01}]'On success, the command prints a JSON document. Note the instance ID. Once the instance is running, retrieve its public address withaws ec2 describe-instances --instance-ids <instance-id> --query "Reservations[].Instances[].PublicIpAddress" --output text.
Connect and retrieve the initial credentials
Connect over SSH with the key pair you selected and the public IP address from Step 2. The SSH login user depends on the operating system of the AMI variant you launched. For the Django on Ubuntu 24.04 variant, the login user is ubuntu.
ssh -i <path-to-key>.pem ubuntu@<public-ip>The first boot generates the Django admin superuser credentials and the PostgreSQL role password. It writes them to /stage/scripts/django-credentials.log with mode 0600. Read the file with sudo:
sudo cat /stage/scripts/django-credentials.logThe output looks like this:
ubuntu@ip-172-31-84-135:~$
sudo cat /stage/scripts/django-credentials.log
# Django 5.2 LTS - Per-VM Credentials
# Generated: Sat May 23 07:49:07 UTC 2026 DJANGO_URL=http://34.205.72.255/ DJANGO_ADMIN_URL=http://34.205.72.255/admin/ DJANGO_ADMIN_USER=sac DJANGO_ADMIN_EMAIL=sac@localhost DJANGO_ADMIN_PASSWORD=<set-at-first-boot> DJANGO_DB_NAME=sac DJANGO_DB_USER=sac DJANGO_DB_PASSWORD=<set-at-first-boot> DJANGO_SECRET_KEY=Q3hVc7Tcg1n6IxYWXLLZ8X2T1xRrIVKK6Q92eGmZWDRgypIIDQStore these credentials in your password manager. The values above are illustrative. Your instance generates its own.
Verify that Django is serving
Open http://<public-ip>/ in a web browser. The SecureAnyCloud starter Django welcome page confirms that gunicorn, nginx and PostgreSQL are running.
Next, open http://<public-ip>/admin/login/ to reach the Django admin login form.
Sign in with the DJANGO_ADMIN_USER and DJANGO_ADMIN_PASSWORD from /stage/scripts/django-credentials.log. The Django admin dashboard then lists the built-in Authentication and Authorization models.
You can also verify Django from the command line:
/opt/django/venv/bin/python -c "import django; print(django.get_version())"The output looks like this:
ubuntu@ip-172-31-84-135:~$ /opt/django/venv/bin/python -c "import django; print(django.get_version())" 5.2.13Customize or replace the starter project
The SecureAnyCloud starter Django project lives at /opt/django/sac and is owned by the django system user. It uses the standard django-admin startproject layout, with a core app added so that the admin index is populated. List the project directory:
sudo ls /opt/django/sacubuntu@ip-172-31-84-135:~$
sudo ls /opt/django/sac sac core db.sqlite3 manage.py media staticTo use your own application, place it in the same directory and re-run migrations. The recommended workflow is:
# 1. Stop gunicorn so your app can write to the project tree
sudo systemctl stop gunicorn
# 2. Replace or update the project under /opt/django/sac
# The settings.py SECRET_KEY, ALLOWED_HOSTS and DATABASE values are read
# from environment variables — keep that pattern in your settings module.
# 3. Re-run migrations + collect static, owned by the django user
sudo -u django -E /opt/django/venv/bin/python /opt/django/sac/manage.py migrate
sudo -u django -E /opt/django/venv/bin/python /opt/django/sac/manage.py collectstatic --noinput
# 4. Restart gunicorn
sudo systemctl restart gunicornThe gunicorn unit file is at /etc/systemd/system/gunicorn.service. It loads every environment variable Django needs from /etc/django/sac.env (mode 0640, group django).
Add your domain to ALLOWED_HOSTS
On first boot, DJANGO_ALLOWED_HOSTS is set to localhost,127.0.0.1,<public-ip>. To serve the site on a custom domain, edit /etc/django/sac.env, append your hostname, and restart gunicorn:
sudo nano /etc/django/sac.env
# Set: DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1,<public-ip>,django.example.com
sudo systemctl restart gunicornsettings.py derives CSRF_TRUSTED_ORIGINS from ALLOWED_HOSTS. Adding the hostname to DJANGO_ALLOWED_HOSTS therefore trusts both the http:// and https:// origins for that name.
Increase gunicorn workers for higher concurrency
The default gunicorn worker count is 3, which suits an m5.large. For larger instance types or higher request rates, raise GUNICORN_WORKERS in /etc/django/sac.env:
sudo sed -i 's|^GUNICORN_WORKERS=.*|GUNICORN_WORKERS=8|' /etc/django/sac.env
sudo systemctl restart gunicornA reasonable starting point is (2 * vCPU) + 1 workers. Tune from there based on your application's I/O profile.
Enable HTTPS
The image ships without TLS. There are two recommended patterns.
Terminate TLS at an AWS load balancer. Put an Application Load Balancer in front of the instance, attach an ACM certificate, listen on 443, and forward to the instance on port 80. The load balancer sends X-Forwarded-Proto: https, which the bundled nginx site passes through to gunicorn. Django then generates https:// URLs.
Use certbot for Let's Encrypt directly on the instance. Install certbot and the nginx plugin, then request a certificate:
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d django.example.comcertbot edits the nginx vhost in place so that it listens on 443 with the issued certificate. It also sets up automatic renewal through a systemd timer.
PostgreSQL operations
The PostgreSQL data directory lives on the dedicated EBS volume mounted at /var/lib/postgresql. To take an application-consistent snapshot, stop gunicorn briefly, snapshot the EBS volume, then resume:
sudo systemctl stop gunicorn
# Issue:
aws ec2 create-snapshot --volume-id <db-volume-id>
sudo systemctl start gunicornTo connect to the database as the postgres superuser:
sudo -u postgres psql -d sacTo connect as the SecureAnyCloud role with its rotated password:
sudo grep ^DJANGO_DB_PASSWORD= /etc/django/sac.env | cut -d= -f2- | sudo PGPASSWORD="$(cat)" psql -h 127.0.0.1 -U sac sacYou can resize the PostgreSQL data volume in the AWS Console or CLI without service interruption. Then grow the filesystem with sudo resize2fs $(findmnt -no SOURCE /var/lib/postgresql).
Routine maintenance
- OS updates: run
sudo apt-get update && sudo apt-get -y dist-upgrade. Reboot if the kernel changed. - Django and dependency upgrades: Django 5.2 LTS is supported through April 2028. To upgrade dependencies inside the starter project's virtualenv, run: sudo -u django /opt/django/venv/bin/pip install --upgrade Django==5.2.<minor> gunicorn psycopg, then sudo systemctl restart gunicorn.
- PostgreSQL upgrades: PostgreSQL 16 is the bundled major version. Major version upgrades require
pg_upgradeand a fresh data directory. The recommended path is to snapshot the database, provision a new instance, restore, and cut over. - Backups: schedule EBS snapshots of the
/var/lib/postgresqldata volume and the/opt/djangoapplication volume independently. The OS root volume is reproducible from the AMI itself and does not require backup.
Screenshots
The SecureAnyCloud starter Django project welcome page, served by gunicorn behind nginx on port 80.
The Django admin login form at /admin/, for the per-instance superuser created at first boot.
The Django admin dashboard after signing in with the rotated SecureAnyCloud superuser credentials.
Support
SecureAnyCloud provides 24/7 technical support for this image by email and chat. Contact support for help with Django deployment, gunicorn and nginx tuning, PostgreSQL configuration, TLS termination and framework upgrades.
Deploy on AWS
Launch Django on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com