CloudTask is a production-oriented RESTful Task Management API built with Spring Boot. It demonstrates how to build, secure, test, containerize, deploy, and monitor a real-world backend application.
The system provides REST APIs for task management, authentication, authorization, audit logging, and administration, with PostgreSQL persistence, Flyway migrations, Docker, GitHub Actions CI/CD, AWS EC2 deployment, Prometheus metrics, and Grafana monitoring.
- Overview
- Architecture
- Tech Stack
- Key Features
- System Design
- Getting Started
- API Documentation
- Testing
- Observability
- CI/CD
- AWS EC2 Deployment
- Project Structure
- Interview Talking Points
- Future Enhancements
CloudTask is designed as a production-oriented monolithic backend for secure task management.
- Build a layered Spring Boot REST API
- Implement secure JWT authentication and authorization
- Enforce user ownership and data isolation
- Persist data reliably with PostgreSQL and Flyway
- Protect APIs with rate limiting
- Validate behavior with unit and integration testing
- Automate CI/CD using GitHub Actions
- Deploy the application to AWS EC2 using Docker Compose
- Monitor the application using Actuator, Prometheus, and Grafana
CloudTask follows a layered architecture with security, persistence, CI/CD, and observability around the application.
βββββββββββββββββββββββββββ
β Client β
β REST API / Swagger UI β
ββββββββββββββ¬βββββββββββββ
β HTTP
βΌ
βββββββββββββββββββββββββββ
β Spring Boot API β
β β
β JWT Authentication β
β Rate Limit Filter β
β Spring Security β
β Controllers β
β Services β
β Repositories β
βββββββββ¬ββββββββββ¬ββββββββ
β β
β βββββββββββββββββββββββ
βΌ βΌ
βββββββββββββββββββββ ββββββββββββββββββββββ
β PostgreSQL β β Spring Boot β
β β β Actuator / Metrics β
β JPA / Hibernate β βββββββββββ¬βββββββββββ
β Flyway Migrations β β
βββββββββββββββββββββ βΌ
ββββββββββββββββββββββ
β Prometheus β
β Metric Collection β
βββββββββββ¬βββββββββββ
β
βΌ
ββββββββββββββββββββββ
β Grafana β
β Monitoring Dashboardβ
ββββββββββββββββββββββ
ββββββββββββββββ
β GitHub β
ββββββββ¬ββββββββ
β
βΌ
ββββββββββββββββββββββββ
β GitHub Actions CI β
β Build + Test β
βββββββββββ¬βββββββββββββ
β success
βΌ
ββββββββββββββββββββββββ
β GitHub Actions CD β
β SSH Deployment β
βββββββββββ¬βββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββ
β AWS EC2 β
β β
β Docker Compose β
β β
β βββββββββββββββ ββββββββββββββ β
β β CloudTask β β PostgreSQL β β
β β API :8080 β β :5432 β β
β ββββββββ¬βββββββ ββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββ ββββββββββββββ β
β β Prometheus β β Grafana β β
β β :9090 β β :3000 β β
β βββββββββββββββ ββββββββββββββ β
βββββββββββββββββββββββββββββββββββ
| Category | Technology | Purpose |
|---|---|---|
| Language | Java 21 | Application development |
| Backend | Spring Boot 3 | REST API and application framework |
| Web | Spring Web / Tomcat | HTTP and REST endpoints |
| Security | Spring Security | Authentication and authorization |
| Authentication | JWT | Stateless access-token authentication |
| Refresh Tokens | JWT refresh-token flow | Access-token renewal |
| Password Security | BCrypt | Password hashing |
| Database | PostgreSQL 18 | Persistent relational storage |
| Persistence | Spring Data JPA / Hibernate | ORM and data access |
| Migration | Flyway | Version-controlled database migrations |
| Validation | Jakarta Validation | Request validation |
| API Documentation | OpenAPI / Swagger UI | Interactive API documentation |
| Rate Limiting | Bucket4j | Per-user token-bucket rate limiting |
| Audit Logging | Custom audit logging | User activity and request auditing |
| Unit Testing | JUnit 5 / Mockito | Isolated business-logic testing |
| Integration Testing | Spring Boot Test / Testcontainers | Full application and database integration testing |
| Containerization | Docker | Application containerization |
| Orchestration | Docker Compose | Local and EC2 multi-container deployment |
| CI/CD | GitHub Actions | Automated build, test, and deployment |
| Cloud | AWS EC2 | Application hosting |
| Monitoring | Spring Boot Actuator | Health and application metrics |
| Metrics | Micrometer / Prometheus | Metrics instrumentation and collection |
| Visualization | Grafana | Monitoring dashboards |
| Build Tool | Maven | Dependency and build management |
- Create, retrieve, update, and soft-delete tasks
- Task ownership
- Task priority and status
- Pagination
- Sorting
- Dynamic filtering using JPA Specifications
- Search across multiple task fields
- User registration
- User login
- JWT-based authentication
- Refresh tokens
- Role-based authorization
USERandADMINroles- BCrypt password hashing
- Ownership validation for protected resources
- Spring Security
- JWT authentication filter
- User ownership enforcement
- Collection-level task isolation
- Per-user rate limiting with Bucket4j
- Correct
401,403,404, and429HTTP behavior
- Audit logging for important actions
- User activity tracking
- IP address capture
- User-Agent capture
- Request context handling
- PostgreSQL
- Spring Data JPA
- Hibernate
- Flyway database migrations
- JPA auditing
- Soft delete
- Persistent Docker volume
- JUnit 5 unit tests
- Mockito-based service tests
- Spring Boot integration tests
- PostgreSQL integration testing with Testcontainers
- Authentication and JWT integration testing
- Task ownership and authorization integration testing
- Rate-limit integration testing
- Actuator health endpoint testing
- Meaningful coverage focused on important behavior and security boundaries
- GitHub Actions CI
- Automated Maven build and test execution
- Continuous deployment after successful CI
- SSH-based EC2 deployment
git fetch+git reset --hard origin/main- Docker Compose rebuild and startup
- Post-deployment application health checks
- Deployment failure when the application does not become healthy
- Spring Boot Actuator
/actuator/health/actuator/prometheus- Prometheus metric collection
- Grafana dashboards
- Application availability monitoring
- HTTP request-rate monitoring
- HTTP 5xx error-rate monitoring
- HTTP response-time monitoring
- JVM memory and heap monitoring
- Process CPU monitoring
- JVM live-thread monitoring
- Failure and recovery verification
Client
β
βΌ
JWT Authentication
β
βΌ
Rate Limit Filter
β
βΌ
Spring Security
β
βΌ
Controller
β
βΌ
Service
β
βΌ
Repository
β
βΌ
PostgreSQL
β
βΌ
HTTP Response
Client
β
βββ POST /auth/register
β
βββ POST /auth/login
β
βΌ
Authentication
β
βΌ
Access Token
β
βΌ
Authorization: Bearer <JWT>
β
βΌ
Protected API
Access Token Expires
β
βΌ
POST /auth/refresh
β
βΌ
Validate Refresh Token
β
βΌ
Issue New Token Pair
Authenticated Request
β
βΌ
RateLimitFilter
β
βΌ
User ID Bucket
β
βΌ
Token Available?
β β
YES NO
β β
βΌ βΌ
Continue HTTP 429
Request Retry-After
- Java 21
- Docker
- Docker Compose
- Git
- Maven Wrapper included in the project
git clone https://github.com/mkalki/cloud-task-api.git
cd cloud-task-apiCreate a .env file in the project root:
POSTGRES_DB=cloudtask
POSTGRES_USER=cloudtask
POSTGRES_PASSWORD=your_database_password
JWT_SECRET=your_long_random_jwt_secretNever commit .env to GitHub.
Add this to .gitignore:
.env
Build and start the application stack:
docker compose up -d --buildCheck running containers:
docker compose psView CloudTask logs:
docker compose logs appStop the stack:
docker compose downThe PostgreSQL data is stored in a persistent Docker volume.
To remove the database volume as well:
docker compose down -vWarning:
docker compose down -vdeletes the PostgreSQL volume and its stored database data.
CloudTask uses OpenAPI and Swagger UI for interactive API documentation.
Swagger UI provides:
- REST endpoint discovery
- Request and response schemas
- Interactive endpoint testing
- JWT authorization support
http://localhost:8080/swagger-ui/index.html
http://<EC2_PUBLIC_IP>:8080/swagger-ui/index.html
| Method | Endpoint | Description |
|---|---|---|
| POST | /auth/register |
Register a new user |
| POST | /auth/login |
Authenticate and receive tokens |
| POST | /auth/refresh |
Refresh an access token |
| Method | Endpoint | Description |
|---|---|---|
| GET | /tasks |
Get tasks belonging to the authenticated user |
| POST | /tasks |
Create a new task |
| GET | /tasks/{id} |
Get a task by ID |
| PUT | /tasks/{id} |
Update a task |
| DELETE | /tasks/{id} |
Soft-delete a task |
Protected endpoints require:
Authorization: Bearer <JWT_TOKEN>POST /tasks
{
"title": "Deploy CloudTask",
"description": "Deploy the application on AWS EC2",
"priority": "HIGH"
}Example response:
{
"id": 1,
"title": "Deploy CloudTask",
"description": "Deploy the application on AWS EC2",
"status": "TODO",
"priority": "HIGH",
"ownerId": 1
}CloudTask supports pagination and sorting:
GET /tasks?page=0&size=10
Dynamic filtering is implemented using JPA Specifications.
Supported capabilities:
- Pagination
- Sorting
- Dynamic filtering
- Searching across multiple task fields
CloudTask uses both unit testing and integration testing.
Unit tests focus on isolated service-layer business logic using:
- JUnit 5
- Mockito
- Mocked dependencies
- Behavior-focused assertions
Integration tests verify multiple real components working together.
The integration environment includes:
MockMvc
β
βΌ
Spring Security
β
βΌ
Controller
β
βΌ
Service
β
βΌ
Repository
β
βΌ
PostgreSQL Testcontainer
Integration coverage includes:
- Spring application context loading
- Authentication and JWT flows
- Refresh token behavior
- Logout/token revocation
- Task ownership
- Authorization boundaries
- Collection-level task isolation
- Unauthenticated access
- Rate limiting
- Actuator health
Run the complete test suite:
./mvnw testThe full suite has been verified successfully.
CloudTask uses Spring Boot Actuator + Micrometer + Prometheus + Grafana.
Health endpoint:
http://localhost:8080/actuator/health
Prometheus metrics:
http://localhost:8080/actuator/prometheus
Prometheus scrapes CloudTask every 15 seconds using:
app:8080/actuator/prometheus
Prometheus is available locally at:
http://localhost:9090
Grafana reads metrics from Prometheus and provides a monitoring dashboard.
Grafana is available locally at:
http://localhost:3000
The CloudTask dashboard currently includes:
| Panel | Purpose |
|---|---|
| CloudTask Availability | Detect whether the application is reachable |
| JVM Memory Usage | Monitor JVM memory consumption |
| HTTP Request Rate | Monitor incoming request traffic |
| HTTP 5xx Error Rate | Monitor server-side failures |
| HTTP Average Response Time | Monitor request latency |
| JVM Heap Usage % | Monitor heap utilization |
| Process CPU Usage % | Monitor application CPU consumption |
| JVM Live Threads | Monitor JVM thread count |
CloudTask
β
βΌ
Spring Boot Actuator
β
βΌ
/actuator/prometheus
β
βΌ
Prometheus
β
βΌ
Grafana
β
βΌ
CloudTask Monitoring Dashboard
The monitoring setup was tested by stopping and restarting the CloudTask application container.
Application Running
β
βΌ
Prometheus up = 1
β
βΌ
Stop CloudTask
β
βΌ
Prometheus up = 0
β
βΌ
Restart CloudTask
β
βΌ
Prometheus up = 1
This verifies that the monitoring system can detect both application failure and recovery.
The exported Grafana dashboard is stored in:
grafana/dashboards/cloudtask-dashboard.json
GitHub Actions runs the project's automated build and test process.
Git Push
β
βΌ
GitHub Actions CI
β
βΌ
Maven Build + Tests
Successful CI triggers deployment for pushes to main.
Push to main
β
βΌ
CI succeeds
β
βΌ
CD workflow
β
βΌ
SSH to EC2
β
βΌ
git fetch origin
β
βΌ
git reset --hard origin/main
β
βΌ
docker compose up -d --build
β
βΌ
Wait for application startup
β
βΌ
GET /actuator/health
β
ββββ΄ββββ
UP FAIL
β β
βΌ βΌ
Success Deployment fails
The CD workflow also collects Docker status and recent application logs when the health check fails.
CloudTask is deployed to AWS EC2 using Docker Compose.
GitHub
β
βΌ
GitHub Actions
β
βΌ
AWS EC2
β
βΌ
Docker Compose
β
βββ CloudTask API :8080
βββ PostgreSQL :5432
βββ Prometheus :9090
βββ Grafana :3000
The application is available at:
http://<EC2_PUBLIC_IP>:8080
Swagger UI:
http://<EC2_PUBLIC_IP>:8080/swagger-ui/index.html
The EC2 Security Group must allow the required inbound ports.
The deployment has been verified for:
- User registration
- User login
- JWT authentication
- Protected REST endpoints
- Task creation
- Task updates
- Task retrieval
- External access through the EC2 public IP
- PostgreSQL persistence after Docker restart
- Post-deployment health verification
cloud-task-api/
βββ .github/
β βββ workflows/
β βββ ci.yml
β βββ cd.yml
β
βββ grafana/
β βββ dashboards/
β βββ cloudtask-dashboard.json
β
βββ src/
β βββ main/
β β βββ java/
β β β βββ com/
β β β βββ mkalki/
β β β βββ cloudtaskapi/
β β β βββ audit/
β β β βββ config/
β β β βββ context/
β β β βββ controller/
β β β βββ dto/
β β β βββ entity/
β β β βββ exception/
β β β βββ ratelimit/
β β β βββ repository/
β β β βββ security/
β β β βββ service/
β β β βββ specification/
β β β
β β βββ resources/
β β βββ application.properties
β β βββ db/
β β βββ migration/
β β
β βββ test/
β
βββ compose.yaml
βββ Dockerfile
βββ prometheus.yml
βββ pom.xml
βββ README.md
Layer separation keeps responsibilities focused:
Controller β Service β Repository β Database
This improves maintainability, testability, and separation of concerns.
JWT provides stateless authentication:
- No server-side HTTP session required
- Token carries authenticated identity
- Works well for REST APIs
- Protected endpoints validate the token before access
Short-lived access tokens reduce exposure if an access token is compromised, while refresh tokens allow clients to obtain new access tokens without forcing frequent login.
CloudTask also supports refresh-token invalidation and reuse protection.
Testcontainers allows the integration tests to run against a real PostgreSQL database rather than a mocked database.
This verifies real interactions across:
Application
β JPA
β Hibernate
β PostgreSQL
The goal is to cover:
- Important application behavior
- Security boundaries
- Major success paths
- Important failure paths
Testing every possible parameter combination can increase maintenance cost without proportional value.
CloudTask uses a token-bucket model where each authenticated user gets a bucket keyed by user ID.
User ID
β
Bucket
β
Token available β request allowed
No token β 429
The production configuration currently allows 100 tokens with a 100-token refill over one minute.
Prometheus collects and stores numerical application metrics.
Grafana queries Prometheus and turns those metrics into dashboards.
Application
β
Actuator / Micrometer
β
Prometheus
β
Grafana
A successful Docker startup does not necessarily mean the application is ready to serve traffic.
The CD workflow therefore verifies:
Container Started
β
Application Started
β
/actuator/health
β
Deployment Success
- v2: Redis-based priority task scheduler β scheduled task execution (September 2026)
- Nginx + HTTPS β reverse proxy, TLS, and domain configuration
- Advanced observability β alerting and production dashboards
CloudTask is currently an educational and portfolio-oriented backend project.
Mohan R

