Finally got a cursor subscription so wanted to build something cool. NanoStatus is a lightweight, single-container monitoring dashboard built with Go and React. Monitor your services' uptime, response times, and health in real-time with a beautiful, modern interface. I personally made this for my PiZero, UptimeKuma was a little too heavy so wanted to make something a little lighter.
- π Single Binary Deployment - Everything embedded in one Go binary
- π Real-time Monitoring - Server-Sent Events (SSE) for instant updates
- π¨ Modern UI - Sleek dark theme with glassmorphism effects and smooth animations
- π Response Time Charts - Historical data with multiple time frames (1h, 12h, 1w, 1y)
- π Service Management - Add, edit, pause, and delete monitors
- π± Fully Responsive - Works beautifully on desktop, tablet, and mobile
- πΎ SQLite Database - Lightweight persistence with automatic cleanup
- β‘ Efficient Updates - Only sends data when values actually change
- π― Customizable Intervals - Set individual check intervals per service
- π§Ή Auto Cleanup - Automatically removes check history older than 1 year
-
Build the Docker image:
docker build -t nanostatus . -
Run the container:
docker run -p 8080:8080 -v "$(pwd)/data:/data" nanostatusThe application will be available at
http://localhost:8080Note: The
-v "$(pwd)/data:/data"flag persists the database across container restarts. -
Access the dashboard: Open
http://localhost:8080in your browser
-
Build the frontend:
cd src bun install bun run build.ts --outdir=../dist cd ..
-
Run the Go server:
go run main.go
Or build and run:
go build -o nanostatus main.go ./nanostatus
-
Access the application: Open http://localhost:8080 in your browser
make build # Build both frontend and backend
make run # Build and run the application
make dev-frontend # Run frontend dev server (with hot reload)
make dev-backend # Run Go server (requires built frontend)
make clean # Clean build artifacts- Backend: Go HTTP server with embedded frontend static files
- Frontend: React with TypeScript, Tailwind CSS, and Framer Motion
- Database: SQLite with GORM ORM
- Real-time Updates: Server-Sent Events (SSE) for efficient streaming
- Background Jobs:
- Automatic service health checks based on individual intervals
- Daily cleanup of check history older than 1 year (runs at midnight)
- Services are checked via actual HTTP requests
- Response times are measured and stored in the database
- Uptime is calculated from the last 24 hours of check history
- Stats are only calculated and broadcast when values change
- Updates are streamed to clients via SSE (no polling needed)
- Check history is stored in SQLite for historical analysis
- Automatic cleanup runs daily at 12:00 AM to remove data older than 1 year
- Database uses WAL mode for better concurrency
- All data persists in
/datavolume when using Docker
GET /api/monitors- List all monitorsPOST /api/monitors/create- Create a new monitorGET /api/stats- Get overall statistics (only unpaused services)GET /api/response-time?id=<id>&range=<range>- Get response time historyrangeoptions:1h,12h,24h,1w,1y(default:24h)
GET /api/monitor?id=<id>- Get specific monitor detailsPUT /api/monitor?id=<id>- Update a monitor or toggle pause stateDELETE /api/monitor?id=<id>- Delete a monitor
GET /api/events- Real-time event stream- Event types:
monitor_update,monitor_added,monitor_deleted,stats_update - Automatically reconnects on connection loss
- Keepalive messages every 30 seconds
- Event types:
PORT- Server port (default:8080)DB_PATH- Database file path- Default:
./nanostatus.db(local) or/data/nanostatus.db(Docker)
- Default:
You can pre-populate monitors using a YAML configuration file. Place monitors.yaml in the same directory as your database file.
Example monitors.yaml (in the same directory as your database):
monitors:
- name: "Example.com"
url: "https://example.com"
icon: "π"
checkInterval: 60
isThirdParty: false
paused: false
- name: "Google"
url: "https://google.com"
icon: "π"
checkInterval: 30
isThirdParty: true
paused: false
- name: "GitHub"
url: "https://github.com"
icon: "π»"
checkInterval: 120
isThirdParty: true
paused: falseConfiguration Fields:
name(required) - Display name for the serviceurl(required) - Full URL to monitor (e.g.,https://example.com)icon(optional) - Emoji icon to displaycheckInterval(optional) - How often to check in seconds (default: 60)isThirdParty(optional) - Whether this is a third-party service (default: false)paused(optional) - Whether monitoring should start paused (default: false)
Location:
- The YAML file must be named
monitors.yamland placed in the same directory as your database - For Docker: If
DB_PATH=/data/nanostatus.db, place the file at/data/monitors.yaml - For local: If database is at
./nanostatus.db, place the file at./monitors.yaml
How It Works:
- The YAML configuration is synchronized on every server startup
- Each monitor from YAML gets a hash calculated from its configuration
- The system compares hashes to detect changes:
- New monitors in YAML are created
- Changed monitors (different hash) are updated (preserving runtime data like status and uptime)
- Removed monitors (no longer in YAML) are deleted
- Unchanged monitors are left as-is
- Monitors created via the UI/API are not managed by YAML and won't be modified
- If a monitor with the same name/URL exists but was created via UI/API, the YAML version will be skipped to avoid duplicates
When creating a monitor via the UI or API, you can configure:
- Name: Display name for the service
- URL: Full URL to monitor (e.g.,
https://example.com) - Icon: Optional emoji icon
- Check Interval: How often to check (10-3600 seconds, default: 60)
- Third-party Service: Flag for external services
- Add Services: Create new monitors with custom check intervals
- Edit Services: Update name, URL, icon, and check interval
- Pause/Resume: Temporarily disable monitoring for specific services
- Delete Services: Remove monitors and their history
- SSE Streaming: All updates pushed instantly to connected clients
- Change Detection: Stats only calculated and sent when values change
- Debouncing: Rapid monitor updates are batched for efficiency
- No Polling: Frontend receives updates via SSE, eliminating HTTP polling
- Multiple Time Frames: View data for 1 hour, 12 hours, 1 week, or 1 year
- Interactive Charts: Beautiful area charts with gradient fills
- Time-based Formatting: Labels adapt to the selected time range
- Overall Uptime: Average uptime across all unpaused services
- Service Counts: Number of services online/offline (unpaused only)
- Average Response Time: Calculated from last 24 hours of check history
- Real-time Updates: Stats update automatically when monitors change
.
βββ main.go # Main server entry point and routing
βββ models.go # Data models and structures
βββ database.go # Database initialization and seeding
βββ config.go # YAML configuration loader
βββ checker.go # Service health checking logic
βββ stats.go # Statistics calculation
βββ sse.go # Server-Sent Events broadcasting
βββ handlers.go # HTTP API endpoint handlers
βββ cleanup.go # Background cleanup jobs
βββ go.mod # Go dependencies
βββ go.sum # Go dependency checksums
βββ Dockerfile # Standard multi-stage Docker build (distroless)
βββ Dockerfile.minimal # Minimal Docker build with UPX compression (scratch)
βββ Makefile # Build automation
βββ monitors.yaml.example # Example YAML configuration file
βββ dist/ # Frontend build output (generated)
βββ nanostatus.db # SQLite database (generated)
βββ monitors.yaml # YAML config (optional, same dir as DB)
βββ src/ # Frontend source code
β βββ src/
β β βββ App.tsx # Main React component
β β βββ components/ # React components
β β β βββ Header.tsx
β β β βββ StatsGrid.tsx
β β β βββ ServicesGrid.tsx
β β β βββ ServiceCard.tsx
β β β βββ MonitorDetails.tsx
β β β βββ AddServiceDialog.tsx
β β β βββ EditServiceDialog.tsx
β β β βββ ui/ # shadcn/ui components
β β βββ types/ # TypeScript type definitions
β β βββ ...
β βββ package.json
βββ README.md
# Build frontend
cd src
bun install
bun run build.ts --outdir=../dist
# Build backend
go build -o nanostatus main.goThe Dockerfile uses a multi-stage build:
- Frontend Builder: Builds React app with Bun
- Backend Builder: Compiles Go binary with embedded frontend
- Final Stage: Minimal distroless image (no shell, no package manager)
Result: Ultra-small, secure container (~16MB)
See LICENSE file for details.
Built with:
- Go - Backend server
- React - Frontend framework
- Bun - JavaScript runtime and package manager
- Tailwind CSS - Styling
- Framer Motion - Animations
- Recharts - Charting
- shadcn/ui - UI components
- GORM - ORM
- SQLite - Database
The favicon/logo is from the small-n-flat icon set, licensed under CC0 1.0 Universal. See ICON_LICENSE for full license text.
