Kubernetes
In your watchlog-node-agent.yaml, locate the PostgreSQL env vars:
- name: MONITOR_POSTGRESQL
value: "false"
- name: POSTGRESQL_HOST
value: "127.0.0.1"
- name: POSTGRESQL_PORT
value: "5432"
- name: POSTGRESQL_USERNAME
value: ""
- name: POSTGRESQL_PASSWORD
value: ""
- name: POSTGRESQL_DATABASES
value: ""
Update as follows:
- value: "false"
+ value: "true"
- - name: POSTGRESQL_DATABASES
- value: ""
+ - name: POSTGRESQL_DATABASES
+ value: "db1,db2"
Then apply:
kubectl apply -f watchlog-node-agent.yaml
POSTGRESQL_DATABASES decides which databases get per-table and per-index detail. Cluster- wide metrics — connections, transactions, cache ratio, WAL, replication — need no list at all.
Keep the password in a Secret
apiVersion: v1
kind: Secret
metadata:
name: watchlog-postgresql
namespace: watchlog
type: Opaque
stringData:
password: "your_password"
- name: POSTGRESQL_PASSWORD
valueFrom:
secretKeyRef:
name: watchlog-postgresql
key: password
Pointing at a PostgreSQL Service
The node agent runs as a DaemonSet, so 127.0.0.1 only reaches a PostgreSQL on that same node. For a cluster-hosted database, use the Service DNS name:
- name: POSTGRESQL_HOST
value: "postgres.database.svc.cluster.local"
- name: POSTGRESQL_PORT
value: "5432"
pg_hba.conf
The agent connects from a pod IP that changes on every restart. A pg_hba.conf line scoped to a single address will start failing the first time the agent pod is rescheduled. Use the pod CIDR:
host all watchlog_monitor 10.244.0.0/16 scram-sha-256
Creating the monitoring role
kubectl exec -i -n database statefulset/postgres -- psql -U postgres <<'SQL'
CREATE USER watchlog_monitor WITH PASSWORD 'your_password';
GRANT pg_monitor TO watchlog_monitor;
GRANT CONNECT ON DATABASE myapp TO watchlog_monitor;
SQL
kubectl exec needs -i for the heredoc to reach the container.
Which agent should monitor it?
A DaemonSet means every node's agent polls PostgreSQL — N agents collecting the same data, each opening one connection per configured database. Watchlog identifies an instance by host:port, so they all write to the same integration and the data stays correct, but the connections are wasted and they count against max_connections.
- Preferred: run a single-replica Deployment of the agent dedicated to integrations, and keep the DaemonSet for host and container metrics only.
- Or use a node selector so only one node's agent carries the PostgreSQL env vars.
pg_stat_statements
This is what unlocks the Queries and Slow Queries tabs, and Watchlog will never install it. For a Helm-deployed PostgreSQL, set it in the chart values:
primary:
extendedConfiguration: |
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.track = top
shared_preload_libraries needs a pod restart, not a reload. Then create the extension once per database:
kubectl exec -i -n database statefulset/postgres -- \
psql -U postgres -d myapp -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;"
Advanced options in Kubernetes
The env vars cover the connection. For collection caps, throttle intervals and the activity thresholds, mount an integration.json through a ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: watchlog-integrations
namespace: watchlog
data:
integration.json: |
[
{
"service": "postgresql",
"monitor": true,
"host": "postgres.database.svc.cluster.local",
"port": "5432",
"username": "watchlog_monitor",
"database": ["myapp"],
"advanced": {
"queries": true,
"activity": true,
"locks": true,
"storage": true,
"indexes": true,
"vacuum": true,
"wal": true,
"replication": true,
"storageIntervalSeconds": 300
},
"slowQuery": { "thresholdMs": 100 },
"activity": {
"longQuerySeconds": 30,
"longTransactionSeconds": 60,
"idleTransactionSeconds": 300
}
}
]
volumeMounts:
- name: integrations
mountPath: /app/app/config/integration.json
subPath: integration.json
readOnly: true
volumes:
- name: integrations
configMap:
name: watchlog-integrations
Warning
Do not put the password in the ConfigMap — a ConfigMap is readable by anything with get on the namespace. Keep it in the POSTGRESQL_PASSWORD Secret env var.
Operator-managed clusters
For PostgreSQL deployed by an operator (CloudNativePG, Zalando, Crunchy), the credentials live in a generated Secret:
kubectl get secret <cluster>-superuser -n database \
-o go-template='{{.data.password | base64decode}}'
Create a dedicated watchlog_monitor role with pg_monitor rather than reusing the superuser. Point the agent at the read-write Service for cluster-wide metrics; adding the read-only Service as a second entry gives you each replica's own replay lag, which the primary cannot report.
