Goal: You will launch an EC2 instance, install Python and Flask, create a simple health check API, and configure GitHub Actions to verify your deployment is working.
flowchart LR
GH[GitHub Actions<br/>Verifier]
AWS[AWS API]
SG[Security Group<br/>Firewall]
EC2[EC2 Instance<br/>Flask API]
GH -->|AWS CLI| AWS
GH -->|HTTP :80| SG --> EC2
- AWS Account with Free Tier access
- GitHub account
- SSH client (Terminal on Mac/Linux, PowerShell or PuTTY on Windows)
- Go to EC2 Dashboard --> Key Pairs (under Network & Security)
- Click Create key pair
- Name:
cs1660-key(or your preferred name) - Key pair type: RSA
- Private key format: .pem (Mac/Linux) or .ppk (Windows/PuTTY)
- Click Create key pair and save the downloaded file securely
Important: You cannot download this key again. Store it safely.
-
Go to EC2 Dashboard --> Security Groups (under Network & Security)
-
Click Create security group
-
Configure:
- Security group name:
flask-server-sg - Description:
SSH and HTTP access for Flask server - VPC: Leave as default
- Security group name:
-
Add Inbound rules:
| Type | Port | Source | Description |
|---|---|---|---|
| SSH | 22 | My IP | SSH access from your machine |
| HTTP | 80 | 0.0.0.0/0 | HTTP access from anywhere |
- Verify Outbound rules allow all traffic (this is the default):
| Type | Protocol | Port | Destination |
|---|---|---|---|
| HTTP | 443 | 0.0.0.0/0 | HTTPS egress traffic to anywhere |
we need an outbound rule on 443 to download python deps over HTTPS.
- Click Create security group
- Go to EC2 Dashboard --> Launch instance
- Configure:
- Name:
flask-server - AMI: Ubuntu Server 24.04 LTS (Free tier eligible)
- Instance type: t3.micro (Free tier eligible)
- Key pair: Select the key pair you created
- Network settings: Click Edit, then select existing security group
flask-server-sg
- Name:
- Click Launch instance
- Note your instance's Public IPv4 address and Instance ID from the console
# set permissions on key file (Mac/Linux only)
chmod 400 ~/Downloads/cs1660-key.pem
# connect to your instance
# note: replace your YOUR_PUBLIC_IP with your IP address
ssh -i ~/Downloads/cs1660-key.pem ubuntu@YOUR_PUBLIC_IP# update packages
sudo apt update && sudo apt upgrade -y
# install Python and pip
sudo apt install -y python3 python3-pip python3-venv
# verify installation
python3 --version
pip3 --version# create project directory
mkdir ~/flask-api
cd ~/flask-api
# create virtual environment
python3 -m venv .venv
source .venv/bin/activate
# install Flask
pip install flaskCreate a file named app.py:
touch app.pyAdd the following code with either Vim or nano choose your own adventure:
from flask import Flask, jsonify
import socket
from datetime import datetime, timezone
app = Flask(__name__)
@app.route('/health', methods=['GET'])
def health():
return jsonify({
"status": "healthy",
"timestamp": datetime.now(timezone.utc).isoformat(),
"hostname": socket.gethostname()
}), 200
@app.route('/', methods=['GET'])
def index():
return jsonify({
"message": "Welcome to Cloud Computing",
"endpoints": {
"health": "/health"
}
}), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)Flask needs root privileges to bind to port 80:
sudo ~/flask-api/.venv/bin/python app.pyIn a new SSH session (keep the server running):
curl http://localhost/healthYou should see:
{"hostname":"ip-xxx-xxx-xxx-xxx","status":"healthy","timestamp":"2025-01-28T..."}Open a browser or terminal on your local machine:
curl http://YOUR_PUBLIC_IP/healthIf this works, your server is publicly accessible.
To keep the server running after you disconnect:
sudo nohup ~/flask-api/.venv/bin/python ~/flask-api/app.py > ~/flask-api/server.log 2>&1 &
# test in the same terminal
curl localhost- Go to IAM in the AWS Console
- Click Users --> Create user
- User name:
github-actions - Click Next
- Select Attach policies directly
- Search for and select:
AmazonEC2ReadOnlyAccess - Click Next --> Create user
- Click on the user
github-actions - Go to Security credentials tab
- Under Access keys, click Create access key
- Select Third-party service
- Check the confirmation box, click Next
- Click Create access key
- Copy both values (you won't see the secret again):
- Access key ID
- Secret access key
Accept the GitHub Classroom assignment link (provided in Canvas) and clone your repository.
- Go to your repository on GitHub
- Navigate to Settings --> Secrets and variables --> Actions
- Click the Secrets tab
- Add the following secrets (click New repository secret for each):
| Secret Name | Value |
|---|---|
AWS_ACCESS_KEY_ID |
Your IAM user access key ID |
AWS_SECRET_ACCESS_KEY |
Your IAM user secret access key |
- Still in Settings --> Secrets and variables --> Actions
- Click the Variables tab
- Click New repository variable and add:
| Variable Name | Value |
|---|---|
EC2_PUBLIC_IP |
Your EC2 instance's public IP (no http://) |
EC2_INSTANCE_ID |
Your EC2 instance ID (e.g., i-0abc123def456) |
Push an empty commit to trigger the workflow:
git commit --allow-empty -m "trigger verification workflow"
git pushOr manually trigger it:
- Go to the Actions tab in your repository
- Select the Verify Flask Server workflow
- Click Run workflow
Check the Actions tab for a green checkmark. If the workflow fails, review the logs for troubleshooting hints.
Submit the following to Canvas:
- GitHub Repository URL (your GitHub Classroom repository)
2Public IP address of your EC2 instance in an
ip.txtfile
| Component | Points |
|---|---|
| EC2 instance is running (verified via AWS CLI) | 3 |
| Security Group allows HTTP from anywhere (verified via AWS CLI) | 3 |
| Flask API returns valid JSON on /health endpoint | 4 |
| GitHub Actions workflow passes | 5 |
| Total | 15 |
- Verify your Security Group allows SSH (port 22) from your IP
- Check that you're using the correct key file
- Ensure the instance is in "running" state
- Check that your Security Group allows all outbound traffic
- Go to your Security Group --> Outbound rules --> Ensure "All traffic" to 0.0.0.0/0 exists
- Verify Security Group allows HTTP (port 80) from 0.0.0.0/0
- Confirm Flask is running:
sudo lsof -i :80 - Check Flask is binding to 0.0.0.0, not 127.0.0.1
- Verify your AWS credentials are correct in GitHub Secrets
- Make sure there are no extra spaces when copying the keys
- Check that the IAM user has
AmazonEC2ReadOnlyAccesspolicy attached
- Verify the
EC2_INSTANCE_IDvariable is correct (starts withi-) - Check the instance exists in us-east-1 region
- Ensure your Security Group has an inbound rule for HTTP (port 80) from 0.0.0.0/0
- The rule must allow traffic from anywhere, not just your IP
- Use nohup (see Part 4)
- Verify with:
curl http://localhost/healthafter reconnecting
Important: When you are finished with this assignment, stop or terminate your EC2 instance to avoid charges.
# find your instance ID
aws ec2 describe-instances \
--query "Reservations[*].Instances[*].{ID:InstanceId,State:State.Name,Name:Tags[?Key=='Name']|[0].Value}" \
--output table \
--region us-east-1
# stop the instance
aws ec2 stop-instances --instance-ids i-xxxxxxxxxxxxxxxxx --region us-east-1Or from the AWS Console: EC2 Dashboard --> Instances --> Select instance --> Instance state --> Stop instance