An open-source status page application for monitoring services, servers, and infrastructure. Built with simplicity, transparency, and self-hosting in mind.
- Service monitoring — Track component statuses (operational, degraded, partial outage, major outage)
- Incident management — Full lifecycle tracking (investigating → identified → monitoring → resolved)
- Scheduled maintenance — Plan and communicate maintenance windows
- Service grouping — Organize services by groups with customizable ordering
- Metrics tracking — Store and display uptime, response time, and error rate (30-day charts)
- Notifications — Email and webhook subscriber notifications on incidents
- Monitoring integrations — Inbound webhook for automated status updates from tools like Prometheus
- Admin dashboard — CRUD interface for managing services, incidents, and maintenance
- Dark mode — Automatic dark mode support via system preferences
- Docker ready — One-command setup with Docker Compose
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ Frontend │────▶│ Backend │────▶│ PostgreSQL │
│ Vue.js 3 │ │ Express.js │ │ 16 │
│ Tailwind │ │ Node.js 20 │ │ │
│ Chart.js │ │ JWT Auth │ │ │
└─────────────┘ └──────────────┘ └──────────────┘
:8080 :3000 :5432
For detailed diagrams (component tree, backend MVC, database ER, status flows), see docs/architecture.md.
- Frontend: Vue.js 3 + Vite + Tailwind CSS + Chart.js
- Backend: Node.js 20 + Express.js (MVC pattern) + JWT authentication
- Database: PostgreSQL 16
- Docker and Docker Compose
- Or: Node.js 20+ and PostgreSQL 16+
# Clone the repository
git clone http://devcentral.nasqueron.org/source/servpulse.git
cd servpulse
# Copy environment configuration
cp .env.example .env
# Edit .env with your settings (database credentials, JWT secret, SMTP config)
# Start all services
docker compose upThe application will be available at:
- Status page: http://localhost:8080
- API: http://localhost:3000/api
# Start PostgreSQL and create the database
psql -U postgres -c "CREATE DATABASE servpulse;"
psql -U postgres -d servpulse -f database/init.sql
# Backend
cd backend
cp .env.dist .env # Edit with your database connection string
npm install
npm run dev # Development mode with auto-reload
# Frontend (in another terminal)
cd frontend
npm install
npm run dev # Vite dev server at http://localhost:5173# Build and run with production Docker Compose
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -dSee Deployment Guide for detailed production setup.
servpulse/
├── backend/ # Express.js API server
│ ├── config/ # App config (app.json) and database connection
│ ├── controllers/ # Request handlers (service, incident, maintenance, metric, subscriber, webhook)
│ ├── middleware/ # JWT authentication middleware
│ ├── models/ # Data access layer (raw pg queries)
│ ├── routes/ # API route definitions
│ ├── services/ # Business logic (notification dispatch)
│ └── __tests__/ # Jest unit tests
├── frontend/ # Vue.js 3 application
│ ├── src/
│ │ ├── components/ # Reusable UI components
│ │ │ ├── AppNavbar.vue
│ │ │ ├── AppFooter.vue
│ │ │ ├── StatusBadge.vue
│ │ │ ├── OverallStatus.vue
│ │ │ ├── ServiceGroup.vue
│ │ │ ├── IncidentTimeline.vue
│ │ │ ├── MaintenanceCard.vue
│ │ │ ├── UptimeChart.vue
│ │ │ ├── SubscribeForm.vue
│ │ │ └── __tests__/ # Vitest component tests
│ │ ├── composables/ # Vue 3 composables (useServices, useIncidents, etc.)
│ │ ├── views/ # Page views (StatusPage, AdminDashboard, AdminLogin)
│ │ ├── plugins/ # API client (axios)
│ │ ├── utils/ # Status helpers and formatters
│ │ └── router/ # Vue Router config with auth guards
│ └── public/ # Static assets
├── database/ # SQL schema (init.sql)
├── docker-compose.yml # Development Docker Compose
├── docker-compose.prod.yml # Production Docker Compose
└── .env.example # Environment variable template
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/services |
List all services (ordered by group) |
| GET | /api/services/:id |
Get a single service |
| GET | /api/incidents |
List all incidents (newest first) |
| GET | /api/incidents/:id |
Get incident with updates and affected services |
| GET | /api/maintenances |
List all scheduled maintenances |
| GET | /api/maintenances/:id |
Get maintenance with affected services |
| GET | /api/metrics |
Get latest metrics per service |
| GET | /api/metrics/service/:id |
Get raw metrics for a service (query: ?days=30) |
| GET | /api/metrics/service/:id/daily |
Get daily metric summaries (query: ?days=30) |
| GET | /api/config/getAll |
Get app configuration (navbar, branding) |
| POST | /api/subscribers |
Subscribe to notifications (email or webhook) |
| GET | /api/subscribers/confirm/:token |
Confirm a subscription |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/verify |
Verify a JWT token |
| POST | /api/services |
Create a service |
| PUT | /api/services/:id |
Update a service |
| DELETE | /api/services/:id |
Delete a service |
| POST | /api/incidents |
Create an incident (supports service_ids) |
| PUT | /api/incidents/:id |
Update an incident (supports service_ids, message) |
| PUT | /api/incidents/:id/resolve |
Resolve an incident |
| POST | /api/maintenances |
Schedule maintenance (supports service_ids) |
| PUT | /api/maintenances/:id |
Update maintenance |
| DELETE | /api/maintenances/:id |
Delete maintenance |
| POST | /api/metrics |
Record a metric data point |
| GET | /api/subscribers |
List all subscribers |
| DELETE | /api/subscribers/:id |
Remove a subscriber |
| PUT | /api/config |
Update app configuration (navbar, branding) |
| POST | /api/webhooks/ingest |
Inbound monitoring webhook |
External monitoring tools can push status updates via POST /api/webhooks/ingest:
{
"service_name": "Web Server",
"status": "degraded",
"message": "High response times detected",
"impact": "minor"
}This automatically updates the service status and creates an incident if the status is non-operational.
| Variable | Default | Description |
|---|---|---|
POSTGRES_USER |
servpulse |
Database username |
POSTGRES_PASSWORD |
— | Database password |
POSTGRES_DB |
servpulse |
Database name |
POSTGRES_CONNECTION_STRING |
— | Full PostgreSQL connection string |
EXPRESS_PORT |
3000 |
Backend API port |
JWT_SECRET |
servpulse-dev-secret |
Secret for JWT token signing |
HEALTH_CHECK_INTERVAL |
60000 |
Health check interval in milliseconds |
VITE_API_URL |
http://localhost:3000/api |
API URL for frontend |
SMTP_HOST |
localhost |
SMTP server for email notifications |
SMTP_PORT |
587 |
SMTP port |
SMTP_SECURE |
false |
Use TLS for SMTP |
SMTP_USER |
— | SMTP username |
SMTP_PASS |
— | SMTP password |
SMTP_FROM |
ServPulse <noreply@servpulse.local> |
Sender address |
# Backend tests (Jest)
cd backend
npm test
# Frontend tests (Vitest)
cd frontend
npm run test:unit- Clone the repository on your server
- Copy
.env.exampleto.envand configure:- Set a strong
JWT_SECRET - Set
POSTGRES_PASSWORD - Configure SMTP for email notifications (optional)
- Set a strong
- Use the production Docker Compose:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
- Generate an admin JWT token:
docker compose exec backend node -e "const {generateToken} = require('./middleware/auth.js'); console.log(generateToken({role:'admin'}))"
- Use the token to log into the admin dashboard at
/admin/login
Note: Tokens are signed with
JWT_SECRET. Changing the secret invalidates all existing tokens — you must generate a new one. After changingJWT_SECRET, recreate the containers withdocker compose down && docker compose up -d.
For production, place Nginx in front to serve the frontend and proxy API requests:
server {
listen 80;
server_name status.example.com;
location / {
proxy_pass http://localhost:8080;
}
location /api/ {
proxy_pass http://localhost:3000;
}
}This project uses Phabricator for issue tracking (callsign: SP).
Code conventions: https://agora.nasqueron.org/Code_conventions
- Single quotes for strings
- camelCase for variables and functions
- Vue 3 Composition API (
<script setup>) for components - Tailwind CSS utility classes for styling