---
title: "Deploy pgwatch"
description: "Self-hosted PostgreSQL monitoring with preloaded Grafana dashboards."
category: "Observability"
url: https://railway.com/deploy/pgwatch
---

# Deploy pgwatch

Self-hosted PostgreSQL monitoring with preloaded Grafana dashboards.

**[Deploy pgwatch on Railway](https://railway.com/template/pgwatch)**

- **Creator:** darseen
- **Category:** Observability
- **Total deploys:** 1

## Template content

### grafana https://devicons.railway.com/i/grafana.svg

- **Image:** grafana/grafana-oss:13.0.2
- **Start command:** `sh -c 'set -e
DASHDIR=/var/lib/grafana/dashboards/postgres
STAMP=$DASHDIR/.provisioned-$PGWATCH_VERSION
if [ ! -f "$STAMP" ]; then
  rm -rf "$DASHDIR"
  mkdir -p "$DASHDIR"
  echo "fetching pgwatch v$PGWATCH_VERSION dashboards..."
  curl -fsSL "https://codeload.github.com/cybertec-postgresql/pgwatch/tar.gz/refs/tags/v$PGWATCH_VERSION" \
    | tar -xz -C "$DASHDIR" --strip-components=4 "pgwatch-$PGWATCH_VERSION/grafana/postgres/v12"
  touch "$STAMP"
fi
mkdir -p /etc/grafana/provisioning/dashboards /etc/grafana/provisioning/datasources
cat > /etc/grafana/provisioning/dashboards/pgwatch.yaml <<EOF
apiVersion: 1
providers:
  - name: pgwatch
    orgId: 1
    folder: pgwatch
    type: file
    disableDeletion: false
    allowUiUpdates: true
    updateIntervalSeconds: 30
    options:
      path: $DASHDIR
EOF
cat > /etc/grafana/provisioning/datasources/pgwatch.yaml <<EOF
apiVersion: 1
datasources:
  - name: pgwatch metrics (postgres)
    uid: pgwatch-metrics
    type: grafana-postgresql-datasource
    access: proxy
    url: $PGHOST:$PGPORT
    user: $PGUSER
    database: $PGDATABASE
    isDefault: true
    editable: true
    secureJsonData:
      password: $PGPASSWORD
    jsonData:
      sslmode: disable
      postgresVersion: 1800
EOF
exec /run.sh'`
- **Health check:** /api/health
- **Public domain:** Yes

### pgwatch https://github.com/cybertec-postgresql.png?size=460

- **Image:** cybertecpostgresql/pgwatch:5.3.0
- **Health check:** /readiness
- **Public domain:** Yes

### postgres-metrics https://devicons.railway.app/i/postgresql.svg

- **Image:** ghcr.io/railwayapp-templates/postgres-ssl:18

### postgres-config https://devicons.railway.app/i/postgresql.svg

- **Image:** ghcr.io/railwayapp-templates/postgres-ssl:18

## Documentation

# Deploy and Host pgwatch on Railway

pgwatch is an open-source PostgreSQL monitoring tool. It polls the statistics collectors of any Postgres instance you point it at, stores the measurements as time series, and visualizes them in Grafana. No agent is installed on the monitored database, only a connection string is needed.

## About Hosting pgwatch

This template runs the full pgwatch stack from public Docker images, with no source repository to fork or maintain. Four services deploy together: a config database holding the list of monitored instances, a metrics database holding the gathered time series, the pgwatch gatherer with its web UI, and Grafana. pgwatch creates its own schema in both databases on first connect, so nothing needs bootstrapping. Grafana fetches the 22 official pgwatch dashboards from the pinned upstream release on first boot, caches them on its volume, and provisions both the dashboards and the metrics datasource automatically, so the graphs are populated the moment you add a monitored database.

## Common Use Cases

- Finding slow queries and performance bottlenecks across production Postgres instances
- Tracking connection counts, locks, cache hit ratios, and WAL activity over time
- Watching table and index bloat, and reviewing pgwatch's built-in configuration recommendations

## Dependencies for pgwatch Hosting

- A PostgreSQL instance to monitor, reachable from Railway, plus a connection string for it. Everything else is provisioned by the template.
- A monitoring role on that instance. `pg_monitor` membership (or superuser) gives full coverage; a plain login role still yields the basic metrics.
- Optional: `pg_stat_statements` on the monitored instance for the query-performance dashboards.

### Deployment Dependencies

- [pgwatch repository](https://github.com/cybertec-postgresql/pgwatch)
- [pgwatch documentation](https://pgwat.ch/v5.x/)
- [pgwatch image](https://hub.docker.com/r/cybertecpostgresql/pgwatch)
- [Grafana documentation](https://grafana.com/docs/)

### Implementation Details

**Getting started after deploy.** Open the pgwatch service's domain and log in with `admin` and the generated `PW_WEBPASSWORD` from that service's variables. Add your database under Sources, then open Grafana (its own domain, user `admin`, generated `GF_SECURITY_ADMIN_PASSWORD`) and pick a dashboard from the `pgwatch` folder. The first measurements land within about two minutes, since pgwatch rescans its source list every 120 seconds.

**No custom images.** Upstream ships the Grafana dashboards as files in the source tree rather than in any image, which normally forces a custom Dockerfile and therefore a repository. Instead, the Grafana service runs the stock `grafana/grafana-oss` image with a start command that downloads the pinned release tarball, writes the dashboard and datasource provisioning YAML, and then hands off to Grafana's own entrypoint:

```sh
curl -fsSL "https://codeload.github.com/cybertec-postgresql/pgwatch/tar.gz/refs/tags/v$PGWATCH_VERSION" \
  | tar -xz -C "$DASHDIR" --strip-components=4 "pgwatch-$PGWATCH_VERSION/grafana/postgres/v12"
...
exec /run.sh
```

The download is cached on the Grafana volume and re-runs only when `PGWATCH_VERSION` changes, so restarts do not depend on GitHub being reachable. The datasource is provisioned with uid `pgwatch-metrics`, which is what all 22 dashboards bind to; renaming it silently blanks every panel.

**Plugins.** The dashboards use one external panel plugin, `marcusolsson-treemap-panel`, installed via `GF_PLUGINS_PREINSTALL`. `GF_INSTALL_PLUGINS` is deprecated in current Grafana and does nothing unless `GF_INSTALL_PLUGINS_FORCE=true` is also set.

**Ports and permissions.** Railway probes the healthcheck on `PORT`, while pgwatch binds `PW_WEBADDR`; both are pinned to 8080 so the probe and the process agree. Grafana runs with `RAILWAY_RUN_UID=0` because Railway mounts volumes root-owned while the image would otherwise run as uid 472.

**Credentials.** Both admin passwords are generated per deployment rather than shipped as `admin`/`admin`. Leaving `PW_WEBUSER` and `PW_WEBPASSWORD` both empty disables pgwatch's authentication entirely, so keep them set.

## Why Deploy pgwatch on Railway?


Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.

By deploying pgwatch on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.



## Similar templates

- [Pyroscope profiling](https://railway.com/deploy/pyroscope-profiling) — Protected continuous profiling with durable Pyroscope storage.
- [SigOnly](https://railway.com/deploy/sigonly) — Deploy SigNoz with a working demo app & config in one click
- [Unwrapped Spotify Music Stats](https://railway.com/deploy/wide-bold) — Unwrapped Spotify Music Stats, Estatísticas de músicas disponíveis

Open this page in a browser: https://railway.com/deploy/pgwatch
