Self-hosted observability and analytics dashboard for n8n.
3.4K
Self-hosted observability dashboard for n8n. Tracks workflow executions, instance health metrics, and provides a Prometheus-style metrics explorer — all behind role-based access control with audit logging. Runs as a single Docker container backed by PostgreSQL. Push-based ingestion; n8n-trace never calls your n8n instances.
n8n-trace uses a push-based ingestion model. Scheduled workflows running inside your n8n instances collect execution data and metrics and write them directly to PostgreSQL. The n8n-trace service reads from the database and presents analytics in the dashboard.
n8n Instance
│
│ (scheduled workflows)
▼
PostgreSQL ◄── n8n-trace
│
▼
Dashboard (React)
node-pg-migrate| Tag | Description |
|---|---|
latest | Most recent stable build |
v2.0.0 | Current release — unified single-container architecture |
Recommendation: Pin to a version tag in production (e.g.,
mohammedaljer/n8n-trace:v2.0.0).
docker run -d \
--name n8n-trace \
-p 8899:8001 \
-e APP_ENV=production \
-e DATABASE_URL=postgres://n8n_trace:password@host:5432/n8n_trace \
-e JWT_SECRET=your-secret-minimum-32-characters \
-e APP_URL=http://localhost:8899 \
-e CORS_ORIGIN=http://localhost:8899 \
mohammedaljer/n8n-trace:latest
The dashboard will be available at http://localhost:8899.
Requires PostgreSQL 14+ (tested with 17) and accessible via
DATABASE_URL. Tables and migrations are applied automatically on first startup.
services:
postgres:
image: postgres:17.2-alpine
restart: unless-stopped
environment:
POSTGRES_DB: n8n_trace
POSTGRES_USER: n8n_trace
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?required}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n_trace -d n8n_trace"]
interval: 5s
timeout: 5s
retries: 10
security_opt:
- no-new-privileges:true
app:
image: mohammedaljer/n8n-trace:v2.0.0
container_name: n8n_trace_app
restart: unless-stopped
ports:
- "${HTTP_PORT:-8899}:8001"
environment:
APP_ENV: production
DATABASE_URL: postgres://n8n_trace:${POSTGRES_PASSWORD}@postgres:5432/n8n_trace
PORT: "8001"
JWT_SECRET: ${JWT_SECRET:?required - minimum 32 characters}
APP_URL: ${APP_URL:-http://localhost:8899}
CORS_ORIGIN: ${CORS_ORIGIN:-http://localhost:8899}
COOKIE_SECURE: ${COOKIE_SECURE:-true}
TRUST_PROXY: ${TRUST_PROXY:-false}
RETENTION_ENABLED: ${RETENTION_ENABLED:-true}
RETENTION_DAYS: ${RETENTION_DAYS:-90}
METRICS_ENABLED: ${METRICS_ENABLED:-true}
depends_on:
postgres:
condition: service_healthy
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=64M
deploy:
resources:
limits:
memory: 512M
cpus: "1.0"
volumes:
postgres_data:
Create a .env file with your secrets:
POSTGRES_PASSWORD=your-strong-database-password
JWT_SECRET=your-jwt-secret-minimum-32-characters
APP_URL=https://trace.yourdomain.com
CORS_ORIGIN=https://trace.yourdomain.com
| Variable | Default | Description |
|---|---|---|
APP_ENV | production | production or development |
DATABASE_URL | — | PostgreSQL connection string (required) |
PORT | 8001 | Container listening port |
JWT_SECRET | — | Signing secret for session tokens (min 32 chars, required) |
APP_URL | — | Full external URL (required in production) |
CORS_ORIGIN | — | Allowed CORS origin (required in production) |
COOKIE_SECURE | true | Set false only for plain HTTP development |
TRUST_PROXY | false | Set 1 when behind a reverse proxy |
| Variable | Default | Description |
|---|---|---|
RETENTION_ENABLED | true | Enable automatic data cleanup |
RETENTION_DAYS | 90 | Days to retain execution data |
METRICS_ENABLED | true | Enable metrics ingestion and explorer |
METRICS_MAX_TIME_RANGE_DAYS | 30 | Maximum query time range |
METRICS_MAX_DATAPOINTS | 1000 | Maximum data points per query |
| Variable | Default | Description |
|---|---|---|
ADMIN_EMAIL | — | Auto-create first admin on startup |
ADMIN_PASSWORD | — | Password for auto-created admin (min 12 chars) |
PASSWORD_MIN_LENGTH | 12 | Minimum password length |
ACCOUNT_LOCKOUT_THRESHOLD | 10 | Failed attempts before lockout |
ACCOUNT_LOCKOUT_DURATION_MINUTES | 15 | Lockout duration |
LOGIN_RATE_LIMIT_MAX | 20 | Max login attempts per 15-min window |
n8n-trace uses a push-based model. Two n8n workflows (included in the repository under Workflows/) run on a schedule inside your n8n instance:
/metrics endpointBoth write directly to PostgreSQL using a least-privilege ingest user that has INSERT/UPDATE access to ingestion tables only — no access to user accounts, audit logs, or RBAC configuration.
See the Workflows README for setup instructions.
gcr.io/distroless/nodejs22-debian12:nonroot (no shell, no package manager)read_only: true with tmpfs /tmpno-new-privileges security opt'self'.env files for credentials. Never commit secrets to version control.N8N_DIAGNOSTICS_ENABLED=false on your n8n instances if you prefer not to send anonymous usage data.v2.0.0)| Endpoint | Purpose |
|---|---|
GET /health | Returns 200 OK when the application is running |
GET /ready | Returns 200 OK when the database connection is established |
The Docker Compose examples include a built-in healthcheck using node fetch() (no curl required in distroless).
This project is licensed under the MIT License.
Content type
Image
Digest
sha256:da5d82a22…
Size
54.1 MB
Last updated
2 months ago
docker pull mohammedaljer/n8n-trace