← Back to DevBytes

How to Deploy a Django Application on AWS EC2

What Is AWS EC2 and Why Deploy Django There?

Amazon Elastic Compute Cloud (EC2) is a core AWS service that provides resizable virtual servers in the cloud. You launch an EC2 instance, choose an operating system (like Ubuntu or Amazon Linux), and gain full root access to configure it exactly as your application requires. For Django developers, deploying on EC2 means you retain complete control over the server environment, can integrate tightly with other AWS services (RDS, S3, CloudWatch), and can scale vertically or horizontally as traffic grows. Unlike Platform-as-a-Service offerings, EC2 does not abstract away the server layer — you own the stack from the operating system up to the application, which is ideal when you need custom middleware, specific Python versions, or fine‑grained performance tuning.

What This Tutorial Covers

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

You will learn how to launch an Ubuntu EC2 instance, install Python and a virtual environment, clone a Django project, configure Gunicorn as the application server, set up Nginx as a reverse proxy, manage the process with systemd, secure the server with a firewall and optional SSL, and adopt production‑ready best practices. Every step includes concrete commands and configuration files that you can copy and adapt.

Prerequisites

Step 1: Launch an Ubuntu EC2 Instance

Log into the AWS Management Console, navigate to EC2, and click Launch Instance. Choose the following:

Launch the instance. Once it shows “running”, note its public IPv4 address and public IPv4 DNS.

Step 2: Connect to the Instance via SSH

Open a terminal and use the key pair you downloaded. Replace your-key.pem and the IP address.

chmod 400 your-key.pem
ssh -i your-key.pem ubuntu@<instance-public-ip>

The default username for Ubuntu AMIs is ubuntu. After login, you should see a welcome banner.

Step 3: Update the System and Install Python Dependencies

First, refresh the package index and install Python, pip, and other build tools.

sudo apt update && sudo apt upgrade -y
sudo apt install -y python3-pip python3-dev python3-venv libpq-dev curl git

If your Django project uses PostgreSQL (recommended), install the PostgreSQL client library as shown (libpq-dev). For MySQL, use libmysqlclient-dev.

Step 4: Create a Virtual Environment and Clone the Project

We’ll place the application in /home/ubuntu/app. Create the directory and set up a Python virtual environment.

mkdir -p /home/ubuntu/app
cd /home/ubuntu/app
python3 -m venv venv
source venv/bin/activate

Now clone your Django project from GitHub (or copy the source).

git clone https://github.com/your-username/your-django-project.git .

Install the project dependencies inside the virtual environment.

pip install -r requirements.txt

If you haven’t pinned versions, run pip freeze > requirements.txt after a successful local test.

Step 5: Configure Django Settings for Production

Create a separate production settings file or use environment variables. A minimal safe production configuration:

# settings/production.py or settings.py with condition
DEBUG = False
ALLOWED_HOSTS = ['your-domain.com', '<instance-ip>', 'localhost']

# Security
SECURE_SSL_REDIRECT = True  # once HTTPS is set
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000  # 1 year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

Never run with DEBUG = True in production. Store the SECRET_KEY and database credentials in environment variables (see best practices).

Step 6: Install and Test Gunicorn

Gunicorn is a production‑grade WSGI HTTP server for Python. Install it inside the virtual environment.

pip install gunicorn

Test that Gunicorn can serve your Django app locally. Run it from the project root (where manage.py resides):

gunicorn --bind 0.0.0.0:8000 your_project_name.wsgi:application

Replace your_project_name with the actual Django project name. Open a browser to http://<instance-ip>:8000 (you may need to temporarily open port 8000 in the security group). If you see your site, stop Gunicorn (Ctrl+C).

Step 7: Configure Gunicorn as a Systemd Service

We want Gunicorn to start on boot and restart if it crashes. Create a systemd unit file.

sudo nano /etc/systemd/system/gunicorn.service

Add the following content, adjusting paths and the project name:

[Unit]
Description=Gunicorn daemon for Django project
After=network.target

