Self-Hosted Collector
The self-hosted collector is a lightweight binary you run on your own infrastructure. Use this mode when your database is not publicly reachable, or when you want full control over the collector.
Before You Start
- Sign in at app.pgpulse.io.
- Click Add Instance and select Self-Hosted.
- Copy your API key (
pk_live_...) from Settings → API Keys.
Postgres Requirements
| Requirement | Detail |
|---|---|
| PostgreSQL version | 12 or later |
| Role | pg_monitor membership |
| Extensions (optional) | pg_stat_statements — enables query throughput metrics. pg_buffercache — enables memory metrics. The collector skips these gracefully if not installed. |
| Connections used | 1 persistent connection (+ 1 per database if multi-database mode is enabled) |
CREATE USER pgpulse_monitor WITH PASSWORD 'strong-password';
GRANT pg_monitor TO pgpulse_monitor;
Configuration File
Create a pgpulse-collector.yaml file on the machine that will run the collector.
Minimal config
postgres:
host: "your-db-host"
port: 5432
user: "pgpulse_monitor"
password: "strong-password"
database: "postgres"
sslmode: "require"
api:
endpoint: "https://api.pgpulse.io/v1/collect"
api_key: "pk_live_your_api_key_here"
collector:
instance_name: "prod-primary"
Connection Scenarios
Supabase — Direct Connection
postgres:
host: "db.<your-project-ref>.supabase.co"
port: 5432
user: "postgres"
password: "<your-db-password>"
database: "postgres"
sslmode: "require"
Supabase — Pooler Connection
If you connect via the Supabase pooler, embed your project ref in the username.
postgres:
host: "aws-<zone-id>-<region>.pooler.supabase.com"
port: 5432
user: "postgres.<your-project-ref>"
password: "<your-db-password>"
database: "postgres"
sslmode: "require"
Session mode only — transaction mode is not supported.
Bare Metal / VPS / Any Cloud Provider
postgres:
host: "<host-ip>"
port: 5432
user: "pgpulse_monitor"
password: "strong-password"
database: "postgres"
sslmode: "prefer"
Connection String (DSN)
If you store a full DSN in a secret manager, use connection_string instead of individual fields.
postgres:
connection_string: "postgresql://pgpulse_monitor:password@host:5432/postgres?sslmode=require"
Multiple Databases on One Instance
Enable collect_all_databases to cover all databases on the instance (useful for bloat, vacuum health, and index stats per database).
collector:
instance_name: "prod-primary"
collect_all_databases: true
# Optional allow/deny lists
include_databases:
- "postgres"
- "app_prod"
exclude_databases:
- "analytics_shadow"
The Pulse score stays a single per-instance number. The dashboard shows which database drove a domain's score.
Deployment
Docker (recommended)
docker run -d \
--name pgpulse-collector \
--restart unless-stopped \
-e PGPULSE_API_KEY=pk_live_your_api_key \
-e PGPULSE_POSTGRES_HOST=your-db-host \
-e PGPULSE_POSTGRES_USER=pgpulse_monitor \
-e PGPULSE_POSTGRES_PASSWORD=strong-password \
-e PGPULSE_POSTGRES_DATABASE=postgres \
-e PGPULSE_POSTGRES_SSLMODE=require \
-e PGPULSE_COLLECTOR_INSTANCE_NAME=prod-primary \
-v pgpulse-buffer:/var/lib/pgpulse/buffer \
pgpulse/collector:latest
Mount the buffer volume so data survives container restarts.
systemd (Linux)
# Download the binary
curl -L https://releases.pgpulse.io/collector/latest/pgpulse-collector-linux-amd64 \
-o /usr/local/bin/pgpulse-collector
chmod +x /usr/local/bin/pgpulse-collector
# Dedicated system user
useradd --system --no-create-home --shell /bin/false pgpulse
# Directories
mkdir -p /etc/pgpulse /var/lib/pgpulse/buffer
chown pgpulse:pgpulse /var/lib/pgpulse/buffer
# Config
cp pgpulse-collector.yaml /etc/pgpulse/collector.yaml
chown root:pgpulse /etc/pgpulse/collector.yaml
chmod 640 /etc/pgpulse/collector.yaml
Create /etc/systemd/system/pgpulse-collector.service:
[Unit]
Description=pgpulse Postgres Collector
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=pgpulse
ExecStart=/usr/local/bin/pgpulse-collector --config /etc/pgpulse/collector.yaml
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now pgpulse-collector
journalctl -u pgpulse-collector -f
Environment Variables (no config file)
Useful for Heroku, Render, Railway, and similar PaaS platforms.
export PGPULSE_API_KEY=pk_live_your_api_key
export PGPULSE_POSTGRES_HOST=your-db-host
export PGPULSE_POSTGRES_PORT=5432
export PGPULSE_POSTGRES_USER=pgpulse_monitor
export PGPULSE_POSTGRES_PASSWORD=strong-password
export PGPULSE_POSTGRES_DATABASE=postgres
export PGPULSE_POSTGRES_SSLMODE=require
export PGPULSE_COLLECTOR_INSTANCE_NAME=prod-primary
./pgpulse-collector
| Variable | Description |
|---|---|
PGPULSE_API_KEY | Required. Your pk_live_ API key. |
PGPULSE_POSTGRES_CONNECTION_STRING | Full DSN — overrides individual fields. |
PGPULSE_POSTGRES_HOST | Postgres host. |
PGPULSE_POSTGRES_PORT | Postgres port (default 5432). |
PGPULSE_POSTGRES_USER | Postgres user. |
PGPULSE_POSTGRES_PASSWORD | Postgres password. |
PGPULSE_POSTGRES_DATABASE | Database name. |
PGPULSE_POSTGRES_SSLMODE | disable · prefer · require · verify-full |
PGPULSE_COLLECTOR_INSTANCE_NAME | Label shown on the dashboard. |
PGPULSE_COLLECTOR_COLLECT_ALL_DATABASES | Set true to enable multi-database mode. |
PGPULSE_LOGGING_LEVEL | debug · info · warn · error (default info) |
PGPULSE_LOGGING_FORMAT | json · text (default json) |
Validate Before Going Live
./pgpulse-collector --config collector.yaml --validate
Checks your config, confirms the Postgres host is reachable, opens a connection, and verifies your API key — then exits. Exit code 0 means everything is good.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
FATAL: PGPULSE_API_KEY is required | No API key set | Add api.api_key to your YAML or set PGPULSE_API_KEY. |
preflight TCP check failed | Postgres host unreachable | Check the host, port, and any firewall rules. |
401 at startup | Invalid API key | Re-copy the key from the dashboard. |
403 at startup | Host doesn't match registered instance | For Supabase pooler, make sure the project ref is in the username (postgres.<ref>). |
| Pulse not updating on dashboard | Collector not sending data | Run --validate and check the logs with journalctl -u pgpulse-collector -f. |