Self-hosted uptime monitoring & beautiful status pages
Monitor your websites, APIs, and services. Alert your team instantly. Show the world your uptime.
Status One is a lightweight, self-hosted uptime monitoring platform built by DeForge Labs. It provides real-time monitoring of your infrastructure, beautiful public status pages, intelligent incident management, and multi-channel notifications — all running on a single SQLite database with zero external dependencies.
| Type | Description |
|---|---|
| HTTP(S) | Monitor endpoints with custom methods, headers, body, auth (Basic/Bearer), and accepted status codes |
| Keyword | HTTP check + verify a keyword exists (or doesn't) in the response body |
| Ping | ICMP ping monitoring |
| TCP | TCP port connectivity checks |
| DNS | DNS record resolution (A, AAAA, CNAME, MX, TXT, etc.) |
| SSL | SSL certificate expiry monitoring with configurable warning threshold |
| Push / Heartbeat | Passive monitoring — your services push heartbeats to Status One |
Additional capabilities:
- Configurable check intervals (10s – 24h), timeouts, and retry logic
- Degraded state detection (response time threshold)
- Per-monitor notification channel linking
- Tag-based organization with color-coded labels
- Pause/resume and one-off test checks
- Status badges for individual monitors
- Public-facing pages at
/status/your-slug— no login required - Custom domain support — point your domain and it resolves automatically
- Branding — custom logo, header text, footer text, and injected CSS
- Light & dark themes per page
- 90-day uptime history visualization with daily bars
- Active incidents & maintenance display with live updates
- Embeddable uptime badges — 5 SVG styles with Markdown/HTML snippets
- Auto-refresh every 60 seconds
- Published / draft toggle for controlling visibility
- Auto-detection — incidents created automatically after consecutive check failures
- Auto-resolution — incidents resolved when monitor recovers
- Manual incidents — create and track incidents independently
- Status workflow — Investigating → Identified → Monitoring → Resolved
- Timeline updates — add detailed status messages to track progress
- Notifications sent on creation, updates, and resolution
- Scheduled maintenance periods with start/end times
- Per-monitor or global scope
- Recurring schedules via cron expressions
- Monitors in maintenance are automatically skipped (no false alerts)
| Channel | Highlights |
|---|---|
| SMTP-based HTML emails with status-prefixed subjects | |
| Discord | Color-coded rich embed notifications via webhooks |
| Telegram | Bot notifications with subscriber management (/start, /stop) |
| Webhook | JSON payloads with HMAC-SHA256 signature verification |
| Slack | Slack-compatible webhook integration |
Events: Monitor Down, Recovered, Degraded, Incident Updates, and Test notifications.
- Dashboard overview with real-time status of all monitors
- 24h / 7d / 30d / 90d uptime percentages and average response times
- Response time charts (area charts with gradient fills)
- Daily stats aggregation via cron (total checks, up/down/degraded counts, avg/min/max response)
- Configurable data retention (default: 90 days raw, 365 days aggregated)
- First-run setup wizard — guided admin account creation
- Multi-user support with safety guards (can't delete last admin)
- JWT authentication (7-day expiry) for the web dashboard
- API key authentication (
X-API-Key) for external integrations - Password reset via email with time-limited tokens
- Rate limiting — 200 req/min global, 20 req/min on auth endpoints
- Health check endpoint (public, no auth)
- System info (version, runtime, DB size, monitor counts, memory)
- Database backup (SQLite
VACUUM INTO) - Purge old check data
- Factory reset with confirmation safeguard
- Configurable application settings (app name, URL, retention periods, cron schedules)
| Layer | Technology |
|---|---|
| Backend Runtime | Bun |
| Backend Framework | Express.js |
| Database | SQLite (WAL mode, zero-config) |
| Frontend | Next.js 16 (App Router) + React 19 |
| Styling | Tailwind CSS v4 |
| Charts | Recharts |
| Icons | Lucide React |
The fastest way to get Status One running. Requires Docker and Docker Compose.
git clone https://github.com/DeForge-Labs/status-one.git
cd status-onecp .env.example .envEdit .env and set at minimum:
# IMPORTANT: Generate a strong random secret
JWT_SECRET=your-random-secret-here
# URL the browser uses to reach the backend API
# Use your server IP or domain if not running locally
NEXT_PUBLIC_API_URL=http://localhost:3000/apiTip: Generate a JWT secret with:
openssl rand -hex 32
docker compose build
docker compose up -dThat's it! Open http://localhost:3001 in your browser. You'll be guided through the setup wizard to create your admin account.
docker compose pull
docker compose up -d| Volume | Purpose |
|---|---|
status-one-data |
SQLite database and backups — back this up! |
cd backend
cp .env.example .env # Edit with your settings
bun install
bun run start # Production
bun run dev # Development (hot reload)The backend starts on http://localhost:3000 by default.
cd frontend
cp env.local.example .env.local # Set NEXT_PUBLIC_API_URL
npm install
npm run build && npm start # Production
npm run dev # DevelopmentThe frontend starts on http://localhost:3001 by default.
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
Backend HTTP port |
JWT_SECRET |
change-me-to-a-random-secret |
Change this! Secret for JWT signing |
SMTP_HOST |
(empty) | SMTP server hostname |
SMTP_PORT |
587 |
SMTP server port |
SMTP_SECURE |
false |
Use TLS for SMTP |
SMTP_USER |
(empty) | SMTP username |
SMTP_PASS |
(empty) | SMTP password |
SMTP_FROM |
Status One <noreply@example.com> |
Sender address for emails |
| Variable | Default | Description |
|---|---|---|
NEXT_PUBLIC_API_URL |
http://localhost:3000/api |
Backend API URL (used by browser) |
MAIN_DOMAINS |
(empty) | Domains not to be used for status pages (basically dashboard url) |
For production deployments behind Nginx or Caddy:
Nginx example
server {
listen 80;
server_name status.yourdomain.com;
# Frontend
location / {
proxy_pass http://127.0.0.1:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Backend API
location /api/ {
proxy_pass http://127.0.0.1:3000/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Caddy example
status.yourdomain.com {
handle /api/* {
reverse_proxy localhost:3000
}
handle {
reverse_proxy localhost:3001
}
}When using a reverse proxy, update your .env:
NEXT_PUBLIC_API_URL=https://status.yourdomain.com/apistatus-one/
├── backend/ # Bun + Express API server
│ ├── src/
│ │ ├── database/ # SQLite connection, migrations, seeds
│ │ ├── middleware/ # Auth, rate limiting, error handling
│ │ ├── models/ # Data models (monitors, incidents, etc.)
│ │ ├── routes/ # API route handlers
│ │ ├── services/ # Monitor engine, notifiers, analytics
│ │ │ └── checkers/ # HTTP, TCP, Ping, DNS, SSL, Keyword checks
│ │ └── utils/ # Crypto, helpers, logger, validators
│ └── data/ # SQLite database files (auto-created)
├── frontend/ # Next.js 16 web application
│ ├── app/ # App Router pages
│ │ ├── (auth)/ # Login, setup, password reset
│ │ ├── (dashboard)/ # Dashboard, monitors, incidents, etc.
│ │ └── status/[slug]/ # Public status pages
│ ├── components/ # Reusable UI components
│ ├── contexts/ # Auth & theme providers
│ ├── hooks/ # Custom hooks (polling)
│ └── lib/ # API client & utilities
├── docker-compose.yml # One-click deployment
└── .github/workflows/ # CI/CD pipeline
Status One is open-source software licensed under the GNU General Public License v3.0.
This project was mostly vibe coded.
Also, you might ask, why does this project exist ? The reason is, we were looking for a self hostable solution for status page. There are quite a few options in that regard. We chose uptime kuma for our status page. We were even using it in our beta phase. But it has a major drawback, no incident retention. So, we planned on switching to something else. We were thinking of Gatus but then decided on openstatus. But I failed at hosting openstatus because there were just too many complications and I just couldn't fix everything. Hence, status-one was made as a simple alternative with just the features we need.
Made with care by DeForge Labs