Skip to content

ProductDatabases

Put what your database knows on your status page.

Stuck orders, failed invoices, a backed-up queue. One read-only query on a schedule becomes a status your customers can see, with no exporter to run.

Postgres: the query returns one number, 4 orders. Healthy under 20, unhealthy over 100. Verdict: Healthy.

The query

SQL
SELECT count(*) FROM orders
WHERE status = 'pending'
  AND created_at < now() - interval '10 min'

What Postgres returns

count4

One row, one column. Any other shape is no data, with a reason.

One number

Orders stuck in pending

4orders

The verdict

Healthy
< 20
Unhealthy
> 100
Otherwise
degraded

Healthy

4 is under 20: the healthy rule matches.

Your status page

Shown as each reading lands. A real page waits out the confirmation time first, 3 minutes by default.

status.example.com
All systems operational

Services

  • Orders
    100%Operational
15 OctNow
The status page is the product's own components, drawing sample data. Switch the engine or step through the readings.

Secrets

Your connection string stays on your host.

Observer stores the name of an environment variable. The agent reads its value when the query runs, inside your network, and sends back the number, never the secret.

Stored in Observer

Cloud
key: stuck-orders
source_type: database
source_config:
  kind: postgres
  connection_string_ref: ORDERS_PG_DSN
  query: |
    SELECT count(*) FROM orders
    WHERE status = 'pending'
      AND created_at < now() - interval '10 min'
  statement_timeout_ms: 5000

Paste the URL itself and the save is refused: "connection_string_ref must be an UPPER_SNAKE_CASE env var name".

Only on the agent host

Your network
# agent environment
ORDERS_PG_DSN=postgres://observer_ro:••••••@orders-db:5432/orders

Read on every run. Never logged and never written to a result: it goes to your database and nowhere else.

Sent to Observer

Each reading
{
  "metric_id": "9b41…",
  "value": 37,
  "timestamp": "2026-10-06T12:05:00Z",
  "status": "degraded"
}

The number and the agent's verdict. No rows, no columns, no credentials.

Guardrails

Nothing that writes gets through.

Give the agent a read-only user; your database enforces that. On top of it, a query that could write is refused when you save, the agent checks every query again before it leaves the host, and it refuses any answer that isn't one number.

A connection string pasted where the reference goes

connection_string_ref: postgres://observer_ro:s3cret@orders-db:5432/orders

Refused

When you save

connection_string_ref must be an UPPER_SNAKE_CASE env var name

A write

UPDATE orders SET status = 'failed'
WHERE status = 'pending'

Refused

When you save, and again on the agent

UPDATE statements are not allowed

A second statement

SELECT 1; DROP TABLE orders

Refused

When you save, and again on the agent

multi-statement queries are not allowed

A SELECT that writes a table

SELECT * INTO orders_copy FROM orders

Refused

When you save, and again on the agent

INTO clause is not allowed

A Redis command that isn't a read

FLUSHALL

Refused

When you save, and again on the agent

FLUSHALL is not in the read-only command allowlist

A MongoDB operation that isn't a count

{"db": "app", "collection": "webhooks",
 "op": "deleteMany", "filter": {}}

Refused

When you save, and again on the agent

op must be countDocuments or estimatedDocumentCount

A query that returns rows, not a number

SELECT id FROM orders WHERE status = 'pending'

No data

After the query

db_multi_row: query returned 37 rows

A CTE that ends in one count

WITH stuck AS (
  SELECT id FROM orders WHERE status = 'pending')
SELECT count(*) FROM stuck

Runs

Runs

One row, one column: the value is the count.

Timeout

A slow query is stopped.

5 seconds by default, up to 30. Postgres, MySQL and MongoDB cancel the query on the server; for Redis, the agent abandons the command when time runs out.

Connections

Two at most.

Each connection string gets a pool of two, so a probe can't exhaust your connection limit.

Wrong shape

No data, with a reason.

No rows, several rows, several columns, a null or a value that isn't a number: the metric shows no data and says which.

Then

From here, it's just a metric.

The count gets the same thresholds, confirmation window, services, SLOs and drafted incidents as any other source.

Credential

Create a read-only user.

Put its connection string in the agent's environment under a name like ORDERS_PG_DSN.

Query

Ask for one number.

A SQL SELECT, a Redis read command or a MongoDB count. It runs every minute by default.

Page

Bind it to a service.

Two thresholds decide the verdict, and every status page that lists the service follows it.

Connect one metric. See its record in ten minutes.