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
| Instance | vCPU | RAM | Monthly Cost |
|---|---|---|---|
| t3.micro | 2 | 1GB | ~$8/mo |
| t3.small | 2 | 2GB | ~$15/mo |
| t3.medium | 2 | 4GB | ~$30/mo |
| t3.large | 2 | 8GB | ~$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.