This update introduces optional automatic discovery and onboarding of all databases on a PostgreSQL instance. It also enhances the frontend UI with grouped target display and navigation, making it easier to view and manage related databases. Additionally, new backend endpoints and logic ensure seamless integration of these features.
NexaPG
NexaPG is a full-stack PostgreSQL monitoring platform for multiple remote targets. It combines FastAPI, React, and PostgreSQL in a Docker Compose stack with RBAC, polling collectors, query insights, alerting, and target-owner email notifications.
Table of Contents
- Quick Start
- Prerequisites
- Make Commands
- Configuration Reference (
.env) - Core Functional Areas
- Target Owner Notifications
- API Overview
pg_stat_statementsRequirement- Reverse Proxy / SSL Guidance
- PostgreSQL Compatibility Smoke Test
- Troubleshooting
- Security Notes
Highlights
- Multi-target monitoring for remote PostgreSQL instances
- Optional one-click target onboarding for "all databases" discovery on an instance
- PostgreSQL compatibility support:
14,15,16,17,18 - JWT auth (
access+refresh) and RBAC (admin,operator,viewer) - Polling collector for metrics, locks, activity, and optional
pg_stat_statements - Target detail overview (instance, storage, replication, core performance metrics)
- Alerts system:
- standard built-in alerts
- custom SQL alerts (admin/operator)
- warning + alert severities
- real-time UI updates + toast notifications
- Target owners: alert emails are sent only to responsible users assigned to a target
- SMTP settings in admin UI (send-only) with test mail support
- Structured backend logs + audit logs
UI Preview
Dashboard
Targets Management
Query Insights
Alerts
Admin Settings
Target Detail (DBA Mode)
Target Detail (Easy Mode)
Repository Layout
backend/FastAPI app, SQLAlchemy async models, Alembic migrations, collector servicesfrontend/React + Vite UIops/helper files/scriptsdocker-compose.ymlfull local stack.env.examplecomplete environment templateMakefilecommon commands
Prerequisites
- Docker Engine
24+ - Docker Compose
v2+ - GNU Make (optional but recommended)
- Open host ports (or custom values in
.env):FRONTEND_PORT(default5173)BACKEND_PORT(default8000)DB_PORT(default5433)
Optional:
psqlfor manual DB checks
Quick Start
- Copy environment template:
cp .env.example .env
- Generate a Fernet key and set
ENCRYPTION_KEYin.env:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
- Start the stack:
make up
- Open the application:
- Frontend:
http://<SERVER_IP>:<FRONTEND_PORT> - API base:
http://<SERVER_IP>:<BACKEND_PORT>/api/v1 - OpenAPI:
http://<SERVER_IP>:<BACKEND_PORT>/docs
Initial admin bootstrap user (created from .env if missing):
- Email: value from
INIT_ADMIN_EMAIL - Password: value from
INIT_ADMIN_PASSWORD
Make Commands
make up # build and start all services
make down # stop all services
make logs # follow compose logs
make migrate # optional/manual: run alembic upgrade head in backend container
Note: Migrations run automatically when the backend container starts (entrypoint.sh).
Configuration Reference (.env)
Application
| Variable | Description |
|---|---|
APP_NAME |
Application display name |
ENVIRONMENT |
Runtime environment (dev, staging, prod, test) |
LOG_LEVEL |
Backend log level (DEBUG, INFO, WARNING, ERROR) |
Core Database
| Variable | Description |
|---|---|
DB_NAME |
Core metadata database name |
DB_USER |
Core database user |
DB_PASSWORD |
Core database password |
DB_PORT |
Host port mapped to internal PostgreSQL 5432 |
Backend / Security
| Variable | Description |
|---|---|
BACKEND_PORT |
Host port mapped to backend container port 8000 |
JWT_SECRET_KEY |
JWT signing secret |
JWT_ALGORITHM |
JWT algorithm (default HS256) |
JWT_ACCESS_TOKEN_MINUTES |
Access token lifetime in minutes |
JWT_REFRESH_TOKEN_MINUTES |
Refresh token lifetime in minutes |
ENCRYPTION_KEY |
Fernet key for target credentials and SMTP password encryption |
CORS_ORIGINS |
Allowed CORS origins (comma-separated or * for dev only) |
POLL_INTERVAL_SECONDS |
Collector polling interval |
INIT_ADMIN_EMAIL |
Bootstrap admin email |
INIT_ADMIN_PASSWORD |
Bootstrap admin password |
Alert Noise Tuning
| Variable | Description |
|---|---|
ALERT_ACTIVE_CONNECTION_RATIO_MIN_TOTAL_CONNECTIONS |
Minimum total sessions required before evaluating active-connection ratio |
ALERT_ROLLBACK_RATIO_WINDOW_MINUTES |
Time window for rollback ratio evaluation |
ALERT_ROLLBACK_RATIO_MIN_TOTAL_TRANSACTIONS |
Minimum transaction volume before rollback ratio is evaluated |
ALERT_ROLLBACK_RATIO_MIN_ROLLBACKS |
Minimum rollback count before rollback ratio is evaluated |
Frontend
| Variable | Description |
|---|---|
FRONTEND_PORT |
Host port mapped to frontend container port 80 |
VITE_API_URL |
Frontend API base URL (build-time) |
Recommended values for VITE_API_URL:
- Reverse proxy setup:
/api/v1 - Direct backend access:
http://<SERVER_IP>:<BACKEND_PORT>/api/v1
Core Functional Areas
Targets
- Create, list, edit, delete targets
- Test target connection before save
- Optional "discover all databases" mode (creates one monitored target per discovered DB)
- Configure SSL mode per target
- Toggle
pg_stat_statementsusage per target - Assign responsible users (target owners)
Target Details
- Database Overview section with instance, role, uptime, size, replication, and core metrics
- Metric charts with range selection and live mode
- Locks and activity tables
Query Insights
- Uses collected
pg_stat_statementsdata - Ranking and categorization views
- Search and pagination
- Disabled automatically for targets where query insights flag is off
Alerts
- Warning and alert severity split
- Expandable alert cards with details and recommended actions
- Custom alert definitions (SQL + thresholds)
- Real-time refresh and in-app toast notifications
Admin Settings
- User management (RBAC)
- SMTP settings for outgoing alert mails:
- enable/disable
- host/port/auth
- STARTTLS / SSL mode
- from email + from name
- recipient test mail
Target Owner Notifications
Email alert routing is target-specific:
- only users assigned as owners for a target receive that target's alert emails
- supports multiple owners per target
- notification sending is throttled to reduce repeated alert spam
API Overview
Health
GET /api/v1/healthzGET /api/v1/readyz
Auth
POST /api/v1/auth/loginPOST /api/v1/auth/refreshPOST /api/v1/auth/logoutGET /api/v1/me
Targets
GET /api/v1/targetsPOST /api/v1/targetsPOST /api/v1/targets/test-connectionGET /api/v1/targets/{id}PUT /api/v1/targets/{id}DELETE /api/v1/targets/{id}GET /api/v1/targets/{id}/ownersPUT /api/v1/targets/{id}/ownersGET /api/v1/targets/owner-candidatesGET /api/v1/targets/{id}/metricsGET /api/v1/targets/{id}/locksGET /api/v1/targets/{id}/activityGET /api/v1/targets/{id}/top-queriesGET /api/v1/targets/{id}/overview
Alerts
GET /api/v1/alerts/statusGET /api/v1/alerts/definitionsPOST /api/v1/alerts/definitionsPUT /api/v1/alerts/definitions/{id}DELETE /api/v1/alerts/definitions/{id}POST /api/v1/alerts/definitions/test
Admin
GET /api/v1/admin/usersPOST /api/v1/admin/usersPUT /api/v1/admin/users/{user_id}DELETE /api/v1/admin/users/{user_id}GET /api/v1/admin/settings/emailPUT /api/v1/admin/settings/emailPOST /api/v1/admin/settings/email/test
pg_stat_statements Requirement
Query Insights requires pg_stat_statements on the monitored target:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
If unavailable, disable it per target in target settings.
Reverse Proxy / SSL Guidance
For production, serve frontend and API under the same public origin via reverse proxy.
- Frontend URL example:
https://monitor.example.com - Proxy API path
/api/to backend service - Use
VITE_API_URL=/api/v1
This prevents mixed-content and CORS issues.
PostgreSQL Compatibility Smoke Test
Run manually against one DSN:
PG_DSN='postgresql://postgres:postgres@127.0.0.1:5432/compatdb?sslmode=disable' \
python backend/scripts/pg_compat_smoke.py
Run with DSN candidates (CI style):
PG_DSN_CANDIDATES='postgresql://postgres:postgres@postgres:5432/compatdb?sslmode=disable,postgresql://postgres:postgres@127.0.0.1:5432/compatdb?sslmode=disable' \
python backend/scripts/pg_compat_smoke.py
Troubleshooting
Backend container keeps restarting during make migrate
Most common reason: failed migration. Check logs:
docker compose logs --tail=200 backend
docker compose logs --tail=200 db
CORS or mixed-content issues behind SSL proxy
- Set
VITE_API_URL=/api/v1 - Ensure proxy forwards
/api/to backend - Set correct frontend origin(s) in
CORS_ORIGINS
rejected SSL upgrade for a target
Target likely does not support SSL with current settings.
Set target sslmode to disable (or correct SSL config on target DB).
Query Insights empty
- Check target has
Use pg_stat_statementsenabled - Verify extension exists on target (
CREATE EXTENSION ...)
Security Notes
- No secrets hardcoded in repository
- Passwords hashed with Argon2
- Sensitive values encrypted at rest (Fernet)
- RBAC enforced on protected endpoints
- Audit logs for critical actions
- Collector error logging includes throttling to reduce repeated noise






