Skip to main content
Deploy LangShazam to AWS EC2 with automated infrastructure provisioning using CloudFormation, Docker Compose orchestration, and Nginx reverse proxy.

Overview

The EC2 deployment provides:
  • Automated infrastructure with CloudFormation
  • VPC, subnet, and security group configuration
  • Docker Compose for container orchestration
  • Nginx reverse proxy with SSL support
  • Auto-restart and health monitoring

Architecture

┌───────────────────────────────────────────────────┐
│                    Internet                         │
└───────────────────────┬───────────────────────────┘

                       │ Port 80/443

    ┌──────────────────▼───────────────────────────────┐
    │              EC2 Instance                         │
    │  ┌─────────────────────────────────────────┐  │
    │  │      Nginx Reverse Proxy              │  │
    │  │  - SSL/TLS termination                │  │
    │  │  - WebSocket support                  │  │
    │  └────────────────┬─────────────────────────┘  │
    │                 │                                  │
    │  ┌───────────────▼─────────────────────────┐  │
    │  │   LangShazam Container (Port 10000)   │  │
    │  │  - FastAPI application                │  │
    │  │  - WebSocket handler                  │  │
    │  │  - Health checks                      │  │
    │  └─────────────────────────────────────────┘  │
    └───────────────────────────────────────────────────┘

Prerequisites

AWS Account

With EC2 and CloudFormation permissions

AWS CLI

Installed and configured

SSH Key Pair

EC2 key pair in your AWS region

OpenAI API Key

Valid API key with Whisper access

Deployment Files

Located in backend/deployment/ec2/:
  • ec2-config.yaml - CloudFormation template
  • docker-compose.yml - Container orchestration
  • nginx/conf/language-detector.conf - Nginx configuration

Quick Start

1

Create CloudFormation Stack

aws cloudformation create-stack \
  --stack-name language-detector \
  --template-body file://backend/deployment/ec2/ec2-config.yaml \
  --parameters ParameterKey=KeyName,ParameterValue=YOUR_KEY_NAME \
               ParameterKey=EnvironmentName,ParameterValue=prod \
               ParameterKey=InstanceType,ParameterValue=t2.micro
Replace YOUR_KEY_NAME with your EC2 key pair name.
2

Wait for Stack Creation

aws cloudformation wait stack-create-complete --stack-name language-detector
This takes approximately 5-10 minutes.
3

Get EC2 Public IP

EC2_IP=$(aws cloudformation describe-stacks \
  --stack-name language-detector \
  --query "Stacks[0].Outputs[?OutputKey=='PublicIP'].OutputValue" \
  --output text)

echo "EC2 instance created with IP: $EC2_IP"
4

SSH into Instance

ssh -i YOUR_KEY_NAME.pem ec2-user@$EC2_IP
5

Clone Repository and Deploy

# Clone your repository
git clone https://github.com/your-username/langshazam.git
cd langshazam/backend/deployment/ec2

# Set OpenAI API key
export OPENAI_API_KEY=your_api_key_here

# Start with Docker Compose
docker-compose up -d
6

Verify Deployment

# Check containers
docker-compose ps

# Test locally
curl http://localhost/

# Test from your machine
curl http://$EC2_IP/

CloudFormation Template

Parameters

From ec2/ec2-config.yaml:4-27:
Parameters:
  EnvironmentName:
    Description: Environment name
    Type: String
    Default: dev
    AllowedValues: [dev, staging, prod]
  
  InstanceType:
    Description: EC2 instance type
    Type: String
    Default: t2.micro
    AllowedValues: [t2.micro, t2.small, t2.medium, t3.micro, t3.small, t3.medium]
  
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: Must be the name of an existing EC2 KeyPair

  SSHLocation:
    Description: IP address range that can SSH to the EC2 instance
    Type: String
    Default: 0.0.0.0/0
For production, restrict SSHLocation to your IP address instead of 0.0.0.0/0.

Security Group

