A lightweight Personal REST API built with Python/Flask and deployed on AWS EC2 using a production-grade DevOps stack. This project demonstrates a real-world deployment pipeline — from writing an API to keeping it persistently alive behind a reverse proxy.
Live URL:
http://13.60.222.90
┌─────────────────────────────────────────┐
│ AWS EC2 Instance │
│ (Ubuntu 22.04) │
│ │
Client Request │ ┌──────────┐ ┌──────────────┐ │
──────────────────────►│ │ │ :5000 │ │ │
http://your-ip:80 │ │ Nginx ├──────►│ Gunicorn │ │
│ │ (Port │ │ (WSGI │ │
◄──────────────────── │ │ 80) │◄──────┤ Server) │ │
JSON Response │ │ │ │ │ │
│ └──────────┘ └──────┬───────┘ │
│ Reverse Proxy │ │
│ ┌──────▼───────┐ │
│ │ Flask App │ │
│ │ (app.py) │ │
│ └──────────────┘ │
│ │
│ ┌──────────────────────────────────┐ │
│ │ systemd — keeps service alive │ │
│ │ auto-restarts on crash/reboot │ │
│ └──────────────────────────────────┘ │
└─────────────────────────────────────────┘
Stack: Python/Flask → Gunicorn → Nginx → Internet
Port: Flask/Gunicorn binds to 127.0.0.1:5000 (private)
Nginx listens on 0.0.0.0:80 (public)
| Method | Endpoint | Description | Status Code | Content-Type |
|---|---|---|---|---|
| GET | / |
API status check | 200 OK |
application/json |
| GET | /health |
Health check | 200 OK |
application/json |
| GET | /me |
Personal details | 200 OK |
application/json |
GET /
{
"message": "API is running"
}GET /health
{
"message": "healthy"
}GET /me
{
"name": "Emmanuel Kabari",
"email": "kabariirenaeus@gmail.com",
"github": "https://github.com/Kabari"
}personal-api/
│
├── app.py # Main Flask application (all 3 endpoints)
├── requirements.txt # Python dependencies (Flask, Gunicorn)
├── README.md # Project documentation (you are here)
│
└── venv/ # Python virtual environment (not committed to Git)
├── bin/
│ ├── python3
│ └── gunicorn
└── lib/
- Python 3.10 or higher
- pip
- git
1. Clone the repository
git clone https://github.com/Kabari/personal-api.git
cd personal-api2. Create and activate a virtual environment
python3 -m venv venv
source venv/bin/activate # Mac/Linux
# venv\Scripts\activate # Windows3. Install dependencies
pip install -r requirements.txt4. Run the application
python3 app.py5. Test all endpoints
curl http://127.0.0.1:5000/
curl http://127.0.0.1:5000/health
curl http://127.0.0.1:5000/me- Launch an AWS EC2 instance (Ubuntu 22.04 LTS, t2.micro)
- Configure inbound Security Group rules:
| Port | Protocol | Source | Purpose |
|---|---|---|---|
| 22 | TCP | Your IP | SSH access |
| 80 | TCP | 0.0.0.0/0 | HTTP traffic |
| 443 | TCP | 0.0.0.0/0 | HTTPS (future) |
⚠️ Do NOT open port 5000 publicly. Flask/Gunicorn must stay private.
chmod 400 your-key.pem
ssh -i your-key.pem ubuntu@your-server-ipsudo apt update && sudo apt upgrade -y
sudo apt install python3 python3-pip python3-venv nginx -ymkdir ~/personal-api && cd ~/personal-api
python3 -m venv venv
source venv/bin/activate
pip install flask gunicorn
pip freeze > requirements.txtCreate app.py:
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/")
def home():
return jsonify({"message": "API is running"}), 200
@app.route("/health")
def health():
return jsonify({"message": "healthy"}), 200
@app.route("/me")
def me():
return jsonify({
"name": "Emmanuel Kabari",
"email": "kabariirenaeus@gmail.com",
"github": "https://github.com/Kabari"
}), 200
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)sudo nano /etc/systemd/system/personal-api.servicePaste:
[Unit]
Description=Personal Flask API
After=network.target
[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/personal-api
Environment="PATH=/home/ubuntu/personal-api/venv/bin"
ExecStart=/home/ubuntu/personal-api/venv/bin/gunicorn -w 3 -b 127.0.0.1:5000 app:app
Restart=always
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl daemon-reload
sudo systemctl start personal-api
sudo systemctl enable personal-apisudo nano /etc/nginx/sites-available/personal-apiPaste:
server {
listen 80;
server_name _;
location / {
proxy_pass http://127.0.0.1:5000;
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;
}
}Enable and restart:
sudo ln -s /etc/nginx/sites-available/personal-api /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl restart nginxRun these to confirm everything is working correctly:
# 1. Check the service is running
sudo systemctl status personal-api
# 2. Check Nginx is running
sudo systemctl status nginx
# 3. Test endpoints internally (on the server)
curl http://127.0.0.1:5000/
curl http://127.0.0.1:5000/health
curl http://127.0.0.1:5000/me
# 4. Test endpoints publicly (from your local machine)
curl -i http://your-server-ip/
curl -i http://your-server-ip/health
curl -i http://your-server-ip/me
# 5. Verify Content-Type header is application/json
curl -I http://your-server-ip/
# 6. Check response time (must be under 500ms)
curl -o /dev/null -s -w "Total time: %{time_total}s\n" http://your-server-ip/✅ Flask app binds to 127.0.0.1:5000 (not exposed publicly)
✅ Only ports 22 and 80 open in AWS Security Group
✅ Port 5000 is NOT open in AWS Security Group
✅ Nginx acts as the only public entry point (port 80)
✅ App runs as non-root user (ubuntu)
✅ systemd auto-restarts app on crash or server reboot
✅ All endpoints return Content-Type: application/json
✅ All endpoints return HTTP 200
✅ All endpoints respond within 500ms
✅ GitHub repository is set to Public
Service not running?
sudo journalctl -u personal-api -n 50
sudo systemctl restart personal-api502 Bad Gateway from Nginx?
# Gunicorn is likely not running
sudo systemctl status personal-api
sudo systemctl start personal-apiNginx config error?
sudo nginx -t
sudo systemctl restart nginxGetting HTML instead of JSON?
# Default Nginx page is still active
sudo rm /etc/nginx/sites-enabled/default
sudo systemctl restart nginxCheck live logs in real time:
sudo journalctl -u personal-api -f| Technology | Role |
|---|---|
| Python 3 | Programming language |
| Flask | Web framework / API |
| Gunicorn | Production WSGI server |
| Nginx | Reverse proxy / load balancer |
| systemd | Process & service manager |
| AWS EC2 | Cloud server (VPS) |
| Ubuntu | Server operating system |
Emmanuel Kabari 📧 kabariirenaeus@gmail.com 🐙 https://github.com/Kabari