Stage EC2 Deployment Guide
Overview
Stage backend deployment on Amazon Linux EC2 instance.
- Domain:
stage-backend.oktivo.com - IP:
13.235.46.242 - Region:
ap-south-1 - Services: client_backend, analysis, agent, valkey
Architecture
Internet → Nginx (host) → Docker containers
├── /client → cb-client-backend (port 3000)
└── /analysis → cb-analysis (port 8001)EC2 Instance Setup
1. Install Docker & Docker Compose
bash
sudo yum update -y
sudo yum install -y docker
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker ec2-user
# Docker Compose plugin
sudo mkdir -p /usr/local/lib/docker/cli-plugins
sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 \
-o /usr/local/lib/docker/cli-plugins/docker-compose
sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
# Log out and back in for group changes
exit2. Install Nginx & Certbot
bash
sudo yum install -y nginx certbot python3-certbot-nginx
sudo systemctl start nginx
sudo systemctl enable nginx3. Docker Hub Login
bash
docker login -u dheerajoptimusprime
# Token: dckr_pat_pLgvvJqP_WFAPa-HWMi6eGB8a4s4. Project Directory
bash
mkdir -p /home/ec2-user/oktivo-backend/credentials
cd /home/ec2-user/oktivo-backendConfiguration Files
.env
env
DEPLOY_ENV=stage
AWS_REGION=ap-south-1
REDIS_PASSWORD=abc123#
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=Note:
AWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEYare intentionally blank. Containers use the EC2 instance IAM role for AWS credentials via the metadata service.
docker-compose.yml
See docker-compose-stage.yml in this repo for the full file.
AWS Secrets Manager
Secrets are loaded at container startup via the EC2 instance IAM role:
| Secret Name | Used By |
|---|---|
oktivo/stage/shared | All services |
oktivo/stage/analysis | analysis |
oktivo/stage/agent | agent |
oktivo/stage/client-backend | client_backend |
IAM Role Requirements
The EC2 instance must have an IAM role with:
secretsmanager:GetSecretValueonarn:aws:secretsmanager:ap-south-1:*:secret:oktivo/stage/*
Instance Metadata (IMDSv2)
Docker containers need hop limit = 2 to reach the EC2 metadata service:
bash
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
aws ec2 modify-instance-metadata-options \
--instance-id $INSTANCE_ID \
--http-put-response-hop-limit 2 \
--http-endpoint enabled \
--region ap-south-1Or set this in AWS Console: EC2 → Instance → Actions → Instance Settings → Modify instance metadata options → Hop limit = 2.
Nginx Configuration
File: /etc/nginx/conf.d/oktivo-backend.conf
nginx
server {
listen 80;
server_name stage-backend.oktivo.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name stage-backend.oktivo.com;
ssl_certificate /etc/letsencrypt/live/stage-backend.oktivo.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/stage-backend.oktivo.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Client Backend
location /client/ {
proxy_pass http://127.0.0.1:3000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
proxy_read_timeout 86400;
}
# Analysis Backend
location /analysis/ {
proxy_pass http://127.0.0.1:8001/;
proxy_http_version 1.1;
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;
proxy_read_timeout 300;
}
}SSL Certificate
bash
# Initial setup
sudo mkdir -p /var/www/certbot
sudo certbot certonly --webroot -w /var/www/certbot \
-d stage-backend.oktivo.com \
--non-interactive --agree-tos --email [email protected]
# Auto-renewal (add to crontab: sudo crontab -e)
0 0,12 * * * certbot renew --quiet --post-hook "systemctl reload nginx"Docker Images (Docker Hub)
| Image Tag | Service |
|---|---|
dheerajoptimusprime/oktivo:analysis-stage-latest | analysis |
dheerajoptimusprime/oktivo:agent-stage-latest | agent |
dheerajoptimusprime/oktivo:client-backend-stage-latest | client_backend |
Operations
Deploy / Update
bash
cd /home/ec2-user/oktivo-backend
docker compose pull
docker compose up -dView Logs
bash
docker compose logs -f # all services
docker compose logs -f client_backend # single serviceRestart
bash
docker compose down
docker compose up -dCheck Status
bash
docker compose ps
docker psPorts
| Port | Service | Exposed To |
|---|---|---|
| 80 | Nginx (HTTP → HTTPS redirect) | Public |
| 443 | Nginx (SSL termination) | Public |
| 3000 | client_backend | Localhost only (via Nginx) |
| 8001 | analysis | Localhost only (via Nginx) |
| 6379 | Valkey (Redis) | Localhost only |
Security Group (EC2)
Inbound rules required:
- Port 80 (HTTP)
0.0.0.0/0 - Port 443 (HTTPS)
0.0.0.0/0 - Port 22 (SSH) Your IP only
Troubleshooting
Secrets Manager AccessDenied
- Verify IAM role is attached to EC2 instance
- Verify IMDSv2 hop limit is set to 2
- Do NOT pass
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYin env (they override the instance role)
Valkey Connection Refused
- Redis password contains
#must be URL-encoded as%23inREDIS_URL - Ensure
depends_onwithcondition: service_healthyis set
Docker Compose Warnings about blank AWS vars
- Harmless. The blank values in
.envsilence them while allowing the instance role to work.
Key Decisions
- No AWS keys in environment Uses EC2 instance IAM role instead of static credentials
- Host Nginx SSL termination on host, not in Docker (simpler certbot management)
- Valkey password Uses
abc123#from Secrets Manager (oktivo/stage/shared), URL-encoded in REDIS_URL - No Docker nginx/certbot containers Removed from compose since host handles SSL