From ec2/ec2-config.yaml:86-107:
AppSecurityGroup:
  Type: AWS::EC2::SecurityGroup
  Properties:
    GroupDescription: Security group for Language Detector application
    VpcId: !Ref VPC
    SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 22
        ToPort: 22
        CidrIp: !Ref SSHLocation
      - IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        CidrIp: 0.0.0.0/0
      - IpProtocol: tcp
        FromPort: 443
        ToPort: 443
        CidrIp: 0.0.0.0/0
      - IpProtocol: tcp
        FromPort: 10000
        ToPort: 10000
        CidrIp: 0.0.0.0/0
Ports opened:
  • 22: SSH access
  • 80: HTTP traffic
  • 443: HTTPS traffic
  • 10000: Direct application access (optional)

User Data Script

The CloudFormation template automatically installs (from ec2/ec2-config.yaml:118-138):
#!/bin/bash -xe
# Update system packages
yum update -y

# Install Docker
amazon-linux-extras install docker -y
systemctl start docker
systemctl enable docker
usermod -a -G docker ec2-user

# Install Docker Compose
curl -L "https://github.com/docker/compose/releases/download/v2.24.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

# Install useful tools
yum install -y git jq

Docker Compose Configuration

From ec2/docker-compose.yml:
version: '3.8'

services:
  language-detector:
    build: 
      context: ../../
      dockerfile: deployment/docker/Dockerfile
    container_name: language-detector
    restart: always
    ports:
      - "10000:10000"
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - LOGGING_LEVEL=INFO
      - MAX_CONNECTIONS=100
      - MAX_AUDIO_SIZE_MB=5
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:10000/"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 5s
    volumes:
      - app_logs:/app/logs

  nginx:
    image: nginx:alpine
    container_name: nginx
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf:/etc/nginx/conf.d
      - ./nginx/ssl:/etc/nginx/ssl
      - ./nginx/www:/var/www/html
    depends_on:
      - language-detector

volumes:
  app_logs:
Key features:
  • Restart policy: Containers auto-restart on failure
  • Health checks: Automatic monitoring
  • Persistent logs: Stored in Docker volume
  • Nginx: Reverse proxy with SSL support

Nginx Configuration

