---
title: "Deploy PgDog"
description: "PostgreSQL proxy that pools connections and splits reads from writes"
category: "Other"
url: https://railway.com/deploy/pgdog-pooler
---

# Deploy PgDog

PostgreSQL proxy that pools connections and splits reads from writes

**[Deploy PgDog on Railway](https://railway.com/template/pgdog-pooler)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/pgdog-pooler/manifest.json

- **Creator:** A3A
- **Category:** Other
- **Total deploys:** 1

## Template content

### postgres-replica https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/postgresql.svg

- **Source:** https://github.com/gridalpha/postgresql-railway
- **Health check:** /healthz

### pgdog https://cdn.jsdelivr.net/gh/pgdogdev/pgdog@main/.github/logo-white.png

- **Source:** https://github.com/gridalpha/pgdog-railway
- **Health check:** /healthz

### postgres https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/postgresql.svg

- **Source:** https://github.com/gridalpha/postgresql-railway
- **Health check:** /healthz

### pgweb https://cdn.jsdelivr.net/gh/sosedoff/pgweb@master/static/img/icon.png

- **Image:** sosedoff/pgweb:latest
- **Start command:** `/bin/sh -c 'exec /usr/bin/pgweb --bind=0.0.0.0 --listen=8081 --skip-open --lock-session --open-retry=60 --open-retry-delay=5 --metrics --metrics-addr=:9090 --metrics-path=/metrics --auth-user="$PGWEB_AUTH_USER" --auth-pass="$PGWEB_AUTH_PASS"'`
- **Health check:** /metrics
- **Public domain:** Yes

## Documentation

# Deploy and Host PgDog on Railway

Deploy PgDog and you get a PostgreSQL proxy that understands SQL. Written in Rust, it pools thousands of client connections onto a handful of server ones, parses every statement with PostgreSQL's own parser, and routes it: writes and explicit transactions to the primary, plain `SELECT`s to a replica. Teams reach for it when a serverless runtime or a large worker fleet exhausts `max_connections`, when read traffic outgrows one machine, or when a database has to be sharded — PgDog splits a cluster across shards and reassembles cross-shard results in memory.

Self-host PgDog on Railway and the whole read-scaling shape is wired up for you. The `pgdog` service is the only endpoint anything talks to, published on a TCP proxy so `psql` and any driver reach it from outside the project. Behind it, `postgres` runs PostgreSQL 18 as the primary and `postgres-replica` is a hot standby that clones itself with `pg_basebackup` on first boot, then streams WAL through a physical replication slot. `pgweb` gives you a browser SQL console pointed at the pooler. Neither database is reachable from the internet.

![Diagram of the PgDog, PostgreSQL primary, replica and pgweb services](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789478835/postgresql-pgdog-architecture.webp)

## Getting Started with PgDog on Railway

Deploy starts four services and generates the endpoints; there is no first-run wizard. Open the `pgweb` URL, sign in with the basic-auth user and password you supplied at deploy time, and you land on a console already connected through PgDog to the `railway` database. Create a table and insert rows from the **Query** tab — those statements are parsed and sent to the primary. Then run `select pg_is_in_recovery();` a few times: PgDog alternates it between primary and standby, so the answer flips between `false` and `true`. That is the read/write split working.

For application traffic, copy `DATABASE_URL` from the `pgdog` service for anything in the same project, or `DATABASE_PUBLIC_URL` for a client outside it — that one carries `sslmode=require`, because PgDog presents its own certificate. To inspect the pooler, connect to the `admin` database as `admin` and run `SHOW POOLS`, `SHOW SERVERS` and `SHOW CLIENTS`: the PgBouncer-style console, reporting per-node health, bans and connection reuse.

![pgweb browsing 420 order rows through the PgDog endpoint](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789478839/postgresql-pgdog-orders-table.webp)
![Revenue grouped by order status in the pgweb SQL editor](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789478841/postgresql-pgdog-revenue-query.webp)
![Query result showing the read was served by a replica](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789478842/postgresql-pgdog-read-routing.webp)

## About Hosting PgDog

PgDog sits between your application and PostgreSQL and speaks the PostgreSQL wire protocol on both sides, so no client library changes. Comparing PgDog vs PgBouncer: PgBouncer only multiplexes sockets, while PgDog parses each query with `pg_raw_parse`, the real PostgreSQL parser. That is what lets it tell a write from a read, extract a sharding key, and replay `SET` statements correctly while sharing one server connection between clients.

Key capabilities:

- Transaction and session pooling, with `SET` statements and startup options replayed correctly
- Layer-7 load balancing over a primary and any number of replicas: round-robin, random or least-active-connections
- Automatic read/write split at one endpoint, plus per-query opt-in with `SET pgdog.role`
- Health checks that pull a failing database out of rotation, and lag bans that stop a stale replica serving reads
- Sharding by hash, list or range, with cross-shard `SELECT`, `INSERT`, `UPDATE`, `COPY` and two-phase commit
- A PgBouncer-compatible admin database, OpenMetrics and OpenTelemetry export

Each job lives in one service. `pgdog` holds no state and owns the public endpoint, TLS and pool configuration. `postgres` is the single writer, its cluster on a volume. `postgres-replica` is a read-only hot standby on its own volume, provisioned from the primary with a least-privilege `replicator` role. `pgweb` is a console you can delete.

## Why Deploy PgDog on Railway

Railway removes the operational work this topology needs:

- Replica bootstrap, replication user and `pg_hba` rule configured for you
- Private networking keeps both PostgreSQL services off the internet
- A TCP proxy publishes the pooler for external clients, with TLS already on
- Volumes, health checks and restart policies set per service
- Vertical resizing re-tunes PostgreSQL's memory settings on restart

## Common Use Cases

- Putting a serverless or edge application in front of PostgreSQL without exhausting `max_connections`
- Scaling read-heavy dashboards by adding replicas behind one unchanged connection string
- Giving a Rails, Django or Node app read/write splitting without touching application code
- Preparing for sharding: run PgDog as a pooler first, add shards and a sharding key later

## Dependencies for PgDog

- **pgdog** — `ghcr.io/pgdogdev/pgdog:latest`, with an entrypoint that renders `pgdog.toml` and `users.toml` from these variables
- **postgres** and **postgres-replica** — `ghcr.io/railwayapp-templates/postgres-ssl:18`, the image behind Railway's managed PostgreSQL, with TLS and pgvector
- **pgweb** — `sosedoff/pgweb:latest`, a single-binary SQL client

### Environment Variables Reference

| Variable | Service | Purpose |
|---|---|---|
| `PGDOG_CLIENT_PASSWORD` | pgdog | Password clients present to the pooler |
| `PGDOG_ADMIN_PASSWORD` | pgdog | Unlocks the `admin` console |
| `PGDOG_DB_NAME` | pgdog | Database name clients connect to |
| `PGDOG_REPLICA_HOSTS` | pgdog | Comma-separated replicas; empty means primary only |
| `PGDOG_POOLER_MODE` | pgdog | `transaction` or `session` |
| `PGDOG_DEFAULT_POOL_SIZE` | pgdog | Server connections per node |
| `PGDOG_BAN_REPLICA_LAG_BYTES` | pgdog | Lag past which a replica stops reads |
| `POSTGRES_PASSWORD` | postgres | Superuser password |
| `POSTGRES_REPLICATION_PASSWORD` | postgres | Password for the `replicator` role |
| `PGWEB_AUTH_USER` / `PGWEB_AUTH_PASS` | pgweb | Basic auth for the console |

Every other `[general]` setting in PgDog's reference is an environment variable too: upper-case the name and prefix `PGDOG_`.

### Deployment Dependencies

- Source: [github.com/pgdogdev/pgdog](https://github.com/pgdogdev/pgdog) (AGPL-3.0)
- Docs: [docs.pgdog.dev](https://docs.pgdog.dev/)

## Hardware Requirements for Self-Hosting PgDog

| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 1 vCPU pgdog, 1 per database | 2–4 vCPU pgdog, 2+ per database |
| RAM | 256 MB pgdog, 1 GB per database | 512 MB+ pgdog, 4 GB+ per database |
| Storage | 5 GB volume per database | Your dataset plus WAL headroom |
| Runtime | Linux container, PostgreSQL 14+ | PostgreSQL 18 |

PgDog is light: an async Rust proxy with no disk state. Give the memory to the databases.

## Self-Hosting PgDog

PgDog reads two files: `pgdog.toml` for hosts and routing, `users.toml` for credentials. A primary with one replica:

```
[general]
host = "0.0.0.0"
port = 6432
pooler_mode = "transaction"

[[databases]]
name = "app"
host = "primary.internal"
role = "primary"

[[databases]]
name = "app"
host = "replica.internal"
role = "replica"
read_only = true
```

With a `users.toml` beside it naming one user, the published image runs from Docker:

```
docker run -p 6432:6432 \
  -v $PWD/pgdog.toml:/pgdog/pgdog.toml \
  -v $PWD/users.toml:/pgdog/users.toml \
  ghcr.io/pgdogdev/pgdog:latest
```

To build from source, clone the repository and run `cargo build --release` — the release profile matters for throughput. The project also publishes a Helm chart and an ECS Terraform module.

## How Much Does PgDog Cost to Self-Host?

PgDog is free and open source under the AGPL-3.0. A paid enterprise edition adds extra features, but the community build includes pooling, load balancing, read/write split, sharding, health checks and metrics. On Railway you pay only for the compute and volumes the four services use; deleting `pgweb` or the replica makes it smaller.

## FAQ

**What is PgDog?**
PgDog is an open-source PostgreSQL proxy written in Rust that provides connection pooling, layer-7 load balancing, automatic read/write splitting and database sharding behind a single endpoint.

**What does this Railway template deploy?**
Four services: `pgdog`, a PostgreSQL 18 primary, a streaming read replica, and `pgweb` as a browser SQL console. The pooler is the only publicly reachable endpoint.

**Why does the template include a second PostgreSQL service?**
The replica is what makes the read/write split useful. It clones itself from the primary and streams changes continuously, giving PgDog somewhere to send reads and you a warm standby.

**How do I connect my application to self-hosted PgDog?**
Use `DATABASE_URL` on the `pgdog` service for apps in the same project, or `DATABASE_PUBLIC_URL` with `sslmode=require` from outside. Any PostgreSQL driver works unchanged.

**Can I run PgDog with just one database and no replica?**
Yes. Clear `PGDOG_REPLICA_HOSTS` and delete the replica service; PgDog then behaves as a plain transaction pooler.

**How do I add read replicas or shards later?**
Add another PostgreSQL service and append its private hostname to `PGDOG_REPLICA_HOSTS`. Sharding uses the same `[[databases]]` list with a `shard` number per host plus a sharded-table definition.

**Does transaction pooling break prepared statements or `SET` in PgDog?**
No. PgDog tracks prepared statements per server connection and replays session parameters — the main behavioural difference from PgBouncer's transaction mode.


## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/pgdog-pooler
