agentlytics
Comprehensive analytics dashboard for AI coding agents — Cursor, Windsurf, Claude Code, VS Code Copilot, Zed, Antigravi…
A self-hosted dashboard for CrowdSec: investigate alerts, manage decisions, monitor runtime metrics, and send notificat…
git clone https://github.com/TheDuffman85/crowdsec-web-ui.gitTheDuffman85/crowdsec-web-uiA self-hosted dashboard for CrowdSec: investigate alerts, manage decisions, monitor runtime metrics, and send notifications from one responsive UI.
| Area | Highlights |
|---|---|
| Dashboard | Alert and active-decision totals, attack map, drilldowns, top lists, shared quick filters, and simulation counts |
| Alerts | Searchable alert history, persistent count-aware quick filters, CrowdSec alert contexts, IP/AS/location details, event metadata, simulation labels, and configurable columns |
| Decisions | Active and expired decisions, persistent count-aware quick filters, duplicate hiding, manual bans, custom durations, reasons, and cleanup actions |
| Multi-instance | Several CrowdSec LAPIs, per-instance views, and a Combined scope for Dashboard, Alerts, and Decisions |
| Metrics | Optional Prometheus views for LAPI activity, bouncers, AppSec, parsers, latency, parsing time, and whitelists |
| Notifications | Alert, decision, CVE, availability, and update rules delivered through Email, Gotify, MQTT, ntfy, or Webhooks |
| Security | Initial administrator setup, password and TOTP login, passkeys, OIDC SSO, group roles, and instance-wide read-only mode |
| Localization | Arabic, Chinese, English, French, German, Hindi, Japanese, Portuguese, Russian, and Spanish |
| Experience | Unified search, dark/light themes, and responsive layouts |
You need a running CrowdSec LAPI. Connect the Web UI using either watcher password authentication or agent mTLS.
openssl rand -hex 32 docker exec crowdsec cscli machines add crowdsec-web-ui --password 'replace-with-generated-password' -f /dev/null # For local installations sudo cscli machines add crowdsec-web-ui --password 'replace-with-generated-password' -f /dev/null
Replace replace-with-generated-password with the value printed by openssl.
Important
Keep -f /dev/null. It registers the machine without overwriting the CrowdSec container's existing credentials file.
Configure LAPI TLS authentication and create a client certificate/key pair using the CrowdSec TLS authentication guide.
services:
crowdsec-web-ui:
image: ghcr.io/theduffman85/crowdsec-web-ui:latest
container_name: crowdsec_web_ui
ports:
- "3000:3000"
# For local CrowdSec instances
# extra_hosts:
# - "host.docker.internal:host-gateway"
environment:
CONFIG_INSTANCE_LAPI_URL: http://crowdsec:8080
# For local CrowdSec instances
# CONFIG_INSTANCE_LAPI_URL: http://host.docker.internal:8080
CONFIG_INSTANCE_LAPI_AUTH_USERNAME: crowdsec-web-ui
CONFIG_INSTANCE_LAPI_AUTH_PASSWORD: your-crowdsec-password
volumes:
- ./data:/app/data
restart: unless-stopped
A ready-to-use docker-compose.yml is included. Add the generated password, make sure the Web UI can reach CrowdSec on the same Docker network, then start it.
docker compose up -d
Open http://localhost:3000 and create the initial administrator account.
docker pull ghcr.io/theduffman85/crowdsec-web-ui:latest mkdir -p data docker run -d \ --name crowdsec_web_ui \ -p 3000:3000 \ -v $(pwd)/data:/app/data \ -e CONFIG_INSTANCE_LAPI_URL=http://crowdsec:8080 \ -e CONFIG_INSTANCE_LAPI_AUTH_USERNAME=crowdsec-web-ui \ -e CONFIG_INSTANCE_LAPI_AUTH_PASSWORD=your-crowdsec-password \ --network your_crowdsec_network \ ghcr.io/theduffman85/crowdsec-web-ui:latest
Current images use Node.js and do not have the former Bun/AVX-specific x64 limitation.
services:
crowdsec-web-ui:
image: ghcr.io/theduffman85/crowdsec-web-ui:latest
container_name: crowdsec_web_ui
ports:
- "3000:3000"
environment:
CONFIG_INSTANCE_LAPI_URL: https://crowdsec:8080
CONFIG_INSTANCE_LAPI_AUTH_TYPE: mtls
CONFIG_INSTANCE_LAPI_AUTH_CERT_FILE: /certs/agent.pem
CONFIG_INSTANCE_LAPI_AUTH_KEY_FILE: /certs/agent-key.pem
# CONFIG_INSTANCE_LAPI_TLS_CA_FILE: /certs/ca.pem
volumes:
- ./data:/app/data
- /path/on/host/agent.pem:/certs/agent.pem:ro
- /path/on/host/agent-key.pem:/certs/agent-key.pem:ro
# - /path/on/host/ca.pem:/certs/ca.pem:ro
restart: unless-stopped
Adjust the URL and certificate paths. Enable CONFIG_INSTANCE_LAPI_TLS_CA_FILE and its volume when LAPI uses a private CA.
Caution
Use HTTPS and a hardened reverse proxy for public deployments. Built-in authentication protects the UI and API, but TLS terminates outside the application. OIDC integrations include Authentik, Authelia, and Keycloak. Migrated installations that predate authentication remain unauthenticated until explicitly enabled.
| Component | Implementation |
|---|---|
| Client | React, Vite, and Tailwind CSS; builds to dist/client |
| Server | Node.js and Hono; builds to dist/server |
| Storage | SQLite via better-sqlite3 under /app/data |
| CrowdSec | Watcher password or agent mTLS; delta refreshes and chunked historical synchronization |
| Container | Runs as the non-root node user |
| Environment | Default path |
|---|---|
| Docker | /app/data/config.yaml |
| Local | ./data/config.yaml |
Use CONFIG_FILE only to select another existing file. config.example.yaml contains the complete commented YAML reference.
| Stage | Behavior |
|---|---|
| First start | Creates the missing default file. Supplied values become active YAML; defaults and optional examples remain comments. Generated mappings use block rows and the documented order. |
| Later starts | Treats the file as user-managed. CONFIG_* values override it in memory without rewriting it; generated explanations and defaults are not refreshed. |
| Persistent overrides | CONFIG_PERSIST_OVERRIDES: "true" writes validated merged values while preserving comments where possible. Removing a persisted non-secret override leaves its last value in YAML. |
| Precedence | Applies section variables, then field variables, then indexed array variables. Removing a non-persisted override reveals the file value. |
| Logging | Records applied paths and before/after values. Credentials are redacted; secret references show only their environment name or file path. |
| Reloading | Requires a restart after configuration changes or secret rotation. |
CONFIG_AUTH_OIDC_ADMIN_GROUPS_0, CONFIG_INSTANCES_0_ID, CONFIG_INSTANCES_0_METRICS_0_URL.CONFIG_SERVER, CONFIG_STORAGE, CONFIG_UI, CONFIG_AUTH, CONFIG_NOTIFICATIONS, CONFIG_UPDATES, CONFIG_CROWDSEC, or CONFIG_INSTANCES.CONFIG_INSTANCE_* addresses instance 0: CONFIG_INSTANCE_NAME equals CONFIG_INSTANCES_0_NAME. Metrics index 0 may also be omitted: CONFIG_INSTANCES_0_METRICS_URL equals CONFIG_INSTANCES_0_METRICS_0_URL, and CONFIG_INSTANCE_METRICS_URL applies both shorthands. Do not set equivalent forms together.env: NAME / file: PATH reference. Secret overrides also accept _FILE.| YAML field | Default | Purpose | Environment override |
|---|---|---|---|
server.port |
3000 |
HTTP listen port. | CONFIG_SERVER_PORT |
server.basePath |
"" |
Optional URL prefix such as /crowdsec; no trailing slash. |
CONFIG_SERVER_BASE_PATH |
storage.dataDir |
/app/data |
SQLite database and persistent application state. | CONFIG_STORAGE_DATA_DIR |
storage.geonamesDir |
/app/geonames in Docker; ./geonames locally |
Local GeoNames snapshot used for location labels. | CONFIG_STORAGE_GEONAMES_DIR |
storage.walEnabled |
true |
Enables SQLite write-ahead logging. Set to false for filesystems that do not support WAL. |
CONFIG_STORAGE_WAL_ENABLED |
ui.timeZone |
browser |
Browser timezone or an IANA zone such as Europe/Berlin or UTC. |
CONFIG_UI_TIME_ZONE |
ui.timeFormat |
browser |
Clock format: browser, 12h, or 24h. |
CONFIG_UI_TIME_FORMAT |
ui.readOnly |
false |
Hides management actions and rejects mutating API operations. | CONFIG_UI_READ_ONLY |
updates.enabled |
true in packaged images |
Enables the built-in update check. | CONFIG_UPDATES_ENABLED |
| YAML field | Default | Purpose | Environment override |
|---|---|---|---|
auth.enabled |
auto |
Enables authentication; auto enables new databases while preserving migrated database state. |
CONFIG_AUTH_ENABLED |
auth.sessionSecret |
Generated and stored | Signs sessions and encrypts saved authentication settings. | CONFIG_AUTH_SESSION_SECRET or CONFIG_AUTH_SESSION_SECRET_FILE |
auth.totpSecret |
sessionSecret |
Encrypts stored per-account TOTP seeds. | CONFIG_AUTH_TOTP_SECRET or CONFIG_AUTH_TOTP_SECRET_FILE |
auth.totpSeed |
Unset | Optional base32 fallback TOTP seed for the password user; minimum 26 characters. | CONFIG_AUTH_TOTP_SEED or CONFIG_AUTH_TOTP_SEED_FILE |
auth.oidc.issuerUrl |
Unset | OIDC provider issuer URL. | CONFIG_AUTH_OIDC_ISSUER_URL |
auth.oidc.clientId |
Unset | OIDC client identifier. | CONFIG_AUTH_OIDC_CLIENT_ID |
auth.oidc.clientSecret |
Unset | OIDC client secret. | CONFIG_AUTH_OIDC_CLIENT_SECRET or CONFIG_AUTH_OIDC_CLIENT_SECRET_FILE |
auth.oidc.scope |
openid profile email |
Requested OIDC scopes; must include openid. |
CONFIG_AUTH_OIDC_SCOPE |
auth.oidc.groupsClaim |
groups |
Claim containing role-mapping groups. | CONFIG_AUTH_OIDC_GROUPS_CLAIM |
auth.oidc.adminGroups |
[] |
Groups granted administrator access. | CONFIG_AUTH_OIDC_ADMIN_GROUPS or CONFIG_AUTH_OIDC_ADMIN_GROUPS_<INDEX> |
auth.oidc.readOnlyGroups |
[] |
Groups granted read-only access. | CONFIG_AUTH_OIDC_READ_ONLY_GROUPS or CONFIG_AUTH_OIDC_READ_ONLY_GROUPS_<INDEX> |
auth.oidc.unmatchedRole |
deny |
Role for unmatched OIDC users: deny, admin, or read-only. |
CONFIG_AUTH_OIDC_UNMATCHED_ROLE |
| YAML field | Default | Purpose | Environment override |
|---|---|---|---|
notifications.secretKey |
Generated and stored | Encrypts saved notification credentials. | CONFIG_NOTIFICATIONS_SECRET_KEY or CONFIG_NOTIFICATIONS_SECRET_KEY_FILE |
notifications.allowPrivateAddresses |
true |
Allows private, loopback, and link-local notification destinations. | CONFIG_NOTIFICATIONS_ALLOW_PRIVATE_ADDRESSES |
notifications.debugPayloads |
false |
Logs truncated rendered payloads after failed notification delivery. | CONFIG_NOTIFICATIONS_DEBUG_PAYLOADS |
Omitting crowdsec.alertFilters uses the standard non-CAPI feed. Setting any explicit filter field enables explicit filtering.
| YAML field | Default | Purpose | Environment override |
|---|---|---|---|
crowdsec.simulationsEnabled |
false |
Includes simulation-mode alerts and decisions. | CONFIG_CROWDSEC_SIMULATIONS_ENABLED |
crowdsec.alertFilters.includeOrigins |
[] |
Keeps alerts matching these exact origins. | CONFIG_CROWDSEC_ALERT_FILTERS_INCLUDE_ORIGINS or CONFIG_CROWDSEC_ALERT_FILTERS_INCLUDE_ORIGINS_<INDEX> |
crowdsec.alertFilters.excludeOrigins |
[] |
Drops alerts matching these exact origins. | CONFIG_CROWDSEC_ALERT_FILTERS_EXCLUDE_ORIGINS or CONFIG_CROWDSEC_ALERT_FILTERS_EXCLUDE_ORIGINS_<INDEX> |
crowdsec.alertFilters.includeCapi |
false |
Adds the Central API/community-blocklist feed. | CONFIG_CROWDSEC_ALERT_FILTERS_INCLUDE_CAPI |
crowdsec.alertFilters.includeOriginEmpty |
false |
Keeps empty-origin alerts with explicit include filters. | CONFIG_CROWDSEC_ALERT_FILTERS_INCLUDE_ORIGIN_EMPTY |
crowdsec.alertFilters.excludeOriginEmpty |
false |
Drops alerts whose effective origin is empty. | CONFIG_CROWDSEC_ALERT_FILTERS_EXCLUDE_ORIGIN_EMPTY |
crowdsec.alertFilters.legacy.origins |
[] |
Compatibility origin allowlist; CAPI enables the CAPI feed. |
CONFIG_CROWDSEC_ALERT_FILTERS_LEGACY_ORIGINS or CONFIG_CROWDSEC_ALERT_FILTERS_LEGACY_ORIGINS_<INDEX> |
crowdsec.alertFilters.legacy.extraScenarios |
[] |
Compatibility list of additional scenarios. | CONFIG_CROWDSEC_ALERT_FILTERS_LEGACY_EXTRA_SCENARIOS or CONFIG_CROWDSEC_ALERT_FILTERS_LEGACY_EXTRA_SCENARIOS_<INDEX> |
Synchronization durations accept ms, s, m, h, or d, for example 500ms, 30s, 5m, or 7d. The lookback fields accept only m, h, or d.
| YAML field | Default | Purpose | Environment override |
|---|---|---|---|
crowdsec.sync.lookback |
168h |
Imported history and retention window. | CONFIG_CROWDSEC_SYNC_LOOKBACK |
crowdsec.sync.refreshInterval |
1m |
Active refresh cadence; 0 or manual disables scheduling. |
CONFIG_CROWDSEC_SYNC_REFRESH_INTERVAL |
crowdsec.sync.manualRefreshEnabled |
false |
Enables manual refresh controls. | CONFIG_CROWDSEC_SYNC_MANUAL_REFRESH_ENABLED |
crowdsec.sync.idleRefreshInterval |
10m |
Refresh cadence while the application is idle; 0 disables it. |
CONFIG_CROWDSEC_SYNC_IDLE_REFRESH_INTERVAL |
crowdsec.sync.idleThreshold |
2m |
Inactivity before idle refresh behavior begins. | CONFIG_CROWDSEC_SYNC_IDLE_THRESHOLD |
crowdsec.sync.requestTimeout |
30s |
Timeout for individual LAPI requests. | CONFIG_CROWDSEC_SYNC_REQUEST_TIMEOUT |
crowdsec.sync.bouncerPropagationDelay |
15s |
Grace period before deleting alerts owned by expired decisions. | CONFIG_CROWDSEC_SYNC_BOUNCER_PROPAGATION_DELAY |
crowdsec.sync.deletionQueueMaxAge |
24h |
Stops retrying failed queued deletions after this age; 0 disables the limit. Tombstones remain until the retention window passes. |
CONFIG_CROWDSEC_SYNC_DELETION_QUEUE_MAX_AGE |
crowdsec.sync.metricsRequestTimeout |
5s |
Default timeout for metrics endpoints. | CONFIG_CROWDSEC_SYNC_METRICS_REQUEST_TIMEOUT |
crowdsec.sync.heartbeatInterval |
30s |
CrowdSec machine heartbeat cadence; 0 disables it. |
CONFIG_CROWDSEC_SYNC_HEARTBEAT_INTERVAL |
crowdsec.sync.alertSyncChunk |
12h |
Historical import window size. | CONFIG_CROWDSEC_SYNC_ALERT_SYNC_CHUNK |
crowdsec.sync.alertSyncMinChunk |
15m |
Minimum retry window after a timed-out import. | CONFIG_CROWDSEC_SYNC_ALERT_SYNC_MIN_CHUNK |
crowdsec.sync.reconcileWindow |
1h |
Fixed alert-history reconciliation window size. | CONFIG_CROWDSEC_SYNC_RECONCILE_WINDOW |
crowdsec.sync.reconcileRecentAge |
24h |
Boundary between recent and older windows. | CONFIG_CROWDSEC_SYNC_RECONCILE_RECENT_AGE |
crowdsec.sync.reconcileRecentInterval |
15m |
Reconciliation cadence for recent windows. | CONFIG_CROWDSEC_SYNC_RECONCILE_RECENT_INTERVAL |
crowdsec.sync.reconcileActiveInterval |
5m |
Reconciliation cadence for windows with active decisions. | CONFIG_CROWDSEC_SYNC_RECONCILE_ACTIVE_INTERVAL |
crowdsec.sync.reconcileOldInterval |
3h |
Reconciliation cadence for older windows. | CONFIG_CROWDSEC_SYNC_RECONCILE_OLD_INTERVAL |
crowdsec.sync.reconcileWindowsPerRefresh |
2 |
Maximum due windows processed per refresh. | CONFIG_CROWDSEC_SYNC_RECONCILE_WINDOWS_PER_REFRESH |
crowdsec.sync.bootstrapRetryDelay |
30s |
Delay between failed initial-sync retries; 0 retries immediately. |
CONFIG_CROWDSEC_SYNC_BOOTSTRAP_RETRY_DELAY |
crowdsec.sync.bootstrapRetryEnabled |
true |
Enables background retry after initial synchronization failure. | CONFIG_CROWDSEC_SYNC_BOOTSTRAP_RETRY_ENABLED |
<INDEX> is zero-based.Instance <INDEX>; authentication type is inferred from credentials._, and -. Keep them stable after importing data.Important
Configure exactly one credential shape:
username and passwordcertFile and keyFiletype is optional and inferred from these fields. Set it explicitly to none, password, or mtls when desired. Do not mix password and mTLS credentials.
Plaintext secrets are supported, but mounted secret files are recommended so credentials do not end up in source control, backups, or configuration-management logs.
| YAML field | Default | Purpose | Environment override |
|---|---|---|---|
instances |
One generated default instance |
Configures one or more CrowdSec connections. | CONFIG_INSTANCES or CONFIG_INSTANCES_<INDEX>_* |
instances[].id |
Zero-based instance index | Stable database identity for the instance. | CONFIG_INSTANCES_<INDEX>_ID |
instances[].name |
Instance <INDEX> |
Unique display name. | CONFIG_INSTANCES_<INDEX>_NAME |
instances[].icon |
Unset | Optional short text or emoji shown in the selector. | CONFIG_INSTANCES_<INDEX>_ICON |
instances[].lapi |
Required | Complete LAPI connection object. | CONFIG_INSTANCES_<INDEX>_LAPI |
instances[].lapi.url |
Required (http://crowdsec:8080 in starter config) |
Absolute HTTP(S) LAPI base URL without credentials, a path, or a fragment. | CONFIG_INSTANCES_<INDEX>_LAPI_URL |
instances[].lapi.auth |
type: none |
LAPI authentication object. | CONFIG_INSTANCES_<INDEX>_LAPI_AUTH |
instances[].lapi.auth.type |
Inferred from credentials | Optional authentication mode: none, password, or mtls. |
CONFIG_INSTANCES_<INDEX>_LAPI_AUTH_TYPE |
instances[].lapi.auth.username |
Required for password |
CrowdSec machine username. | CONFIG_INSTANCES_<INDEX>_LAPI_AUTH_USERNAME |
instances[].lapi.auth.password |
Required for password |
CrowdSec machine password or secret reference. | CONFIG_INSTANCES_<INDEX>_LAPI_AUTH_PASSWORD or CONFIG_INSTANCES_<INDEX>_LAPI_AUTH_PASSWORD_FILE |
instances[].lapi.auth.certFile |
Required for mtls |
Client certificate path. | CONFIG_INSTANCES_<INDEX>_LAPI_AUTH_CERT_FILE |
instances[].lapi.auth.keyFile |
Required for mtls |
Client private-key path. | CONFIG_INSTANCES_<INDEX>_LAPI_AUTH_KEY_FILE |
instances[].lapi.tls |
Empty mapping | LAPI server-trust settings. | CONFIG_INSTANCES_<INDEX>_LAPI_TLS |
instances[].lapi.tls.caFile |
Unset | CA bundle used to verify the LAPI server. | CONFIG_INSTANCES_<INDEX>_LAPI_TLS_CA_FILE |
instances[].metrics |
[] |
Zero or more metrics endpoints. | CONFIG_INSTANCES_<INDEX>_METRICS or CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_* |
instances[].sync |
Inherits global values | Per-instance synchronization overrides. | CONFIG_INSTANCES_<INDEX>_SYNC or CONFIG_INSTANCES_<INDEX>_SYNC_* |
<INDEX> selects the instance; zero-based <METRIC_INDEX> selects its endpoint.<METRIC_INDEX> and name to Metrics <METRIC_INDEX>.| YAML field | Default | Purpose | Environment override |
|---|---|---|---|
instances[].metrics[].id |
Zero-based metrics index | Stable identifier unique within the instance. | CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_ID |
instances[].metrics[].name |
Metrics <METRIC_INDEX> |
Display name. | CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_NAME |
instances[].metrics[].url |
Required | Absolute HTTP(S) Prometheus endpoint URL. | CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_URL |
instances[].metrics[].requestTimeout |
Global 5s |
Request timeout for this endpoint. | CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_REQUEST_TIMEOUT |
instances[].metrics[].auth |
type: none |
Complete metrics authentication object. | CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_AUTH |
instances[].metrics[].auth.type |
Inferred from credentials | Optional authentication mode: none, basic, or bearer. |
CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_AUTH_TYPE |
instances[].metrics[].auth.username |
Required for basic |
Basic-auth username. | CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_AUTH_USERNAME |
instances[].metrics[].auth.password |
Required for basic |
Basic-auth password or secret reference. | CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_AUTH_PASSWORD or CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_AUTH_PASSWORD_FILE |
instances[].metrics[].auth.token |
Required for bearer |
Bearer token or secret reference. | CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_AUTH_TOKEN or CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_AUTH_TOKEN_FILE |
instances[].metrics[].tls |
Empty mapping | Metrics TLS settings. | CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_TLS |
instances[].metrics[].tls.caFile |
Unset | CA bundle used to verify the metrics server. | CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_TLS_CA_FILE |
instances[].metrics[].tls.certFile |
Unset | Optional metrics client certificate; requires keyFile. |
CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_TLS_CERT_FILE |
instances[].metrics[].tls.keyFile |
Unset | Optional metrics client private key; requires certFile. |
CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_TLS_KEY_FILE |
Every field inherits its corresponding global value when omitted.
| YAML field | Default | Purpose | Environment override |
|---|---|---|---|
instances[].sync.lookback |
Global 168h |
History and retention window for this instance. | CONFIG_INSTANCES_<INDEX>_SYNC_LOOKBACK |
instances[].sync.refreshInterval |
Global 1m |
Active refresh cadence. | CONFIG_INSTANCES_<INDEX>_SYNC_REFRESH_INTERVAL |
instances[].sync.idleRefreshInterval |
Global 10m |
Idle refresh cadence. | CONFIG_INSTANCES_<INDEX>_SYNC_IDLE_REFRESH_INTERVAL |
instances[].sync.idleThreshold |
Global 2m |
Time before this instance is considered idle. | CONFIG_INSTANCES_<INDEX>_SYNC_IDLE_THRESHOLD |
instances[].sync.requestTimeout |
Global 30s |
LAPI request timeout. | CONFIG_INSTANCES_<INDEX>_SYNC_REQUEST_TIMEOUT |
instances[].sync.heartbeatInterval |
Global 30s |
Machine heartbeat cadence. | CONFIG_INSTANCES_<INDEX>_SYNC_HEARTBEAT_INTERVAL |
instances[].sync.alertSyncChunk |
Global 12h |
Historical import window size. | CONFIG_INSTANCES_<INDEX>_SYNC_ALERT_SYNC_CHUNK |
instances[].sync.alertSyncMinChunk |
Global 15m |
Minimum retry window. | CONFIG_INSTANCES_<INDEX>_SYNC_ALERT_SYNC_MIN_CHUNK |
instances[].sync.reconcileWindow |
Global 1h |
Reconciliation window size. | CONFIG_INSTANCES_<INDEX>_SYNC_RECONCILE_WINDOW |
instances[].sync.reconcileRecentAge |
Global 24h |
Recent-window age boundary. | CONFIG_INSTANCES_<INDEX>_SYNC_RECONCILE_RECENT_AGE |
instances[].sync.reconcileRecentInterval |
Global 15m |
Recent-window reconciliation cadence. | CONFIG_INSTANCES_<INDEX>_SYNC_RECONCILE_RECENT_INTERVAL |
instances[].sync.reconcileActiveInterval |
Global 5m |
Active-decision reconciliation cadence. | CONFIG_INSTANCES_<INDEX>_SYNC_RECONCILE_ACTIVE_INTERVAL |
instances[].sync.reconcileOldInterval |
Global 3h |
Older-window reconciliation cadence. | CONFIG_INSTANCES_<INDEX>_SYNC_RECONCILE_OLD_INTERVAL |
instances[].sync.reconcileWindowsPerRefresh |
Global 2 |
Due-window budget per refresh. | CONFIG_INSTANCES_<INDEX>_SYNC_RECONCILE_WINDOWS_PER_REFRESH |
instances[].sync.bootstrapRetryDelay |
Global 30s |
Initial-sync retry delay. | CONFIG_INSTANCES_<INDEX>_SYNC_BOOTSTRAP_RETRY_DELAY |
instances[].sync.bootstrapRetryEnabled |
Global true |
Enables background initial-sync retry. | CONFIG_INSTANCES_<INDEX>_SYNC_BOOTSTRAP_RETRY_ENABLED |
instances[].sync.bouncerPropagationDelay |
Global 15s |
Alert-deletion grace period. | CONFIG_INSTANCES_<INDEX>_SYNC_BOUNCER_PROPAGATION_DELAY |
Use zero-based CONFIG_INSTANCES_<INDEX>_* overrides to define each instance. Indexes must be contiguous, starting at 0.
services:
crowdsec-web-ui:
image: ghcr.io/theduffman85/crowdsec-web-ui:latest
environment:
CONFIG_INSTANCES_0_ID: eu-prod
CONFIG_INSTANCES_0_NAME: EU Production
CONFIG_INSTANCES_0_LAPI_URL: http://crowdsec-eu:8080
CONFIG_INSTANCES_0_LAPI_AUTH_USERNAME: crowdsec-web-ui
CONFIG_INSTANCES_0_LAPI_AUTH_PASSWORD_FILE: /run/secrets/eu-lapi-password
CONFIG_INSTANCES_0_METRICS_0_ID: lapi
CONFIG_INSTANCES_0_METRICS_0_NAME: EU LAPI
CONFIG_INSTANCES_0_METRICS_0_URL: http://crowdsec-eu:6060/metrics
CONFIG_INSTANCES_1_ID: us-prod
CONFIG_INSTANCES_1_NAME: US Production
CONFIG_INSTANCES_1_LAPI_URL: http://crowdsec-us:8080
CONFIG_INSTANCES_1_LAPI_AUTH_USERNAME: crowdsec-web-ui
CONFIG_INSTANCES_1_LAPI_AUTH_PASSWORD_FILE: /run/secrets/us-lapi-password
# For mTLS, replace instance 1's URL and password credentials above with:
# CONFIG_INSTANCES_1_LAPI_URL: https://crowdsec-us:8080
# CONFIG_INSTANCES_1_LAPI_AUTH_TYPE: mtls
# CONFIG_INSTANCES_1_LAPI_AUTH_CERT_FILE: /certs/us-client-cert.pem
# CONFIG_INSTANCES_1_LAPI_AUTH_KEY_FILE: /run/secrets/us-client-key.pem
# CONFIG_INSTANCES_1_LAPI_TLS_CA_FILE: /certs/us-ca.pem
volumes:
- ./secrets/eu-lapi-password:/run/secrets/eu-lapi-password:ro
- ./secrets/us-lapi-password:/run/secrets/us-lapi-password:ro
# Mount these files when using the commented mTLS configuration:
# - ./certs/us-client-cert.pem:/certs/us-client-cert.pem:ro
# - ./secrets/us-client-key.pem:/run/secrets/us-client-key.pem:ro
# - ./certs/us-ca.pem:/certs/us-ca.pem:ro
CONFIG_INSTANCE_* shorthand for single-instance deployments.CONFIG_INSTANCES_<INDEX>_METRICS_<METRIC_INDEX>_*.Add entries to the top-level instances array. Each entry defines one LAPI connection and zero or more metrics endpoints.
instances:
- id: eu-prod
name: EU Production
icon: 🇪🇺
lapi:
url: http://crowdsec-eu:8080
auth:
type: password
username: crowdsec-web-ui
password:
file: /run/secrets/eu-lapi-password
metrics:
- id: lapi
name: EU LAPI
url: http://crowdsec-eu:6060/metrics
auth:
type: bearer
token:
file: /run/secrets/eu-metrics-token
- id: edge-engine
name: EU Edge Engine
url: http://crowdsec-edge:6060/metrics
sync:
requestTimeout: 45s
alertSyncChunk: 6h
- id: us-prod
name: US Production
icon: 🇺🇸
lapi:
url: http://crowdsec-us:8080
auth:
type: password
username: crowdsec-web-ui
password:
file: /run/secrets/us-lapi-password
# For mTLS, replace url and auth above with:
# url: https://crowdsec-us:8080
# auth:
# type: mtls
# certFile: /run/secrets/us-client-cert
# keyFile: /run/secrets/us-client-key
# tls:
# caFile: /run/secrets/us-ca
_, or -, and must never be reused for another LAPI.icon accepts up to eight Unicode code points of text or emoji without control characters; omitted icons use colored squares and Combined uses a grid.env/file source. mTLS requires both certFile and keyFile; tls.caFile controls server trust.none, basic, and bearer; metrics TLS supports caFile plus an optional complete client certificate/key pair.| Area | Behavior |
|---|---|
| Dashboard, Alerts, Decisions | Support one instance or Combined scope |
| Metrics | Always uses one instance and endpoint; process-local counters are not summed |
| Add decision / clean IP | Runs against every LAPI in Combined scope and reports partial failures |
| Row deletion | Uses the row's owning instance; numeric upstream IDs are never broadcast |
Authentication covers the browser UI and protected APIs; /api/health remains public.
auth.enabled |
Behavior |
|---|---|
auto |
Enables authentication for new databases; preserves the state of migrated databases |
true |
Requires authentication and initial administrator setup |
false |
Disables authentication; this deployment setting is not available in the UI |
Enable authentication explicitly on installations migrated from older releases.
auth: enabled: true
auth.totpSeed fallback.Configure OIDC in Settings or YAML.
auth:
enabled: true
oidc:
issuerUrl: https://idp.example.com/application/o/crowdsec-web-ui/
clientId: crowdsec-web-ui
clientSecret:
file: /run/secrets/oidc_client_secret
scope: openid profile email
groupsClaim: groups
adminGroups: [crowdsec-admins, secops]
readOnlyGroups: [crowdsec-viewers]
unmatchedRole: deny
Register this callback URI with the identity provider.
https://<crowdsec-web-ui-host>/api/auth/oidc/callback
basePath: /crowdsec, use https://<host>/crowdsec/api/auth/oidc/callback.Host or X-Forwarded-Host and X-Forwarded-Proto.openid; add provider-specific scopes such as groups only when required.auth.oidc.unmatchedRole (deny by default).ui.readOnly: true overrides all roles for the deployment. It blocks CrowdSec writes, refresh changes, notification destination/rule management, test sends, and notification deletion. Language changes and marking notifications read remain available. This is not per-user RBAC.Important
CrowdSec only permits alert deletion when the request comes from loopback or an address listed in api.server.trusted_ips. Registering the Web UI as a CrowdSec machine authenticates it, but does not grant this IP-based permission.
When the Web UI runs in Docker, add the Web UI container's source IP or, preferably, its Docker network CIDR to CrowdSec's /etc/crowdsec/config.yaml. This is not the browser's IP or the Docker host's public IP. Without this entry, decision operations can still work while alert deletion fails with 403 Forbidden.
api:
server:
trusted_ips:
- 127.0.0.1
- ::1
- 172.16.0.0/12 # Docker default bridge network
Use the narrowest CIDR that contains the Web UI container and LAPI network. Container IPs can change when containers are recreated, so the Docker network CIDR is usually more reliable than one container IP. Restart CrowdSec after updating the file. The current CrowdSec container does not provide a TRUSTED_IPS environment override. See the CrowdSec configuration reference.
A self-signed certificate or internal CA may produce the following error.
Login failed: unable to get local issuer certificate
Mount the CA certificate and configure it for the LAPI instance.
services:
crowdsec-web-ui:
image: ghcr.io/theduffman85/crowdsec-web-ui:latest
container_name: crowdsec_web_ui
ports:
- "3000:3000"
environment:
CONFIG_INSTANCE_LAPI_URL: https://crowdsec:8080
CONFIG_INSTANCE_LAPI_AUTH_USERNAME: crowdsec-web-ui
CONFIG_INSTANCE_LAPI_AUTH_PASSWORD_FILE: /run/secrets/crowdsec_password
CONFIG_INSTANCE_LAPI_TLS_CA_FILE: /certs/root_ca.crt
secrets:
- crowdsec_password
volumes:
- ./data:/app/data
- /path/on/host/root_ca.crt:/certs/root_ca.crt:ro
restart: unless-stopped
secrets:
crowdsec_password:
file: ./secrets/crowdsec_password.txt
Keep the CA mount read-only. CONFIG_INSTANCE_LAPI_TLS_CA_FILE maps to instances[0].lapi.tls.caFile; no image rebuild is needed.
CrowdSec Web UI listens for HTTP on port 3000 and does not obtain or terminate TLS certificates itself. Put a reverse proxy in front of it for HTTPS deployments and keep port 3000 private to the proxy.
HTTPS is required for passkeys because browsers expose WebAuthn only in a secure context. http://localhost is suitable for local testing, but a remote deployment needs a stable hostname and a certificate trusted by the browser. Passkeys registered for one hostname cannot be used from a different hostname.
The proxy must preserve Host (or set X-Forwarded-Host) and set X-Forwarded-Proto to the original scheme. The application uses these headers for WebAuthn origins, secure session cookies, OIDC callback URLs, and mutation-origin checks.
This minimal example assumes Traefik already has a websecure entrypoint, a Let's Encrypt resolver named letsencrypt, and an external Docker network named proxy. Add the labels and proxy network to the existing Web UI service, replacing the hostname and CrowdSec settings.
services:
crowdsec-web-ui:
image: ghcr.io/theduffman85/crowdsec-web-ui:latest
expose:
- "3000"
environment:
CONFIG_INSTANCE_LAPI_URL: http://crowdsec:8080
CONFIG_INSTANCE_LAPI_AUTH_USERNAME: crowdsec-web-ui
CONFIG_INSTANCE_LAPI_AUTH_PASSWORD: your-crowdsec-password
# For https://crowdsec.example.com/crowdsec/:
# CONFIG_SERVER_BASE_PATH: /crowdsec
volumes:
- ./data:/app/data
labels:
- traefik.enable=true
- traefik.docker.network=proxy
# For /crowdsec/, append: && PathPrefix(`/crowdsec`)
- 'traefik.http.routers.crowdsec-web-ui.rule=Host(`crowdsec.example.com`)'
- traefik.http.routers.crowdsec-web-ui.entrypoints=websecure
- traefik.http.routers.crowdsec-web-ui.tls.certresolver=letsencrypt
- traefik.http.services.crowdsec-web-ui.loadbalancer.server.port=3000
networks:
- proxy
- crowdsec
restart: unless-stopped
networks:
proxy:
external: true
crowdsec:
external: true
name: your_crowdsec_network
Traefik supplies the forwarded headers and WebSocket upgrade handling automatically. See the Traefik ACME documentation if the letsencrypt resolver is not configured yet.
This equivalent example assumes Nginx already terminates HTTPS and the Web UI port is published only on loopback, for example 127.0.0.1:3000:3000.
# For https://crowdsec.example.com/crowdsec/, set
# CONFIG_SERVER_BASE_PATH=/crowdsec and replace both `/` paths below
# with `/crowdsec/`.
location / {
proxy_pass http://localhost:3000/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $http_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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
/ and has no trailing slash./ redirects to it; APIs, assets, and navigation follow it automatically.CONFIG_SERVER_BASE_PATH: /crowdsec and use a router rule such as Host(`example.com`) && PathPrefix(`/crowdsec`). Do not configure StripPrefix; the application expects to receive the base path.private, no-store.Origin and Sec-Fetch-Site headers remain compatible.The public endpoint is GET /api/health. Startup does not wait for LAPI: bootstrap retries in the background, so the container can become healthy before synchronization completes.
curl http://localhost:3000/api/health
# {"status":"ok"}
The built-in check runs every 30 seconds with a five-second timeout, a 10-second start period, and three retries.
docker inspect --format='{{.State.Health.Status}}' crowdsec_web_ui
server.basePath does not affect the internal check at localhost:3000/api/health. If server.port changes, update the health-check command and port mapping.
http://127.0.0.1:6060/metrics.Enable full metrics in CrowdSec's /etc/crowdsec/config.yaml.
prometheus: enabled: true level: full listen_addr: 127.0.0.1 listen_port: 6060
For separate containers, bind listen_addr: 0.0.0.0 on a trusted network, then configure the matching Web UI instance.
environment: CONFIG_INSTANCE_METRICS_URL: http://crowdsec:6060/metrics
| CrowdSec level | Result |
|---|---|
full |
All supported details, including per-machine, per-bouncer, and per-node metrics |
aggregated |
Less detail; omits those per-entity metrics |
none |
Disables metrics registration |
AppSec and latency sections appear only when CrowdSec emits those metrics. Time-window-only rate()/increase() metrics are intentionally omitted. See the CrowdSec Prometheus documentation.
crowdsec.simulationsEnabled: true fetches non-remediating simulation alerts/decisions and shows badges, filters, and dashboard counts. Default: false.ID, Machine, and Origin are hidden by default. Machine prefers machine_alias, then machine_id; multiple alert decision origins display as Mixed.id:, machine:, and origin:.Dashboard, Alerts, and Decisions share one count-aware Quick Filters drawer. Its trigger shows the number of active selections, and the clear control in the drawer header resets all stored quick filters.
Instance and machine options use stable IDs for filtering while displaying their configured names or aliases. Country, instance, and machine searches match the displayed label as well as the stored value. Alerts with several origins or targets contribute to each distinct facet option instead of exposing a combined bucket.
Action, Status, and Alert there; Decisions lists Decision; Dashboard lists all four. Their stored selections are preserved for the page that supports them and can be cleared from any drawer.tausend.me generates target=tausend.me and does not include bw.tausend.me. Manually entering target:tausend.me remains a broader contains search.Dashboard applies the shared fields Country, Scenario, AS, IP / Range, Target, ID, Instance, Region, City, Machine, and Origin. Filters that depend on decision-only or alert-list-only data are retained in Unavailable instead of being silently discarded.
Active decisions are deduplicated by instance, value, and simulation mode. When filters exclude the globally preferred decision, the best matching decision is promoted so enabling Hide duplicates cannot make an otherwise matching duplicate group disappear.
| Syntax | Example |
|---|---|
| Free text / quoted phrase | ssh hetzner, "nginx bf" |
| Field contains / exact value | country:germany, country=DE |
| Date comparison | date>=2026-03-24, date<2026-03-25T12:00:00Z |
| Negative / empty | -sim:simulated, sim<>simulated, origin:"", origin<>"" |
| Boolean / grouping | country:(germany OR france) AND -sim:simulated |
| Decision filters | status:active AND action:ban, alert:123 OR ip:"192.168.5.0/24" |
The : operator performs a case-insensitive contains match, while = matches the complete field value. Quick Filter selections use =. Date fields support <, >, <=, >=, and =>. A bare field name is free text unless followed by :. Quote literal AND, OR, or NOT. The search Info button lists page-specific fields and examples.
| Page | Query |
|---|---|
| Alerts | country:germany ssh |
| Alerts | date>=2026-03-24 AND date<2026-03-25 |
| Alerts | country:(germany OR france) AND -sim:simulated |
| Alerts | origin:"" |
| Decisions | status:active AND action:ban |
| Decisions | date>=2026-03-24 AND action:ban |
| Decisions | alert:123 OR ip:"192.168.5.0/24" |
Limit the local cache by origin when CrowdSec ingests automation, blocklists, or community feeds.
crowdsec:
alertFilters:
includeOrigins: [crowdsec, cscli-import]
excludeOrigins: [cscli]
includeCapi: true
includeOriginEmpty: true
excludeOriginEmpty: false
| Origin | Source |
|---|---|
crowdsec |
Security-engine decisions |
cscli |
Manual cscli decisions add |
cscli-import |
cscli decisions import |
lists |
Imported list feeds |
CAPI |
Central API / community blocklist |
includeCapi: true adds CAPI to the default feed; includeOrigins: [CAPI] selects only CAPI.includeOriginEmpty retains origin-less alerts alongside includes; excludeOriginEmpty removes them.| Setting | Result |
|---|---|
includeOrigins: [crowdsec] |
Keeps security-engine alerts only |
includeOrigins: [lists] |
Keeps list-based alerts only |
includeCapi: true |
Adds CAPI to the default feed |
includeOrigins: [CAPI] |
Keeps CAPI alerts only |
includeOriginEmpty: true |
Keeps origin-less alerts alongside explicit includes |
excludeOriginEmpty: true |
Removes origin-less alerts |
excludeOrigins: [cscli, lists] |
Removes manual and imported-list alerts |
Rules run against locally cached CrowdSec data, create in-app notifications, record delivery status, and optionally deliver outbound messages.
Every rule has a name, severity (info, warning, critical), incident deduplication, and destination channels. Alert rules filter scenario, target, and simulation state; IP Ban and New Alert/Decision also accept exact IP/CIDR filters.
| Rule type | Behavior |
|---|---|
Alert Spike |
Compares the current window with the previous window and triggers when percentage increase and minimum alert count are exceeded. |
Alert Threshold |
Triggers when matching alerts in the configured time window reach the threshold. |
New Alert/Decision |
Creates one notification for every matching alert, decision, or both within the lookback window. Includes record ID, timestamps, scenario, target, source/value, and related alert/decision details. Stable per-record deduplication prevents repeats. |
IP Ban |
Triggers once for each active ban decision in the configured window, supports exact IP/CIDR filters, and deduplicates duplicate active decision rows for the same ban. |
Recent CVE |
Extracts CVE IDs from matching alerts and checks publication age before notifying. |
LAPI Availability |
Triggers when CrowdSec LAPI stays unavailable past the outage threshold, with optional recovery notifications. |
Application Update |
Uses the built-in update check and triggers when a newer CrowdSec Web UI version is available. |
| Scope | Rules |
|---|---|
| Aggregate matching alerts across instances | Alert Spike, Alert Threshold, Recent CVE |
| Evaluate each matching record | New Alert/Decision, IP Ban |
| Evaluate each instance | LAPI Availability |
| Application-wide | Application Update |
Instance-backed titles and metadata identify the contributing instance or instances.
Note
The Recent CVE rule queries the NVD API to determine when a CVE was published. If outbound access to services.nvd.nist.gov is blocked, recent-CVE notifications may be skipped.
Destinations are independently enabled and reusable across rules. Send Test validates saved settings immediately; results are stored as delivered or failed.
| Destination | Settings |
|---|---|
SMTP host/port/security (Plain SMTP, STARTTLS, SMTPS / Implicit TLS), optional user/password, from address, comma-separated recipients, importance (auto, normal, important), and optional insecure TLS for trusted self-signed SMTP endpoints. Auto importance maps info to normal and warning/critical to important. |
|
| Gotify | Gotify URL, app token, and priority (auto or explicit integer). Auto priority maps info to 5, warning to 7, and critical to 10. |
| ntfy | Server URL, topic, optional access token, and priority (auto, min, low, default, high, urgent). Auto priority maps info to default, warning to high, and critical to urgent. |
| MQTT | Generic publish-only output with broker URL, optional username/password/client ID, QoS 0 or 1, keepalive, connect timeout, topic, and retain flag. It does not include Home Assistant discovery, entity sync, or command handling. |
| Webhook | Custom HTTP delivery with method (POST, PUT, PATCH), URL, optional query parameters/headers, auth (none, bearer token, or basic auth), body mode (JSON, Text, Form), timeout, retries, retry delay, and optional insecure TLS for trusted self-signed HTTPS endpoints. |
title, message, severity, metadata, sent_at, channel_id, channel_name, channel_type, rule_id, rule_name, and rule_type. Tests use rule_id=test, rule_name=Test notification, and rule_type=test.event.* fields for title, message, severity, metadata, sent_at, channel_name, rule_id, rule_name, and rule_type. Each has a *Json variant; nullable rule fields also have OrUnknown and OrUnknownJson aliases.notifications.debugPayloads: true also logs a truncated rendered body with sensitive form fields redacted; enable it only while troubleshooting.notifications.secretKey, or an auto-generated key stored in application metadata.notifications.allowPrivateAddresses: false blocks private, loopback, and link-local destinations; default: true.A Helm chart for CrowdSec Web UI is maintained by zekker6.
SQLite data lives under /app/data. Mount the directory—not only crowdsec.db—because WAL mode also uses crowdsec.db-wal and crowdsec.db-shm.
volumes: - ./data:/app/data
crowdsec.sync.lookback (default: seven days).Use POST /api/cache/clear for a full cache reset. Synchronization internals are documented in DEVELOPMENT.md.
| Guide | Contents |
|---|---|
| Configuration example | Complete commented YAML configuration |
| API reference | Authentication, routes, parameters, and request/response shapes |
| Development guide | Local setup, builds, tests, metadata, translations, and synchronization internals |
| Load testing guide | Synthetic profiles, overrides, benchmarks, and container workflow |
|
|
Linux Update Dashboard A self-hosted dashboard for checking and applying Linux package updates across multiple servers. |
more like this
Comprehensive analytics dashboard for AI coding agents — Cursor, Windsurf, Claude Code, VS Code Copilot, Zed, Antigravi…
YiMao · 云海求片助手 — 双核心 Telegram 影视求片机器人。订阅模式:TMDB 智能搜索一键订阅 / 趣味求片模式:AI 生成五层地狱闯关,通关解锁优先求片。深度集成 MoviePilot + Emby/Jellyfin。…
search projects, people, and tags