From ec2/nginx/conf/language-detector.conf:
server {
    listen 80;
    server_name _;

    access_log /var/log/nginx/language-detector.access.log;
    error_log /var/log/nginx/language-detector.error.log;

    location / {
        proxy_pass http://language-detector:10000;
        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;
        
        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
Features:
  • WebSocket support: Critical for real-time language detection
  • Proxy headers: Preserve client information
  • Logging: Access and error logs

SSL Setup with Let’s Encrypt

1

Install Certbot

sudo amazon-linux-extras install epel -y
sudo yum install certbot -y
2

Stop Nginx Temporarily

cd ~/langshazam/backend/deployment/ec2
docker-compose stop nginx
3

Obtain Certificate

sudo certbot certonly --standalone \
  -d your-domain.com \
  --email [email protected] \
  --agree-tos -n
4

Copy Certificates

sudo cp /etc/letsencrypt/live/your-domain.com/fullchain.pem \
  ~/langshazam/backend/deployment/ec2/nginx/ssl/
sudo cp /etc/letsencrypt/live/your-domain.com/privkey.pem \
  ~/langshazam/backend/deployment/ec2/nginx/ssl/

sudo chown ec2-user:ec2-user ~/langshazam/backend/deployment/ec2/nginx/ssl/*.pem
5

Update Nginx Configuration

Edit nginx/conf/language-detector.conf and uncomment the SSL server block (lines 24-49), replacing YOUR_DOMAIN with your actual domain.
6

Restart Nginx

docker-compose start nginx

Auto-Renewal

Set up automatic certificate renewal:
# Test renewal
sudo certbot renew --dry-run

# Add to crontab
sudo crontab -e

# Add this line to renew daily at 2am
0 2 * * * certbot renew --quiet --post-hook "cd /home/ec2-user/langshazam/backend/deployment/ec2 && docker-compose restart nginx"

Management

View Logs

cd ~/langshazam/backend/deployment/ec2

# All containers
docker-compose logs

# Specific service
docker-compose logs language-detector
docker-compose logs nginx

# Follow logs
docker-compose logs -f

Restart Services

# Restart all
docker-compose restart

# Restart specific service
docker-compose restart language-detector
docker-compose restart nginx

Update Application

cd ~/langshazam

# Pull latest code
git pull

# Rebuild and restart
cd backend/deployment/ec2
docker-compose down
docker-compose build
docker-compose up -d

Monitor Resources

# Container stats
docker stats

# System resources
top
htop  # If installed

# Disk usage
df -h
du -sh ~/langshazam

Backup and Restore

Backup

# Create backup directory
mkdir -p ~/backups

# Backup logs
docker run --rm \
  -v language-detector_app_logs:/data \
  -v ~/backups:/backup \
  alpine tar -czvf /backup/app_logs_$(date +%Y%m%d).tar.gz /data

# Backup Nginx configuration
cp -r ~/langshazam/backend/deployment/ec2/nginx ~/backups/nginx_$(date +%Y%m%d)

# Backup to S3 (optional)
aws s3 cp ~/backups s3://your-backup-bucket/langshazam/ --recursive

Restore

# Restore logs
docker run --rm \
  -v language-detector_app_logs:/data \
  -v ~/backups:/backup \
  alpine sh -c "rm -rf /data/* && tar -xzvf /backup/app_logs_20260308.tar.gz -C /"

# Restore Nginx config
cp -r ~/backups/nginx_20260308/* ~/langshazam/backend/deployment/ec2/nginx/

# Restart services
docker-compose restart

CloudFormation Outputs

After stack creation, retrieve outputs:
aws cloudformation describe-stacks \
  --stack-name language-detector \
  --query 'Stacks[0].Outputs'
Available outputs:
  • VPC: VPC ID
  • PublicSubnet: Subnet ID
  • SecurityGroup: Security Group ID
  • PublicIP: EC2 instance public IP
  • PublicDNS: EC2 instance public DNS

Troubleshooting

Cannot SSH into Instance

# Check security group allows your IP
aws ec2 describe-security-groups \
  --group-ids sg-xxxxx \
  --query 'SecurityGroups[0].IpPermissions'

# Verify key permissions
chmod 400 YOUR_KEY_NAME.pem

Containers Not Starting

# Check Docker daemon
sudo systemctl status docker

# Check logs
docker-compose logs

# Rebuild
docker-compose down
docker-compose build --no-cache
docker-compose up -d

Application Not Accessible

# Check containers are running
docker-compose ps

# Check if ports are listening
sudo netstat -tulpn | grep -E '(80|443|10000)'

# Test locally
curl http://localhost/

# Check security group allows traffic
aws ec2 describe-security-groups --group-ids sg-xxxxx

High Costs

Cost optimization tips:
  • Use t3.micro instead of t2.micro (better performance, similar cost)
  • Stop instance when not in use (dev/staging only)
  • Use AWS Free Tier eligible t2.micro for first year
  • Monitor with AWS Cost Explorer

Cleanup

Delete Stack

# Delete CloudFormation stack (removes all resources)
aws cloudformation delete-stack --stack-name language-detector

# Wait for deletion
aws cloudformation wait stack-delete-complete --stack-name language-detector
This permanently deletes the EC2 instance, VPC, security groups, and all data.

Partial Cleanup

# SSH into instance and stop containers
ssh -i YOUR_KEY_NAME.pem ec2-user@$EC2_IP
cd ~/langshazam/backend/deployment/ec2
docker-compose down -v  # -v removes volumes

# Then delete stack as above

Next Steps

Environment Variables

Configure application settings

CORS Setup

Allow your frontend domain

Build docs developers (and LLMs) love