A distributed uptime-monitoring platform that checks websites and APIs, tracks latency and outages, and sends outage and recovery notifications.
PulseWatch monitors websites and APIs to answer three questions:
- Is the service available? Monitors transition through
PENDING,UP,DEGRADED, andDOWN. - How quickly is it responding? Each health check records response latency.
- When did outages occur? Consecutive failures create incidents that are resolved when the service recovers.
Users create monitors through a Next.js dashboard. A Spring Boot scheduler identifies due checks and publishes tasks to RabbitMQ. Workers perform HTTP checks, persist results to PostgreSQL, update monitor state, create and resolve incidents, and trigger outage or recovery notifications through Amazon SES.
- Create, edit, and delete uptime monitors
- Scheduled HTTP health checks
- Response latency tracking
- HTTP and network-error classification
PENDING,UP,DEGRADED, andDOWNmonitor states- Consecutive-failure outage detection
- Historical check results
- Automatic incident creation and recovery
- Amazon SES outage and recovery email alerts
- Live dashboard polling
- Latency history visualization
- Incident history
- Dockerized development environment
- Playwright end-to-end testing
- GitHub Actions continuous integration
- Checkstyle and SpotBugs static analysis
┌─────────────────┐
│ Next.js │
│ Frontend │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Spring Boot │
│ Backend API │
└────────┬────────┘
│
▼
┌─────────────────┐
│ PostgreSQL │
└─────────────────┘
┌─────────────────┐
│ Scheduler │
└────────┬────────┘
│
▼
┌─────────────────┐
│ RabbitMQ │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Worker │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Target Website │
│ or API │
└────────┬────────┘
│
▼
CheckResult
│
▼
Monitor / Incident / Alert
Alert Record
│
▼
┌─────────────────┐
│ Amazon SES │
└────────┬────────┘
│
▼
Email
Frontend
- Displays monitors and their current status
- Provides monitor CRUD operations
- Displays latency history and incidents
- Periodically refreshes monitoring data
Backend API
- Handles monitor CRUD requests
- Validates monitor configuration
- Exposes check and incident history
- Coordinates persistence and application logic
Scheduler
- Finds monitors whose next check is due
- Creates monitoring tasks
- Publishes tasks to RabbitMQ
RabbitMQ
- Decouples scheduling from HTTP checks
- Buffers monitoring tasks for workers
Worker
- Consumes monitoring tasks
- Sends HTTP requests to target services
- Measures latency
- Classifies HTTP and network failures
- Stores check results
- Updates monitor state
- Creates and resolves incidents
PostgreSQL
- Stores monitor configuration
- Stores historical check results
- Stores incidents and alert state
Amazon SES
- Delivers outage and recovery email notifications
A user creates a monitor with information such as:
Name
URL
Check interval
Request timeout
New monitors begin in the PENDING state.
The first health check waits until the monitor's scheduled check time instead of running immediately.
The scheduler periodically searches for monitors where:
nextCheckAt <= currentTime
For every due monitor, it creates a task containing the information needed by a worker and publishes that task to RabbitMQ.
The scheduler itself does not perform HTTP requests.
Monitor
↓
Scheduler
↓
RabbitMQ
A worker consumes the task from RabbitMQ and performs the HTTP request.
RabbitMQ
↓
Worker
↓
Target Website/API
The worker records information such as:
Checked time
HTTP status code
Latency
Network error
The result is persisted as a CheckResult.
PulseWatch uses consecutive failures to avoid declaring an outage after a single temporary error.
Successful check
↓
Reset consecutive failures
↓
UP
A failed check below the outage threshold produces:
Failed check
↓
Increment failure count
↓
DEGRADED
Once the failure threshold is reached:
Failed check
↓
Threshold reached
↓
DOWN
↓
Create Incident
↓
Create OUTAGE alert
The current MVP uses three consecutive failures before transitioning a monitor to DOWN.
When a monitor that is currently down succeeds again:
Successful check
↓
Reset failure count
↓
UP
↓
Resolve open Incident
↓
Create RECOVERY alert
A continuous outage is represented by one incident instead of creating a new incident for every failed check.
Outage and recovery alerts are persisted before notification delivery.
Incident state change
↓
Create Alert
↓
Amazon SES
↓
Email
This keeps monitor health state separate from notification delivery.
For example, an email delivery problem does not change whether the monitored service is considered UP or DOWN.
Each monitor provides:
- Current health state
- Latest HTTP response
- Latest latency
- Consecutive failure count
- Recent health checks
- Latency history
- Incident history
- Edit and delete controls
PulseWatch sends outage and recovery notifications through Amazon SES.
- Java 21
- Spring Boot
- Spring Data JPA
- Maven
- Next.js
- React
- TypeScript
- Playwright
- PostgreSQL
- RabbitMQ
- Amazon SES
- Docker
- Docker Compose
- GitHub Actions
- Checkstyle
- SpotBugs
PulseWatch separates current monitor state from historical checks and outage incidents.
Monitor
│
├───────────────┐
│ │
▼ ▼
CheckResult Incident
│
▼
Alert
Represents a website or API being monitored.
Important fields include:
id
name
url
checkIntervalSeconds
timeoutSeconds
nextCheckAt
status
consecutiveFailureCount
Represents one HTTP monitoring attempt.
id
taskId
monitorId
checkedAt
statusCode
latencyMs
error
Represents a continuous outage.
id
monitorId
startedAt
endedAt
An incident with:
endedAt = NULL
is still active.
Represents a notification associated with an outage or recovery.
Alert types:
OUTAGE
RECOVERY
Delivery states:
PENDING
SENT
FAILED
POST /monitors
GET /monitors
GET /monitors/{id}
PATCH /monitors/{id}
DELETE /monitors/{id}GET /monitors/{id}/checks?limit=50
GET /monitors/{id}/incidents?limit=10Example:
curl http://localhost:8080/monitorsInstall:
- Docker Desktop
- Docker Compose
- Java 21
- Maven
- Node.js
- npm
git clone https://github.com/chill-one/Pulse-Watch.git
cd Pulse-WatchCreate your local environment file:
cp .env.example .envConfigure the required values:
POSTGRES_PASSWORD=your_password
RABBITMQ_USER=pulsewatch
RABBITMQ_PASSWORD=your_password
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=your_access_key
AWS_SECRET_ACCESS_KEY=your_secret_key
PULSEWATCH_EMAIL_FROM=your-verified-email@example.com
PULSEWATCH_EMAIL_TO=your-email@example.comKeep .env private and never commit credentials to Git.
Amazon SES requires a verified sending identity before real email notifications can be delivered.
From the repository root:
docker compose up -d --build backendDocker Compose starts the backend along with its PostgreSQL and RabbitMQ dependencies.
View backend logs:
docker compose logs -f backendVerify that the API is running:
curl http://localhost:8080/monitorsA fresh database may return:
[]Start the monitoring worker:
docker compose up -d --build workerView worker logs:
docker compose logs -f workerFor local frontend development:
cd frontend
npm ci
npm run devOpen:
http://localhost:3000
When the frontend runs directly on the host, it connects to the backend at:
http://localhost:8080
When running inside Docker, the frontend should use the Compose backend service hostname:
http://backend:8080
Stop running containers:
docker compose downAvoid:
docker compose down -vunless you intentionally want to delete PostgreSQL and RabbitMQ volumes and reset local data.
From the repository root:
mvn verifyThe backend pipeline includes Java tests and configured quality checks.
Individual static-analysis checks can also be run separately.
Checkstyle:
mvn checkstyle:checkSpotBugs:
mvn -Dcheckstyle.skip compile spotbugs:checkFrom the frontend directory:
npm ci
npm run lint
npm run buildPulseWatch uses Playwright for browser-level end-to-end testing.
Make sure the backend is running on port 8080, then from frontend run:
npm run test:e2eThe current E2E suite includes:
- A dashboard smoke test
- A full monitor CRUD workflow
The CRUD test exercises:
Open Dashboard
↓
Create Monitor
↓
View Monitor
↓
Edit Monitor
↓
Verify Updated Monitor
↓
Delete Monitor
↓
Verify Removal
The E2E tests use the real Spring Boot backend and database rather than mocking API requests.
They intentionally avoid asserting asynchronous health-state changes because scheduler and worker processing occurs independently of the browser interaction.
PulseWatch includes load and performance tests for both the REST API and the asynchronous monitoring pipeline.
Using k6, the GET /monitors endpoint was tested under sustained load in a local containerized environment.
- Sustained 4,500 requests/second for 60 seconds
- Completed 270,006 requests
- 3.95 ms p95 latency
- 0% HTTP errors
- 0 dropped iterations
Higher-load tests were also used to identify the saturation region of the local test environment.
The asynchronous monitoring pipeline was tested using a controlled RabbitMQ backlog and a local containerized HTTP target.
RabbitMQ
↓
Worker
↓
HTTP Check
↓
CheckResult
↓
PostgreSQL
GitHub Actions automatically validates the project on pushes and pull requests.
The CI pipeline includes:
Checkstyle
│
┌──────────────┼──────────────┐
│ │ │
▼ ▼ ▼
Java Tests SpotBugs Docker Compose
│
▼
Frontend ESLint
│
▼
Frontend Build
│
▼
Playwright E2E
The Playwright CI environment starts:
PostgreSQL
RabbitMQ
Spring Boot Backend
Next.js Frontend
Chromium
before exercising the application through the browser.
This provides coverage across the frontend, API, database, and supporting infrastructure.
HTTP monitoring work can be slow or temporarily blocked by network conditions.
The scheduler should remain focused on deciding when checks should run rather than performing the checks itself.
PulseWatch therefore separates:
Scheduling
↓
Queueing
↓
Execution
using:
Scheduler → RabbitMQ → Worker
This reduces coupling between scheduling and network execution.
A CheckResult represents one observation.
"This HTTP request failed."
A monitor's status represents PulseWatch's current interpretation of multiple observations.
For example:
1st failure
CheckResult = failed
Monitor = DEGRADED
versus:
3rd consecutive failure
CheckResult = failed
Monitor = DOWN
Keeping these concepts separate preserves monitoring history while allowing state transitions to use multiple checks.
Several failed checks may belong to the same outage.
Without incidents:
Failure
Failure
Failure
Failure
could appear as four unrelated outage events.
Instead:
Outage starts
↓
Repeated failures
↓
Service recovers
is represented as one Incident with a start and end time.
Monitoring state and email delivery solve different problems.
PulseWatch first records the monitoring event and alert state before attempting email delivery.
Monitor state change
↓
Incident
↓
Alert
↓
Amazon SES
A notification failure should not change whether the target service is considered healthy.
The MVP periodically refreshes dashboard data.
Monitoring updates are relatively infrequent, so polling keeps the initial implementation simple without introducing persistent connection management.
Possible future alternatives include:
- Server-Sent Events
- WebSockets
PulseWatch currently uses four monitor states.
The monitor has been created but its first scheduled health check has not completed.
The latest health evaluation indicates the target is available.
One or more checks have failed, but the consecutive-failure threshold has not been reached.
The failure threshold has been reached and an outage incident is active.
Typical transition:
PENDING
↓
UP
↓
DEGRADED
↓
DOWN
↓
UP
PulseWatch's architecture is designed around several reliability concepts:
- Scheduled checks are separated from network execution
- RabbitMQ buffers monitoring work
- Request timeouts prevent checks from waiting indefinitely
- Network errors are recorded separately from HTTP responses
- Consecutive failures reduce false outage detection
- Check history is stored separately from current monitor state
- Incidents represent continuous outages
- Alert delivery is separated from monitor health state
- Related state changes use database transactions
PulseWatch currently supports the complete monitoring lifecycle:
Create Monitor
↓
Schedule Check
↓
Publish Task
↓
Worker Checks Service
↓
Store CheckResult
↓
Update Health State
↓
Detect Outage
↓
Create Incident
↓
Send Alert
↓
Display Monitoring Data
Potential post-MVP improvements include:
- Load and performance testing
- Cloud deployment
- Terraform infrastructure
- Database migrations
- Authentication and user ownership
- Server-Sent Events for live updates
- Public status pages
- Additional notification channels
- Alert retry and deduplication
- Distributed scheduler coordination
- Rate limiting
- Per-domain throttling
- Data-retention policies
- Metrics and observability
Additional project design material is available in the design directory, including:
- System architecture diagrams
- Component diagrams
- Class diagrams
- User-flow diagrams
- Design notes
- Technology decision documentation
This project was built for educational and portfolio purposes.




