Deploy PgDog

PostgreSQL proxy that pools connections and splits reads from writes

Deploy PgDog

Just deployed

/var/lib/postgresql/data

Just deployed

Just deployed

/var/lib/postgresql/data

Just deployed

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 SELECTs 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

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 Revenue grouped by order status in the pgweb SQL editor Query result showing the read was served by a replica

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

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

Environment Variables Reference

VariableServicePurpose
PGDOG_CLIENT_PASSWORDpgdogPassword clients present to the pooler
PGDOG_ADMIN_PASSWORDpgdogUnlocks the admin console
PGDOG_DB_NAMEpgdogDatabase name clients connect to
PGDOG_REPLICA_HOSTSpgdogComma-separated replicas; empty means primary only
PGDOG_POOLER_MODEpgdogtransaction or session
PGDOG_DEFAULT_POOL_SIZEpgdogServer connections per node
PGDOG_BAN_REPLICA_LAG_BYTESpgdogLag past which a replica stops reads
POSTGRES_PASSWORDpostgresSuperuser password
POSTGRES_REPLICATION_PASSWORDpostgresPassword for the replicator role
PGWEB_AUTH_USER / PGWEB_AUTH_PASSpgwebBasic 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

Hardware Requirements for Self-Hosting PgDog

ResourceMinimumRecommended
CPU1 vCPU pgdog, 1 per database2–4 vCPU pgdog, 2+ per database
RAM256 MB pgdog, 1 GB per database512 MB+ pgdog, 4 GB+ per database
Storage5 GB volume per databaseYour dataset plus WAL headroom
RuntimeLinux 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.


Template Content

More templates in this category

View Template
Rocky Linux
Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀

codestorm
47
View Template
Foundry Virtual Tabletop
A Self-Hosted & Modern Roleplaying Platform

Lucas
71
View Template
Letta Code Remote
Run a Letta Code agent 24/7. No inbound ports, just deploy.

Letta
51