Shaun Campbell 4e305d67e3
All checks were successful
Build and Push Image / docker-build-and-push (push) Successful in 5m3s
Test and Release PawSQL / test (push) Successful in 44s
Test and Release PawSQL / release (push) Successful in 8s
feat: run managed containers from PawSQL image
2026-09-15 20:38:01 -04:00
2026-09-15 19:52:13 -04:00
2026-09-15 19:52:13 -04:00
2026-09-15 19:52:13 -04:00

PawSQL

PawSQL is a TLS-terminating PostgreSQL router. It accepts PostgreSQL clients on one address, chooses a configured route from the TLS Server Name Indication (SNI) or database name, and proxies the PostgreSQL stream to an external or PawSQL-managed PostgreSQL server.

Prerequisites

  • Go 1.24 or later to build and run PawSQL natively.
  • Docker Engine and a usable docker CLI to build the PawSQL image. PawSQL also needs them in its own execution environment when it manages PostgreSQL containers.
  • A TLS certificate and private key readable by PawSQL. The certificate must cover every hostname clients use for SNI routing.
  • Docker Engine access for each postgres route. Managed database images are limited to postgres:16, postgres:17, and postgres:18.

Build, configure, and run

Build a native binary:

go build -o pawsql ./cmd/pawsql

Create a Barkfile and validate it before starting:

./pawsql validate --config Barkfile
./pawsql --config Barkfile

--config defaults to Barkfile. The listener, TLS material, and at least one database route are required.

To build and run the PawSQL container image for routes reachable from that container:

docker build -t pawsql .
docker run --rm --publish 5432:5432 \
  --volume "$PWD/Barkfile:/etc/pawsql/Barkfile:ro" \
  --volume "$PWD/tls:/etc/pawsql/tls:ro" \
  --volume /var/run/docker.sock:/var/run/docker.sock \
  pawsql

The supplied image includes the Docker CLI so managed postgres routes can create, start, and stop their containers through the mounted Docker socket. The socket grants PawSQL root-equivalent control of the Docker host; mount it only for trusted Barkfiles and trusted administrators.

Barkfile

A Barkfile has one pawsql block. Each database has a unique name and exactly one route type: an upstream external PostgreSQL address or a postgres managed database.

pawsql {
    listen :5432

    tls {
        cert /etc/pawsql/tls/fullchain.pem
        key /etc/pawsql/tls/privkey.pem
    }

    database reporting {
        hostname reports.db.example.com
        upstream reporting.internal:5432
    }

    database application {
        hostname app.db.example.com
        postgres {
            image postgres:17
            volume pawsql-application-data
            password_env APPLICATION_POSTGRES_PASSWORD
            idle_timeout 10m
            traffic_idle_timeout 1h
        }
    }
}

listen is PawSQL's TCP address. cert and key identify the client-facing TLS certificate and key. hostname is optional; it is used only for SNI routing. upstream is the address of an existing PostgreSQL server.

For a managed postgres route, image, volume, and password_env are required. On first use, PawSQL reads the named environment variable to create the database container and configures the database and PostgreSQL user with the route's database name. The named Docker volume preserves its data. Set the password environment variable in PawSQL's environment, not in the Barkfile.

See examples/Barkfile and its accompanying examples/docker-compose.yml for a two-route external PostgreSQL example with locally generated development certificates:

cd examples
docker compose up --build

Routing and TLS

PawSQL requires PostgreSQL's SSL negotiation and terminates client TLS before proxying PostgreSQL bytes to the selected upstream.

  • With SNI: PawSQL uses the TLS server name to select an exact configured hostname match. Hostname matching is case-insensitive and ignores a trailing dot. An unknown SNI name is rejected; PawSQL does not fall back to a database-name route when SNI is present.
  • Without SNI: After TLS is established, PawSQL reads the PostgreSQL startup message and selects the route whose database name exactly matches the requested PostgreSQL database. This makes a route without hostname usable by non-SNI clients.

Use a certificate trusted by clients and containing the SNI hostname they present. Clients that do not send SNI must request the configured database route name.

Managed PostgreSQL lifecycle

Managed PostgreSQL is lazy: PawSQL creates or starts its pawsql-<database> container only when a client selects that route, waits for PostgreSQL to accept connections, then proxies the session. PawSQL stops managed containers but does not remove their data volumes.

Two optional Go-duration controls govern stopping a managed container; 0 disables either control:

  • idle_timeout starts only after the last proxied client session closes. When that countdown expires, PawSQL stops the managed container.
  • traffic_idle_timeout starts for an open session and resets whenever PawSQL proxies bytes in either direction. When it expires, PawSQL stops the managed container even though sessions remain open.

traffic_idle_timeout is intentionally aggressive: it terminates open but silent sessions. Do not enable it for workloads that keep idle connections, transactions, listeners, or connection-pool sessions alive unless that interruption is acceptable.

Description
No description provided
Readme 178 KiB
Languages
Go 85.7%
Svelte 11.1%
TypeScript 2.3%
Dockerfile 0.9%