A backend API for a habit-tracking app to create habits, mark them complete, track streaks and stats.
The app logic is intentionally simple: a handful of CRUD endpoints. The actual point of this project was learning how software gets deployed and kept running in production — containers, CI/CD, a real cloud server, and the operational systems (migrations, backups, secrets, dynamic firewalls, health checks) that most tutorials skip.
Live: https://devtrack.vishwajitmohol.me/docs
(Note: To optimize AWS billing, this EC2 instance is run ephemerally and is not kept online 24/7. The deployment engine and infrastructure code detailed below serve as the primary proof of execution.)
| Layer | Choice |
|---|---|
| API | FastAPI + Pydantic |
| Database | PostgreSQL 15, SQLAlchemy ORM, Alembic migrations |
| Auth | JWT (stateless), bcrypt password hashing |
| Containers | Docker, multi-stage build |
| Host | Single AWS EC2 instance (Ubuntu), Docker Compose |
| Reverse Proxy | Nginx, TLS via Let's Encrypt (Certbot) |
| CI/CD | GitHub Actions |
Note on infrastructure: Deploying to a single EC2 instance, rather than a cluster, was a deliberate constraint. The goal was to actually understand container orchestration and Linux server administration from first principles before reaching for higher-level abstractions.
flowchart LR
Client -->|HTTPS| Nginx[Nginx\nTLS termination]
Nginx --> API[FastAPI container]
API --> DB[(PostgreSQL)]
GH[GitHub Actions] -->|build + push, tag = git SHA| GHCR[(GHCR)]
GH -->|SSH deploy trigger| EC2[EC2 host]
GHCR -->|pull| EC2
EC2 -.contains.-> Nginx
EC2 -.contains.-> API
EC2 -.contains.-> DB
The application logic is a straightforward CRUD implementation for users, habits, and completions.
Interactive Swagger documentation (auto-generated by FastAPI) is available at: https://devtrack.vishwajitmohol.me/docs
On push to main:
- Gate: Lint (
ruff) and tests run. - Artifact: If green, the Docker image is built (multi-stage), tagged with the git commit SHA, and pushed to GHCR.
- Firewall: Deploy job whitelists the GitHub runner's IP in the EC2 security group (runner IPs are ephemeral, so this is executed dynamically per run).
- Pre-Deployment Backup: A
pg_dumpsnapshot is taken and shipped to S3, ensuring a bad schema migration doesn't destroy live data. - State Mutation: SSH into the server, run the Alembic migration against the live DB, pull the new image, and execute
docker compose up -d. - Telemetry: Health check is polled after restart — the deploy pipeline fails loudly if the system doesn't come back healthy.
- Perimeter Seal: Runner's IP is revoked from the security group, regardless of whether the deployment succeeded or failed.
Prerequisites: Docker and Docker Compose.
# 1. Clone the repository
git clone https://github.com/Vishwajit1610/DevTrack-API.git
cd DevTrack-API
# 2. Configure environment variables
cp .env.example .env
# 3. Boot the environment
docker compose up --build
# 4. Apply database migrations
docker compose run --rm api alembic upgrade headThe API base URL is http://localhost:8000 (e.g., http://localhost:8000/api/v1/habits).
Interactive Swagger docs are at http://localhost:8000/docs.
| Variable | Purpose |
|---|---|
DATABASE_URL |
Postgres connection string |
POSTGRES_PASSWORD |
Local Postgres container password |
JWT_SECRET_KEY |
Signs auth tokens — generate your own, do not reuse the example |
(See .env.example for the full list.)
Being upfront about what this system doesn't do yet:
- Downtime Deploys: Deploys aren't zero-downtime. Container restarts mean a short gap. Blue-green routing (two containers + traffic switch) would fix this and is planned.
- Manual Rollbacks: If a deploy goes bad, rollback requires manually re-running the workflow against the previous image tag. It works, but it isn't automatic.
- Test Depth: CI runs pytest against an in-memory SQLite DB, which is fast but misses Postgres-specific behaviors. Integration tests against a real Postgres container are required ahead of any new feature.
- No Caching Layer: Redis is scoped (cache
/statsand/streak, invalidate on completion) but not yet built. - No Observability: Prometheus + Grafana is the next infrastructure piece after test coverage.
- CORS Restrictions: CORS is currently scoped to local dev origins only (
localhost:3000/5173) and needs updating once a frontend is deployed.