Consent Docs

Monitor a deployment

Check health, scrape the Prometheus metrics, and set the first alerts for a self-hosted deployment.

How-to3 min readFor project administrators and deployment operators

Set this up before real consent is recorded. You watch three things: the health check, the metrics and the error lines in the log.

Before you start#

  • METRICS_TOKEN is set, as in Deploy with Docker Compose.
  • You run Prometheus on a host that can reach port 3003 of the API. The console does not forward /metrics.

Set up checks and alerts#

  1. Add an HTTP check on /health.

    Shell
    curl -s https://consent.example.in/health

    A healthy API answers 200:

    JSON
    {
      "status": "ok",
      "database": "up",
      "redis": true,
      "uptimeSeconds": 8123,
      "timestamp": "2026-09-18T06:41:12.000Z"
    }
  2. Alert when redis in that body is false. The status code stays 200, but nobody can sign in.

  3. Write the metrics token to a file that Prometheus can read, such as /etc/prometheus/piedap-consent.token.

  4. Add the scrape job.

    prometheus.ymlYAML
    scrape_configs:
      - job_name: piedap-consent
        metrics_path: /metrics
        authorization:
          credentials_file: /etc/prometheus/piedap-consent.token
        static_configs:
          - targets: ['127.0.0.1:3003']
  5. Add the first alerts.

    alerts.ymlYAML
    groups:
      - name: piedap-consent
        rules:
          - alert: ConsentApiErrors
            expr: >-
              sum(rate(http_requests_total{status=~"5.."}[5m]))
              / sum(rate(http_requests_total[5m])) > 0.01
            for: 10m
          - alert: ConsentFormLatency
            expr: >-
              histogram_quantile(0.95, sum by (le) (rate(
              http_request_duration_seconds_bucket{route="/sdk/v1/form"}[5m]
              ))) > 0.3
            for: 15m
          - alert: ConsentWebhookFailures
            expr: >-
              sum(rate(webhook_deliveries_total{outcome!="delivered"}[15m]))
              > 0
            for: 30m
          - alert: ConsentAnchorMissing
            expr: sum(increase(evidence_anchors_total[26h])) == 0
          - alert: ConsentDashboardSlow
            expr: >-
              histogram_quantile(0.95, sum by (le) (rate(
              dashboard_query_seconds_bucket{cost="E"}[1d]
              ))) > 2
          - alert: ConsentCheckSlow
            expr: >-
              histogram_quantile(0.95, sum by (le) (rate(
              http_request_duration_seconds_bucket{
              route="/sdk/v1/consent/check"}[5m]))) > 0.3
            for: 15m
          - alert: ConsentCheckNoRecord
            expr: >-
              sum(rate(consent_checks_total{
              reason="no_record",environment="live"}[15m]))
              / sum(rate(consent_checks_total{environment="live"}[15m]))
              > 0.9
            for: 30m
          - alert: ConsentAuditWriteFailures
            expr: increase(audit_log_write_failures_total[10m]) > 0
          - alert: ConsentNoticeWithdrawalsPending
            expr: notice_withdrawals_pending > 0
            for: 15m
          - alert: ConsentProcessorTasksOverdue
            expr: processor_tasks_overdue > 0
  6. Reload Prometheus and check that the target piedap-consent is up.

Use ConsentAnchorMissing only if the deployment records consent every day. A day with no sealed events has no anchor.

What to watch#

Signal Where What it tells you
/health answers 200 HTTP check 503 means the database does not answer
http_requests_total, by method, route and status /metrics The error rate
http_request_duration_seconds, by method and route /metrics Latency. Every page view calls /sdk/v1/form
consent_writes_total, by event, source and environment /metrics A sudden drop means sites cannot record consent
consent_form_resolutions_total, by outcome /metrics Forms that do not resolve
webhook_deliveries_total, by outcome /metrics rejected or failed means customers miss events
evidence_anchors_total /metrics The daily anchor job ran
dashboard_query_seconds, by cost /metrics Time taken by each chart query of the Overview
dashboard_query_timeouts_total, by cost /metrics Chart queries stopped by their time limit
consent_checks_total, by reason and environment /metrics Consent checks answered. A sudden rise of no_record can mean identifiers are hashed differently, for example after the pepper changed
consent_import_rows_total, by mode, outcome and environment /metrics Rows checked and written by console imports
notice_links_issued_total, by kind /metrics Notice campaign links issued
notice_responses_total, by response /metrics Answers given on notice links
notice_anchors_total /metrics Campaign anchors written, one for each project and day with notice answers or issued batches. A day without either adds none
notice_withdrawals_pending /metrics Withdrawals made on a notice link over a minute ago and not yet applied to the records
processor_tasks_overdue /metrics Live processor tasks past their due date, at the last daily check
audit_log_write_failures_total /metrics Audit entries the database did not store. Each one is still on standard output with "stored": false

http_request_duration_seconds has buckets at 0.2 and 0.3 seconds, besides the others. cost says what a chart query reads: R daily totals, E single events, I single records, J purposes inside records, S small tables. ConsentDashboardSlow fires when charts that read single events take more than 2 seconds over a day. Timeouts that rise in normal use mean the same. Check the database's load first, then consider a longer DASHBOARD_STATEMENT_TIMEOUT_MS.

Log lines to alert on#

Logs are JSON lines with level, message, timestamp and context. A failed job is logged with "level":"error" and raises nothing else.

Message starts with Meaning
Daily anchor failed No anchors were written. The next run tries again
Retention sweep failed, Sandbox purge failed, Expiry sweep failed A scheduled job stopped
Failed to save audit log An audit entry was not stored. Its copy is on standard output
Disabled webhook An endpoint was turned off after repeated failures
Inactivity erasure is on for project The project has no endpoint for the notice, so nothing is erased
Vendor list refresh failed The API cannot reach the IAB vendor list

The last three messages are logged with "level":"warn", not "error".

Each access log line ends with the request id the caller received in X-Request-Id. Ask for that id when someone reports a failure.

↑↓ to move, Enter to open