Rohan Yeole - Homepage Rohan Yeole

Deploy Django to AWS EC2: Step-by-Step Guide with Nginx, Gunicorn, and SSL

Apr 29, 20261 min read

Deploying Django to AWS EC2 takes about 2 hours if you know the steps. Here is the complete guide — from launching an instance to a running production application with SSL.

Step 1: Launch an EC2 Instance

For most Django applications, start with: - Instance type:t3.small (2 vCPU, 2GB RAM) — handles 50–500 concurrent users comfortably. Upgrade to t3.medium if you run PostgreSQL and Django on the same instance. - AMI: Ubuntu 22.04 LTS - Storage: 20GB SSD (gp3)

Security Group settings: | Type | Port | Source | |------|------|--------| | SSH | 22 | Your IP only | | HTTP | 80 | 0.0.0.0/0 | | HTTPS | 443 | 0.0.0.0/0 |

Never open port 8000 (Gunicorn) or 5432 (PostgreSQL) to the internet — only SSH, 80, and 443 should be reachable publicly.

Create or use an existing key pair for SSH access.

Step 2: Connect and Update the Server

ssh -i your-key.pem ubuntu@your-ec2-ip

# Update packages
sudo apt update && sudo apt upgrade -y

# Install dependencies
sudo apt install -y python3-pip python3-venv python3-dev \
    postgresql postgresql-contrib \
    nginx \
    certbot python3-certbot-nginx \
    git

Step 3: Configure PostgreSQL

sudo -u postgres psql

-- In the psql prompt:
CREATE DATABASE mydb;
CREATE USER myuser WITH PASSWORD 'strong-password-here';
GRANT ALL PRIVILEGES ON DATABASE mydb TO myuser;
\q

Step 4: Deploy Your Django Application

# Create application directory
sudo mkdir -p /srv/myproject
sudo chown ubuntu:ubuntu /srv/myproject

# Clone your repository
cd /srv/myproject
git clone https://github.com/yourusername/myproject.git .

# Create virtual environment
python3 -m venv venv
source venv/bin/activate

# Install dependencies
pip install -r requirements.txt
pip install gunicorn psycopg2-binary

Create the environment file:

sudo nano /srv/myproject/.env
SECRET_KEY=your-production-secret-key
DEBUG=False
ALLOWED_HOSTS=your-domain.com,www.your-domain.com
DATABASE_URL=postgres://myuser:strong-password-here@localhost/mydb
CELERY_BROKER_URL=redis://localhost:6379/0

Run initial setup:

cd /srv/myproject
source venv/bin/activate

python manage.py migrate
python manage.py collectstatic --no-input
python manage.py createsuperuser

Step 5: Configure Gunicorn as a systemd Service

sudo nano /etc/systemd/system/gunicorn.service
[Unit]
Description=Gunicorn Django Application
After=network.target

[Service]
Type=notify
User=ubuntu
Group=ubuntu
WorkingDirectory=/srv/myproject
EnvironmentFile=/srv/myproject/.env
ExecStart=/srv/myproject/venv/bin/gunicorn \
    --workers 3 \
    --worker-class sync \
    --timeout 120 \
    --bind unix:/run/gunicorn/gunicorn.sock \
    --access-logfile /srv/myproject/logs/access.log \
    --error-logfile /srv/myproject/logs/error.log \
    myproject.wsgi:application
RuntimeDirectory=gunicorn
Restart=on-failure

[Install]
WantedBy=multi-user.target

Create the logs directory and start the service:

mkdir -p /srv/myproject/logs
sudo systemctl daemon-reload
sudo systemctl start gunicorn
sudo systemctl enable gunicorn

# Verify it is running
sudo systemctl status gunicorn

Step 6: Configure Nginx

sudo nano /etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name your-domain.com www.your-domain.com;

    client_max_body_size 20M;

    location /static/ {
        alias /srv/myproject/staticfiles/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    location /media/ {
        alias /srv/myproject/media/;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn/gunicorn.sock;
    }
}

Enable and test:

sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

Step 7: SSL with Let's Encrypt

Point your domain's DNS A record to the EC2 public IP, then:

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

Certbot automatically modifies your Nginx config to add HTTPS, redirects HTTP to HTTPS, and sets up certificate auto-renewal via a cron job.

Verify auto-renewal works:

sudo certbot renew --dry-run

Step 8: Celery Worker (if needed)

sudo nano /etc/systemd/system/celery.service
[Unit]
Description=Celery Worker
After=network.target

[Service]
Type=forking
User=ubuntu
WorkingDirectory=/srv/myproject
EnvironmentFile=/srv/myproject/.env
ExecStart=/srv/myproject/venv/bin/celery -A myproject worker \
    --loglevel=info \
    --concurrency=2 \
    --logfile=/srv/myproject/logs/celery.log \
    --pidfile=/run/celery/celery.pid
RuntimeDirectory=celery
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl start celery
sudo systemctl enable celery

Deployment Updates

After pushing code changes:

cd /srv/myproject
git pull origin main
source venv/bin/activate
pip install -r requirements.txt
python manage.py migrate
python manage.py collectstatic --no-input
sudo systemctl restart gunicorn
sudo systemctl restart celery  # If you use Celery

EC2 Cost Reference

InstancevCPURAMMonthly Cost
t3.micro21GB~$8/mo
t3.small22GB~$15/mo
t3.medium24GB~$30/mo
t3.large28GB~$60/mo

Add RDS PostgreSQL (~$15–50/mo) if you want a managed database. Add an Application Load Balancer (~$18/mo) for zero-downtime deployments. A typical small production setup runs $30–80/month.

If you need a Django application deployed to AWS EC2 with Nginx, SSL, Gunicorn, and Celery — configured correctly and maintainable — hire me as an AWS developer or as a Django developer.

Chat with me on WhatsApp