This project demonstrates the end-to-end process of building, containerizing, and deploying a lightweight FastAPI application to an AWS EC2 instance. FastAPI, known for its high performance and intuitive API design, is paired with Docker to create a portable, consistent runtime environment. The containerized application is then hosted on AWS EC2, making it accessible over the internet.
The guide is intentionally simple, making it suitable for beginners who want hands-on experience with API development, containerization, and cloud deployment. By completing this project, you’ll understand how to:
- Develop a basic FastAPI application.
- Write a Dockerfile to containerize the app.
- Build, tag, and push Docker images to a registry.
- Configure and run the container on an AWS EC2 instance.
It’s a foundational DevOps exercise that blends software development with infrastructure management — essential for modern application delivery.
Project layout
fastapi-simple/
├─ main.py
├─ requirements.txt
├─ Dockerfile
├─ .dockerignore
1) Code — main.py
Create main.py
:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(title="Simple FastAPI App")
class Echo(BaseModel):
message: str
@app.get("/")
def read_root():
return {"message": "Hello from FastAPI in Docker!"}
@app.get("/health")
def health():
return {"status": "ok"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
@app.post("/echo")
def post_echo(payload: Echo):
return {"you_sent": payload}
2) Dependencies — requirements.txt
fastapi
uvicorn[standard]
3) Dockerfile
Create Dockerfile
(uses a small Python base, installs dependencies, runs uvicorn):
# Use an official Python runtime as a parent image
FROM python:3.11-slim
# Prevent Python from writing .pyc files and enable stdout buffering
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app
# Copy only requirements first for better caching
COPY requirements.txt .
# Install pip dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Copy app source
COPY . .
# Expose the port uvicorn will listen on
EXPOSE 8000
# Run the app with uvicorn
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
4) .dockerignore (optional but recommended)
__pycache__
*.pyc
.env
.venv
.git
5) Build & run locally
From the project root:
# build
docker build -t fastapi-simple:local .
# run (maps container port 8000 -> host 8000)
docker run --rm -p 8000:8000 fastapi-simple:local
Open http://localhost:8000/
and http://localhost:8000/docs
(interactive API docs).
6) Push to Docker Hub (simple public flow)
- Create a repo at hub.docker.com (or use an existing one).
- Tag and push:
docker login # enter Docker Hub credentials
docker tag fastapi-simple:local yourhubusername/fastapi-simple:latest
docker push yourhubusername/fastapi-simple:latest
If you want a private registry, consider AWS ECR (more secure for production).
7) Deploy on AWS EC2 (manual steps)
Use an Ubuntu 22.04 (or similar) instance. Minimal overview:
- Create an EC2 instance
- AMI: Ubuntu 22.04 LTS
- Instance type: t3.micro (or as needed)
- Key pair: create/download one (you’ll use
.pem
to SSH) - Security Group: allow inbound TCP ports 22 (SSH) and 80 (HTTP). If you plan to use 8000, open that too.
- SSH to EC2
ssh -i path/to/key.pem ubuntu@YOUR_EC2_PUBLIC_IP
- Install Docker (simple method)
sudo apt update
sudo apt install -y docker.io
sudo systemctl enable --now docker
# give your ubuntu user permission to use docker (you may need to logout/login)
sudo usermod -aG docker $USER
- Pull the image and run it (map container 8000 -> host 80)
# (optional) docker login
docker pull yourhubusername/fastapi-simple:latest
# run as detached, restart automatically on reboot/crash
docker run -d --name fastapi \
--restart unless-stopped \
-p 80:8000 \
yourhubusername/fastapi-simple:latest
- Visit
http://YOUR_EC2_PUBLIC_IP/
in your browser. API docs:http://YOUR_EC2_PUBLIC_IP/docs
.
8) Optional: run container as a systemd service (survives reboots)
Create /etc/systemd/system/fastapi.service
with:
[Unit]
Description=FastAPI Docker Container
After=docker.service
Requires=docker.service
[Service]
Restart=always
ExecStartPre=-/usr/bin/docker stop fastapi
ExecStartPre=-/usr/bin/docker rm fastapi
ExecStart=/usr/bin/docker run --name fastapi --rm --restart unless-stopped -p 80:8000 yourhubusername/fastapi-simple:latest
ExecStop=/usr/bin/docker stop fastapi
[Install]
WantedBy=multi-user.target
Then:
sudo systemctl daemon-reload
sudo systemctl enable --now fastapi.service
sudo journalctl -u fastapi -f
9) Optional: EC2 user-data (auto bootstrap on instance creation)
You can use this script as user-data when launching the instance to auto-install docker and run the container. Warning: do not embed Docker Hub credentials in user-data for security — use public image or ECR + IAM role.
Example user-data (Ubuntu):
#!/bin/bash
apt update
apt install -y docker.io
systemctl enable --now docker
# pull public image
docker pull yourhubusername/fastapi-simple:latest
docker run -d --restart unless-stopped --name fastapi -p 80:8000 yourhubusername/fastapi-simple:latest
10) Troubleshooting tips
- If you get a blank page: check
docker logs fastapi
on EC2. - If EC2 public IP not serving: confirm Security Group inbound rules include port 80, and that the container is running (
docker ps
). - If docker commands require sudo: re-login after
usermod -aG docker $USER
or run withsudo
. - For private images: use
docker login
on the EC2 instance beforedocker pull
, or use AWS ECR with an instance role.
11) Next steps / production notes (short)
- For production, use a process manager / orchestrator (ECS, EKS, or at least systemd + reverse proxy like Nginx).
- Use HTTPS: put an nginx reverse proxy + certbot / or use a load balancer with TLS.
- Use AWS ECR + instance profile or secrets manager instead of embedding Docker Hub credentials.
Top comments (0)