[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/app
EnvironmentFile=/home/ubuntu/app/.env
ExecStart=/home/ubuntu/app/venv/bin/gunicorn \
    --workers 3 \
    --bind unix:/home/ubuntu/app/gunicorn.sock \
    your_project_name.wsgi:application
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Key details:

Create the .env file (secure it with chmod 600 .env):

SECRET_KEY=your-secure-random-key
DB_NAME=your_db_name
DB_USER=your_db_user
DB_PASSWORD=your_db_password
DB_HOST=your_db_host (e.g., RDS endpoint)

Enable and start the service:

sudo systemctl daemon-reload
sudo systemctl enable gunicorn
sudo systemctl start gunicorn
sudo systemctl status gunicorn

The socket file gunicorn.sock should appear in the app directory.

Step 8: Install and Configure Nginx

Nginx will act as a reverse proxy, handling client requests and forwarding them to Gunicorn via the Unix socket. It also serves static and media files efficiently.

sudo apt install -y nginx

Create a site configuration:

sudo nano /etc/nginx/sites-available/your_project

Paste the configuration below, substituting your domain or IP.

server {
    listen 80;
    server_name your-domain.com <instance-ip>;

    location = /favicon.ico { access_log off; log_not_found off; }

    location /static/ {
        alias /home/ubuntu/app/staticfiles/;
    }

    location /media/ {
        alias /home/ubuntu/app/media/;
    }

    location / {
        proxy_pass http://unix:/home/ubuntu/app/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;
    }
}

Enable the site and test the configuration:

sudo ln -s /etc/nginx/sites-available/your_project /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default   # optional, remove default catch-all
sudo nginx -t
sudo systemctl restart nginx

Now visit your instance’s public IP or domain (HTTP). Django should respond via Nginx.

Step 9: Update AWS Security Group (Firewall)

Go back to the EC2 console → Security Groups. Edit the inbound rules for your instance’s security group and add:

Only allow SSH (port 22) from your specific IP for security.

Step 10: Obtain an SSL Certificate (Let’s Encrypt)

For production, HTTPS is mandatory. Install Certbot and obtain a free certificate.

sudo apt install -y certbot python3-certbot-nginx

Ensure your domain’s DNS A record points to the instance’s public IP. Then run:

sudo certbot --nginx -d your-domain.com

Follow the prompts. Certbot will modify the Nginx config to enable HTTPS and set up automatic renewal. Test renewal with:

sudo certbot renew --dry-run

Now your site is accessible securely via https://your-domain.com.

Step 11: Collect Static Files and Configure Storage

Django serves static files from a single directory when DEBUG=False. Collect them into the directory defined in STATIC_ROOT.

python manage.py collectstatic --noinput

For media (user uploads), consider using AWS S3 with django-storages to avoid storing files on the instance file system, which isn’t persistent across instance replacements. However, for a single-instance setup, you can keep them locally with regular backups.

Step 12: Set Up a Managed Database (RDS – Optional but Recommended)

Instead of running a database on the same instance, use Amazon RDS for PostgreSQL (or MySQL). It provides automated backups, snapshots, and scaling. Create an RDS instance in the same VPC, note the endpoint, and update your .env file with the credentials. Ensure the EC2 security group allows inbound traffic from the RDS security group (and vice versa) on the database port (e.g., 5432).

In your Django settings, use:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ.get('DB_NAME'),
        'USER': os.environ.get('DB_USER'),
        'PASSWORD': os.environ.get('DB_PASSWORD'),
        'HOST': os.environ.get('DB_HOST'),
        'PORT': '5432',
    }
}

Best Practices for Django on AWS EC2

Conclusion

Deploying a Django application on AWS EC2 gives you complete control, deep AWS integration, and a path to scale as your traffic grows. By following the steps above, you’ve launched a secure, production‑ready instance with Gunicorn, Nginx, systemd supervision, and optional SSL. Combine these practices with managed databases, offloaded static files, and robust monitoring, and your Django app will be ready to serve users reliably. As a next step, consider automating the entire setup with Terraform or AWS CloudFormation, and explore containerized deployments via Amazon ECS or EKS if you move toward microservices. Happy deploying!

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles