Complete guide to deploy your ML project to AWS EC2 with ECR and GitHub Actions CI/CD.
- AWS Account with billing enabled
- GitHub repository with your code
- Basic familiarity with AWS Console
- Sign in to AWS Console: https://console.aws.amazon.com/
- Search for IAM in the top search bar
- Click IAM service
- Click Users in left sidebar
- Click Create user button
- User name:
github-actions-mlproject - Click Next
- Select Attach policies directly
- Search and check these policies:
AmazonEC2ContainerRegistryFullAccess
- Click Next
- Click Create user
- Click on the user you just created
- Go to Security credentials tab
- Scroll to Access keys section
- Click Create access key
- Select Application running outside AWS
- Click Next → Create access key
- IMPORTANT: Copy and save:
- Access key ID (e.g.,
AKIAIOSFODNN7EXAMPLE) - Secret access key (e.g.,
wJalrXUtnFEMI/K7MDENG...) - You'll add these to GitHub Secrets later
- Access key ID (e.g.,
- Search for ECR in AWS Console search bar
- Click Elastic Container Registry
- Click Get Started or Create repository
- Visibility: Select Private
- Repository name:
mlproject - Leave other settings as default
- Click Create repository
After creation, you'll see:
- Repository URI:
123456789012.dkr.ecr.us-east-1.amazonaws.com/mlproject - Copy the part BEFORE
/mlproject→ This is your ECR_REGISTRY- Example:
123456789012.dkr.ecr.us-east-1.amazonaws.com
- Example:
- The repository name is:
mlproject→ This is your ECR_REPOSITORY
- Search for EC2 in AWS Console
- Click EC2 service
-
In left sidebar, click Key Pairs (under Network & Security)
-
Click Create key pair
-
Name:
mlproject-key -
Key pair type: RSA
-
Private key format:
.pem -
Click Create key pair
-
IMPORTANT: File downloads automatically - save it securely!
-
Move to safe location:
# On Windows (PowerShell): Move-Item ~/Downloads/mlproject-key.pem ~/.ssh/ # On Mac/Linux: mv ~/Downloads/mlproject-key.pem ~/.ssh/ chmod 400 ~/.ssh/mlproject-key.pem
-
In left sidebar, click Security Groups
-
Click Create security group
-
Security group name:
mlproject-sg -
Description:
Allow SSH and HTTP for ML project -
VPC: Leave default
-
Add Inbound rules (click "Add rule" for each):
- Rule 1:
- Type:
SSH - Port:
22 - Source:
My IP(for your IP) or0.0.0.0/0(anywhere - less secure)
- Type:
- Rule 2:
- Type:
HTTP - Port:
80 - Source:
0.0.0.0/0(Anywhere IPv4)
- Type:
- Rule 3 (optional for HTTPS later):
- Type:
HTTPS - Port:
443 - Source:
0.0.0.0/0
- Type:
- Rule 1:
-
Click Create security group
- Go to EC2 Dashboard
- Click Launch instance button
-
Name:
mlproject-server -
Application and OS Images (AMI):
- Select Amazon Linux 2023 AMI (free tier eligible)
- OR Ubuntu Server 22.04 LTS
-
Instance type:
- Select t3.medium (2 vCPU, 4GB RAM - recommended for ML)
- Or t2.medium if t3 not available
- Note: t2.micro (free tier) may be too small for ML models
-
Key pair:
- Select the key pair you created:
mlproject-key
- Select the key pair you created:
-
Network settings:
- Click Edit
- Firewall (security groups): Select existing security group
- Choose:
mlproject-sg
-
Configure storage:
- Change to 20 GB or 30 GB (free tier allows up to 30GB)
-
Click Launch instance
-
Wait 1-2 minutes for instance to start
-
Go to Instances in left sidebar
-
Click on your instance
-
Copy and save these from the Details tab:
- Public IPv4 address (e.g.,
54.123.45.67) → This is EC2_HOST - Public IPv4 DNS (e.g.,
ec2-54-123-45-67.compute-1.amazonaws.com)
- Public IPv4 address (e.g.,
# On Windows PowerShell:
ssh -i ~/.ssh/mlproject-key.pem ec2-user@54.123.45.67
# On Mac/Linux:
ssh -i ~/.ssh/mlproject-key.pem ec2-user@YOUR_EC2_IP
# If using Ubuntu AMI, use:
ssh -i ~/.ssh/mlproject-key.pem ubuntu@YOUR_EC2_IPType yes when asked about fingerprint.
# Update system
sudo yum update -y
# Install Docker
sudo yum install docker -y
# Start Docker service
sudo systemctl start docker
sudo systemctl enable docker
# Add user to docker group (no sudo needed)
sudo usermod -aG docker ec2-user
# Log out and back in for group changes
exit
# Then SSH back in# Update system
sudo apt update && sudo apt upgrade -y
# Install Docker
sudo apt install docker.io -y
# Start Docker service
sudo systemctl start docker
sudo systemctl enable docker
# Add user to docker group
sudo usermod -aG docker ubuntu
# Log out and back in
exit
# Then SSH back in# Download AWS CLI v2
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
# Install unzip if not present
sudo yum install unzip -y # Amazon Linux
# OR: sudo apt install unzip -y # Ubuntu
# Unzip and install
unzip awscliv2.zip
sudo ./aws/install
# Verify
aws --version
docker --versionOption A - IAM Role (Recommended for Production):
- In AWS Console → EC2 → Select your instance
- Actions → Security → Modify IAM role
- Create new role with
AmazonEC2ContainerRegistryReadOnly - Attach to instance
Option B - Configure Credentials Manually:
aws configure
# Enter:
# - AWS Access Key ID: (use the IAM user key from Step 1)
# - AWS Secret Access Key: (use the secret from Step 1)
# - Default region: us-east-1 (or your region)
# - Default output format: json# Replace with your region and registry
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
# You should see: "Login Succeeded"- Go to your GitHub repository
- Click Settings tab
- In left sidebar, click Secrets and variables → Actions
- Click New repository secret for each below
Add these secrets one by one:
| Secret Name | Value | Where to Find |
|---|---|---|
AWS_ACCESS_KEY_ID |
Your IAM access key | From Step 1.4 |
AWS_SECRET_ACCESS_KEY |
Your IAM secret key | From Step 1.4 |
AWS_REGION |
us-east-1 |
Your AWS region |
ECR_REGISTRY |
123456789012.dkr.ecr.us-east-1.amazonaws.com |
From Step 2.3 (WITHOUT /mlproject) |
ECR_REPOSITORY |
mlproject |
Repository name from Step 2.2 |
EC2_HOST |
54.123.45.67 |
From Step 3.5 (Public IPv4) |
EC2_USER |
ec2-user |
Use ec2-user for Amazon Linux, ubuntu for Ubuntu |
EC2_SSH_KEY |
Full content of .pem file | See Step 5.3 below |
On Windows (PowerShell):
Get-Content ~/.ssh/mlproject-key.pem | clipOn Mac/Linux:
cat ~/.ssh/mlproject-key.pem | pbcopy # Mac
# OR
cat ~/.ssh/mlproject-key.pem # Copy output manuallyPaste the ENTIRE content (including -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY-----) into the EC2_SSH_KEY secret.
# From your project directory
git add .
git commit -m "Add EC2 CI/CD pipeline"
git push origin main- Go to your GitHub repository
- Click Actions tab
- You should see a workflow running: CI/CD - Deploy to EC2
- Click on the running workflow to see live logs
- ✅ CI: Tests and linting (2-3 min)
- ✅ Build and Push to ECR: Docker build and push (5-10 min)
- ✅ Deploy to EC2: SSH and container deployment (2-3 min)
Open browser and go to:
http://YOUR_EC2_PUBLIC_IP/
You should see your ML project homepage!
http://YOUR_EC2_PUBLIC_IP/predictdata
# SSH into EC2
ssh -i ~/.ssh/mlproject-key.pem ec2-user@YOUR_EC2_IP
# Check running containers
docker ps
# View logs
docker logs mlproject -f
# Check recent logs
docker logs --tail 50 mlproject- Use Route 53, Namecheap, or GoDaddy
-
Go to Route 53 in AWS Console
-
Create hosted zone for your domain
-
Create A Record:
- Name:
mlproject.yourdomain.com - Type: A
- Value: Your EC2 public IP
- TTL: 300
- Name:
# SSH into EC2
# Install Nginx
sudo yum install nginx -y
sudo systemctl start nginx
sudo systemctl enable nginx
# Install Certbot
sudo yum install certbot python3-certbot-nginx -y
# Get certificate
sudo certbot --nginx -d mlproject.yourdomain.com
# Configure Nginx to proxy to Docker container
sudo nano /etc/nginx/conf.d/mlproject.confAdd this config:
server {
listen 80;
server_name mlproject.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name mlproject.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/mlproject.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mlproject.yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}# Test and reload
sudo nginx -t
sudo systemctl reload nginx- Check
EC2_SSH_KEYsecret contains full private key with BEGIN/END lines - Verify
EC2_USERmatches your AMI (ec2-user or ubuntu) - Check Security Group allows SSH from 0.0.0.0/0
- SSH into EC2 and test:
aws ecr get-login-password | docker login ... - Verify IAM role or AWS credentials are configured on EC2
- Check IAM user has ECR permissions
- Docker not installed on EC2
- Reconnect SSH after adding user to docker group
- Check Security Group allows HTTP (port 80) from 0.0.0.0/0
- Verify container is running:
docker ps - Check container logs:
docker logs mlproject - Test locally on EC2:
curl http://localhost
- View logs:
docker logs mlproject - Likely missing artifacts or model files
- Check if
artifacts/folder exists and has models
- Stop EC2 when not in use (or use auto-scaling)
- Delete old ECR images: ECR Console → Repository → Select old images → Delete
- Use t3.micro instead of t3.medium for testing (though may be slow for ML)
- ECR: ~$0.10/GB/month for storage
- EC2 t3.medium: ~$30/month (on-demand, 24/7)
- Data Transfer: First 100GB/month free, then $0.09/GB
- Total: ~$30-35/month for basic setup
Save money:
- Use t3.micro (~$7/month) for testing
- Stop instance when not needed
- Use AWS Free Tier (12 months: 750 hrs/month t2.micro + 30GB storage)
- ✅ Monitor GitHub Actions on every push
- ✅ Add CloudWatch alarms for EC2 health
- ✅ Set up automated backups
- ✅ Implement blue-green deployments
- ✅ Add load balancer for high availability
# SSH to EC2
ssh -i ~/.ssh/mlproject-key.pem ec2-user@YOUR_EC2_IP
# Check running containers
docker ps
# View logs
docker logs -f mlproject
# Restart container
docker restart mlproject
# Pull and run manually
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin YOUR_REGISTRY
docker pull YOUR_REGISTRY/mlproject:latest
docker stop mlproject && docker rm mlproject
docker run -d --name mlproject -p 80:8080 YOUR_REGISTRY/mlproject:latest
# Check disk space
df -h
# Check system resources
htop # install first: sudo yum install htop -yIf you encounter issues:
- Check GitHub Actions logs
- SSH into EC2 and check
docker logs mlproject - Verify all GitHub Secrets are correct
- Review AWS CloudWatch logs (if enabled)
Good luck with your deployment! 🚀