Bichon Docs

Bichon exposes a Prometheus/OpenMetrics endpoint for operational monitoring. It is available in every edition and requires an API token with the system:metrics permission.

Endpoints

MethodPathAuthDescription
GET/healthzNoneLiveness: 200 ok while the process can serve HTTP, in normal operation and maintenance mode
GET/api/statusNoneReadiness and version payload: uptime, timezone, version; 503 in maintenance mode
GET/api/v1/metricsBearer token + system:metricsPrometheus text exposition (text/plain; version=0.0.4)

/api/status answers 200 while the process is serving and 503 while the server is in maintenance mode, so it works as a readiness probe and container healthcheck. /healthz answers 200 in both modes and is the correct liveness probe.

Health checks

Bichon separates liveness ("should the process be restarted?") from readiness ("should it receive traffic?"). They are different endpoints and must not be swapped.

EndpointRoleNormalMaintenance modeAuth
/healthzLiveness200 ok200 okNone
/api/statusReadiness + version payload200503None

Why two endpoints

In maintenance mode the process deliberately stays alive — a legacy data layout needs bichon-admin, a startup pre-check failed, or a Pro licence preflight was rejected — so that you can docker exec / kubectl exec into it and fix the problem. /api/status reports 503 there so load balancers and readiness probes stop sending traffic. A liveness probe must not point at /api/status: Kubernetes restarts the container when liveness fails, which would kill the process exactly when you need to exec into it. /healthz therefore answers 200 in both modes.

/healthz is a pure process-alive signal. It does not open the database, the blob store, or the search indexes, and it does not check disk space: it says "the HTTP loop can answer", not "the archive is healthy". Use /api/status, the metrics endpoint, or the UI for deeper checks.

Docker

The bundled Dockerfile healthcheck uses /api/status, so the container is reported unhealthy during maintenance mode while remaining reachable. Docker itself never restarts a container just because it is unhealthy, so this is safe and keeps docker ps honest. Keep the Docker healthcheck on /api/status; pointing it at /healthz would make maintenance mode look healthy to tools that watch container health.

Kubernetes

livenessProbe:
  httpGet:
    path: /healthz
    port: 15630
  initialDelaySeconds: 10
  periodSeconds: 10
  failureThreshold: 3
readinessProbe:
  httpGet:
    path: /api/status
    port: 15630
  initialDelaySeconds: 5
  periodSeconds: 10
  failureThreshold: 3

With this pair, a pod in maintenance mode is taken out of service (readiness 503) but is not restarted (liveness 200), so kubectl exec keeps working while you run the migration or reindex.

Sub-path deployments

/healthz is mounted at the server root and is not affected by --bichon-base-url (default /), so probes keep a stable path even when the UI and API are served under a prefix such as /bichon. /api/status follows --bichon-base-url, like every other API route. In maintenance mode both endpoints are served over plain HTTP, because TLS is not initialized before the startup checks pass.

Creating a scrape token

Create an API token under Settings → API Tokens and enable metrics only. A metrics-only token:

  • is accepted only on GET /api/v1/metrics and rejected on every other endpoint;
  • can only be created for a user who already holds system:metrics;
  • has the same optional expiry as a normal token.

A normal API token belonging to a user with system:metrics also works. Use a metrics-only token for your monitoring system so a leaked scrape credential cannot read the archive.

Example Prometheus scrape configuration:

scrape_configs:
  - job_name: bichon
    metrics_path: /api/v1/metrics
    scheme: http
    bearer_token: "<metrics-only API token>"
    static_configs:
      - targets: ["bichon.example.com:15630"]

If the REST API is behind HTTPS or a reverse proxy, adjust scheme and targets accordingly.

Metric reference

Counters reset to zero when the process restarts; use rate() or increase() in queries and alerting. Archive totals (emails_total, attachments_total, storage/index usage) are TTL-cached and refreshed periodically rather than recomputed on every scrape.

Build and uptime

MetricTypeLabelsMeaning
bichon_build_infogaugeversionAlways 1; version is in the label
bichon_uptime_secondsgauge—Seconds since process start

Archive inventory

MetricTypeLabelsMeaning
bichon_accounts_totalgaugetypeConfigured accounts by type (imap, nosync, view)
bichon_users_totalgauge—Configured users
bichon_account_mailbox_folders_totalgauge—Tracked mailbox folders across all accounts
bichon_emails_totalgauge—Archived emails
bichon_attachments_totalgauge—Archived attachments
bichon_storage_usage_bytesgauge—Blob storage disk usage
bichon_index_usage_bytesgauge—Full-text index disk usage

Per-account sync state

All of these carry an account_id label. Join them to bichon_account_info{account_id,email,name,type,enabled} to get readable names.

MetricTypeMeaning
bichon_account_sync_runninggauge1 while a download session is running
bichon_account_last_sync_start_timestamp_secondsgaugeStart of the most recent session
bichon_account_last_sync_success_timestamp_secondsgaugeEnd of the most recent successful session (0 = never)
bichon_account_last_sync_succeededgauge1 if the last finished session succeeded
bichon_account_last_sync_duration_secondsgaugeDuration of the last finished session
bichon_account_last_sync_errorsgaugeErrors recorded in the last finished session
bichon_account_emails_totalgaugeEmails archived for the account
bichon_account_download_interval_minutesgaugeConfigured interval (0 = no sync)
bichon_account_gapfill_runninggauge1 while a gap-fill run is in progress
bichon_account_last_gapfill_downloadedgaugeMessages downloaded by the last gap-fill run
bichon_account_last_gapfill_failedgaugeMessages that failed in the last gap-fill run

Ingestion and sync

MetricTypeLabelsMeaning
bichon_emails_ingested_totalcounteraccount_id, source, outcomeIngestion outcomes: imported, filtered, duplicate, failed
bichon_sync_errors_totalcounteraccount_id, kindErrors classified as auth, throttled, network, timeout, protocol, internal
bichon_sync_sessions_totalcounteraccount_id, trigger, statusCompleted sessions, including cancelled ones
bichon_sync_session_duration_secondshistogramaccount_idSession duration

Service-level

MetricTypeLabelsMeaning
bichon_http_requests_totalcountermethod, statusREST requests by status class (2xx … 5xx)
bichon_http_request_duration_secondshistogrammethodREST latency
bichon_smtp_messages_totalcounteroutcomeSMTP messages overall (accepted / rejected)
bichon_smtp_rejections_totalcounterreasonSMTP rejections before archiving
bichon_smtp_active_connectionsgauge—Open SMTP connections
bichon_backup_runs_totalcounteroutcomeBackup runs (success / failed)
bichon_backup_duration_secondshistogram—Backup duration
bichon_backup_last_success_timestamp_secondsgauge—Last successful backup (0 = never)
bichon_oauth2_token_refresh_totalcounteroutcomeOAuth2 token refresh attempts

Useful alert conditions

  • bichon_account_last_sync_succeeded == 0 for an hour or more — a mailbox is failing to sync.
  • time() - bichon_backup_last_success_timestamp_seconds above your backup interval — backups have stopped succeeding.
  • rate(bichon_sync_errors_total{kind="auth"}[15m]) > 0 — credentials or OAuth refresh need attention.
  • rate(bichon_smtp_rejections_total[15m]) > 0 — rejected SMTP deliveries; check whether authentication, TLS, or the target account is misconfigured.

On this page