A production-style CI/CD project that automates deployment of containerized applications on a self-hosted VM.
Designed to simulate real-world DevOps practices including secure networking, automated deployments, and scalable service routing.
Purpose: Simulate real production delivery by deploying a secure, production-like environment with CI/CD, HTTPS, and zero exposed inbound ports.
Key Problem Solved:
- Local VM deployments often expose ports or lack HTTPS.
- This project demonstrates secure, automated, scalable deployment in a homelab setting.
| Component | Purpose |
|---|---|
| Cloudflare Tunnel | Securely exposes the VM to the internet without opening inbound ports |
| TLS Termination | Managed at Cloudflare edge; no local certificate management needed |
| Nginx | Reverse proxy and load balancer routing requests to multiple API containers |
| Docker & Docker Compose | Orchestration for APIs and PostgreSQL |
| Jenkins CI/CD | Automated builds, testing, and deployments triggered on Git push |
| PostgreSQL | Persistent database using Docker volumes |
| Internal Docker Network | Ensures services communicate securely without exposing unnecessary ports |
- Push code to Git repository.
- Jenkins pipeline triggered via webhook.
- Docker images built on target VM.
- Docker Compose deploys/redeploys services.
- Nginx load balances traffic across three API containers.
- Cloudflare Tunnel exposes the application securely over HTTPS.
- Secure deployment without exposing public ports.
- Fully automated CI/CD pipeline with Jenkins.
- Hostname-based routing directing requests to multiple API services behind a single public endpoint.
- TLS termination handled at Cloudflare edge.
- Load balancing between multiple API containers.
- PostgreSQL database with internal-only access.
- Clean, modular separation of concerns for maintainability and security.
- 🌐 Cloudflare Tunnel – secure outbound-only access with TLS at the edge
- 🌀 Nginx – reverse proxy and load balancer
- 🐳 Docker & Docker Compose – container orchestration
- ⚙️ Jenkins CI/CD – automated pipelines
- 🗄️ PostgreSQL – persistent storage
- 🖥️ Linux (Ubuntu) – self-hosted VM environment
- 🟢 Node.js – backend APIs
- 🚀 CI/CD best practices
All traffic is routed through Nginx, which performs reverse proxying and round-robin load balancing to multiple API containers on a private Docker network.
| Endpoint | Service Container | Port |
|---|---|---|
/user/login |
API v1 | 3000 |
/user/signup |
API v2 | 3001 |
/create-note |
API v3 | 3002 |
/get-notes |
API v1 | 3000 |
/request-logs |
API v2 | 3002 |
/health |
Health check for each API | — |
The NoteWatch API is available securely over HTTPS:
https://notewatch.tayolabs.dev
Test endpoints using:
- Postman
- Insomnia
- curl
- Any HTTP client
- Designed and implemented full Jenkins CI/CD pipeline
- Built Docker images and Compose orchestration for multiple services
- Configured Nginx for routing and load balancing
- Implemented Cloudflare Tunnel for secure HTTPS exposure
- Debugged real-world pipeline failures (e.g., Jenkins WORKDIR issues)
- Designed internal networks and secret management for secure deployment
- Simulated production-ready environment on self-hosted homelab VM
Most endpoints require authentication using a Bearer Access Token.
-
Create a user account (
/user/signup) -
Log in (
/user/login) -
Use the returned
accessTokenin theAuthorizationheader:
Request Body
1. POST /user/signup
{
"username": "Tester",
"email": "tester@example.com",
"password": "strongpassword123"
}
2. POST /user/login
{
"email": "tester@example.com",
"password": "strongpassword123"
}
Expected Response
{
"accessToken": "JWT_ACCESS_TOKEN"
}
3. POST /create-note
Authorization: Bearer <ACCESS_TOKEN>
{
"title": "My First Note"
}
4. GET /get-notes
Authorization: Bearer <JWT_ACCESS_TOKEN>
5. PUT /update-note
Authorization: Bearer <JWT_ACCESS_TOKEN>
{
"noteID": "NOTE_ID",
"title": "Updated Note Title"
}
6. DELETE /delete-note/{noteID}
Authorization: Bearer <JWT_ACCESS_TOKEN>
7. GET /my-logs
Authorization: Bearer <JWT_ACCESS_TOKEN>
