From e9105748f2fbc04264359b1b34c43933705986e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Mon, 28 Sep 2026 13:38:06 +0000 Subject: [PATCH 1/9] AI: add agents instructions Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012DSzm4XsY7riy11DAVjsaB --- AGENTS.md | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 1 + 2 files changed, 62 insertions(+) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..561a68b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,61 @@ +# Dockette / PgBouncer + +Instructions for AI coding agents working in this repository. + +## Overview + +`dockette/pgbouncer` republishes `dhi.io/pgbouncer` under the Dockette name on Docker Hub. It is a thin +republish (see IMAGES.md): the `Dockerfile` only sets `ARG`, `FROM` and labels. It adds no config, no +entrypoint and no environment variables, so runtime behaviour must match the upstream tag. + +- **Image**: `dockette/pgbouncer`, tags `1.26.0` and `latest` (same image) +- **Base**: `dhi.io/pgbouncer:${PGBOUNCER_VERSION}`, default `1.26.0` +- **Platforms**: `linux/amd64` + +## Documentation + +- `README.md` is also the Docker Hub description; the `docs` job publishes it from `master`. +- Organization rules are in [dockette/dockette specs](https://github.com/dockette/dockette/tree/master/specs). + +## Commands + +```bash +# Build the image for the default tag (1.26.0); DOCKER_TAG= builds another version +make build + +# Smoke test (pgbouncer --version) +make test + +# Run on port 6432 with a local config, optionally with a userlist +make run PGBOUNCER_CONFIG=$(pwd)/pgbouncer.ini +make run PGBOUNCER_CONFIG=$(pwd)/pgbouncer.ini PGBOUNCER_USERLIST=$(pwd)/userlist.txt +``` + +CI does not call `make`. The `test` job builds with `docker/build-push-action` (tag `-test`, `load: true`) and +runs `pgbouncer --version`; the `build` job pushes `1.26.0` and `latest` from `master` only. + +## Conventions + +- The image tag is the upstream version. `DOCKER_TAG` is passed to the build as `PGBOUNCER_VERSION`. +- Labels follow IMAGES.md; `org.opencontainers.image.version` comes from `PGBOUNCER_VERSION`. +- The `Dockerfile` holds only `ARG`, `FROM` and `LABEL`. Anything more makes it a build image, not a + republish. + +## Traps + +- **A version bump touches four places:** `DOCKER_TAG` in the `Makefile`, `PGBOUNCER_VERSION` in the workflow + `env`, the `Dockerfile` default `ARG` and the README Versions table. Missing one publishes a tag that the + README doesn't list. +- **The Makefile has no `help` and no `push` target.** Plain `make` runs `build`, because it is the first + target. Pushing happens only in CI. +- **CI smoke test and `make test` are separate copies.** The workflow runs its own `docker run ... pgbouncer + --version`; a change to `make test` does not reach CI, so update both. +- **`PGBOUNCER_CONFIG` must be an absolute path.** Its default `pgbouncer.ini` is relative, so Docker reads it + as a named volume and mounts an empty directory instead of the file. +- **The weekly rebuild pulls the same pinned upstream tag.** A new upstream release is not picked up on its + own; bump the version. +- **Old version tags stay on Docker Hub.** Don't delete them after a bump; users pin them (IMAGES.md, Tag + Naming). +- **`linux/arm64` is not built.** Both workflow jobs set `platforms: linux/amd64`. Check that upstream + publishes `arm64` before adding it there and to `DOCKER_PLATFORMS`. +- Usage for image users (config file, port, userlist) lives in `README.md`, not here. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md From 8cabb2dff80334738b0e4bc75c5221f7019c6dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Mon, 28 Sep 2026 13:38:06 +0000 Subject: [PATCH 2/9] License: add MIT license Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012DSzm4XsY7riy11DAVjsaB --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..273776d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Dockette + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 67eb1edc3da3c8af9055b43f1a9fcb556d863b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Mon, 28 Sep 2026 13:38:06 +0000 Subject: [PATCH 3/9] Editorconfig: init Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012DSzm4XsY7riy11DAVjsaB --- .editorconfig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..decaff9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# EditorConfig is awesome: http://EditorConfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 + +[Makefile] +indent_style = tab + +[*.{json,yml,yaml}] +indent_size = 2 From b42dc97de20436bbfbc6f656c5b047512e132728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Mon, 28 Sep 2026 13:38:06 +0000 Subject: [PATCH 4/9] Readme: describe image, usage and versions Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012DSzm4XsY7riy11DAVjsaB --- README.md | 60 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 1e9f15d..6567828 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,37 @@

Dockette / PgBouncer

- Minimal Docker image for PgBouncer, a connection pooler for PostgreSQL. This tag mirrors dhi.io/pgbouncer under the dockette/pgbouncer name on Docker Hub. + GitHub Actions + Docker Hub pulls + GitHub Sponsors + Support/Discussions

-🕹 f3l1x.io | 💻 f3l1x | 🐦 @xf3l1x + PgBouncer, the connection pooler for PostgreSQL, in a Docker image. It is a thin republish of dhi.io/pgbouncer under the dockette/pgbouncer name, for anyone who runs a connection pool in front of PostgreSQL.

- GitHub Actions - Docker Hub pulls - GitHub Sponsors - Support/Discussions +🕹 f3l1x.io | 💻 f3l1x | 🐦 @xf3l1x

----- ## Usage -Mount your `pgbouncer.ini` (and optional userlist) and expose the pool port (default **6432**): +Run PgBouncer on port `6432` with your own `pgbouncer.ini` from the current folder: ```sh docker run --name some-pgbouncer -p 6432:6432 \ - -v /path/to/pgbouncer.ini:/etc/pgbouncer/pgbouncer.ini:ro \ + -v "$(pwd)/pgbouncer.ini:/etc/pgbouncer/pgbouncer.ini:ro" \ dockette/pgbouncer:1.26.0 ``` -Minimal `pgbouncer.ini` sketch (adjust `host`, auth, and pool names for your setup): +Based on `dhi.io/pgbouncer:1.26.0`, with no changes on top: the image adds no entrypoint, no default config and no +environment variables. You configure it with a mounted `pgbouncer.ini` and, when it uses `auth_file`, a mounted +`userlist.txt`. `linux/amd64` only. + +A minimal `pgbouncer.ini` looks like this. Adjust `host`, the auth settings and the pool sizes for your database: ```ini [databases] @@ -43,15 +47,41 @@ max_client_conn = 100 default_pool_size = 20 ``` +The config above reads users from `auth_file`. Mount your userlist next to the config: + +```sh +docker run --name some-pgbouncer -p 6432:6432 \ + -v "$(pwd)/pgbouncer.ini:/etc/pgbouncer/pgbouncer.ini:ro" \ + -v "$(pwd)/userlist.txt:/etc/pgbouncer/userlist.txt:ro" \ + dockette/pgbouncer:1.26.0 +``` + +See the [PgBouncer configuration reference](https://www.pgbouncer.org/config.html) for all settings. + ## Versions -| Image tag | Equivalent upstream | Docker Hub | -|-----------|---------------------|------------| -| `dockette/pgbouncer:1.26.0` | `dhi.io/pgbouncer:1.26.0` | [tags](https://hub.docker.com/r/dockette/pgbouncer/tags/) | -| `dockette/pgbouncer:latest` | same as `1.26.0` (rolling) | [tags](https://hub.docker.com/r/dockette/pgbouncer/tags/) | +| Tag | Upstream | +|-----|----------| +| `dockette/pgbouncer:1.26.0` | `dhi.io/pgbouncer:1.26.0` | +| `dockette/pgbouncer:latest` | Same as `1.26.0` | + +Older version tags stay on [Docker Hub](https://hub.docker.com/r/dockette/pgbouncer/tags) unchanged, so you can pin +them. + +## Development + +Build the image, run the smoke test (`pgbouncer --version`) and start it on port `6432`: + +```sh +make build +make test +make run PGBOUNCER_CONFIG=$(pwd)/pgbouncer.ini +``` -The image is a thin republish: builds use `FROM dhi.io/pgbouncer:${PGBOUNCER_VERSION}` so runtime behavior matches that upstream tag. +`DOCKER_TAG` selects another upstream version for `make build`, `make test` and `make run`. `make run` also accepts +`PGBOUNCER_USERLIST=$(pwd)/userlist.txt`. Both paths must be absolute, or Docker mounts an empty named volume +instead of the file. ## Maintenance -See [how to contribute](https://github.com/dockette/.github/blob/master/CONTRIBUTING.md) to this package. Consider to [support](https://github.com/sponsors/f3l1x) **f3l1x**. Thank you for using this package. +See [how to contribute](https://github.com/dockette/.github/blob/master/CONTRIBUTING.md) to this package. Consider [supporting](https://github.com/sponsors/f3l1x) **f3l1x**. Thank you for using this package. From 387e03530c7963c1a25e591bea2ccd28f24497df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Tue, 29 Sep 2026 17:26:08 +0000 Subject: [PATCH 5/9] AI: simplify agents instructions to development essentials Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012DSzm4XsY7riy11DAVjsaB --- AGENTS.md | 68 ++++++++++++++----------------------------------------- 1 file changed, 17 insertions(+), 51 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 561a68b..088b6b6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,61 +1,27 @@ # Dockette / PgBouncer -Instructions for AI coding agents working in this repository. +PgBouncer connection pooler for PostgreSQL, republished from the upstream hardened image. -## Overview +## Stack -`dockette/pgbouncer` republishes `dhi.io/pgbouncer` under the Dockette name on Docker Hub. It is a thin -republish (see IMAGES.md): the `Dockerfile` only sets `ARG`, `FROM` and labels. It adds no config, no -entrypoint and no environment variables, so runtime behaviour must match the upstream tag. +- Docker image built with `docker buildx`, base `dhi.io/pgbouncer` +- PgBouncer 1.26.0 +- Published to Docker Hub as `dockette/pgbouncer` for linux/amd64 by GitHub Actions -- **Image**: `dockette/pgbouncer`, tags `1.26.0` and `latest` (same image) -- **Base**: `dhi.io/pgbouncer:${PGBOUNCER_VERSION}`, default `1.26.0` -- **Platforms**: `linux/amd64` - -## Documentation - -- `README.md` is also the Docker Hub description; the `docs` job publishes it from `master`. -- Organization rules are in [dockette/dockette specs](https://github.com/dockette/dockette/tree/master/specs). - -## Commands +## Development ```bash -# Build the image for the default tag (1.26.0); DOCKER_TAG= builds another version -make build +make build # build the image +make test # smoke test the image +make run # run it locally on port 6432 (PGBOUNCER_CONFIG=pgbouncer.ini) +``` -# Smoke test (pgbouncer --version) -make test +`make build DOCKER_TAG=1.26.0` builds one PgBouncer version. -# Run on port 6432 with a local config, optionally with a userlist -make run PGBOUNCER_CONFIG=$(pwd)/pgbouncer.ini -make run PGBOUNCER_CONFIG=$(pwd)/pgbouncer.ini PGBOUNCER_USERLIST=$(pwd)/userlist.txt -``` +## Principles -CI does not call `make`. The `test` job builds with `docker/build-push-action` (tag `-test`, `load: true`) and -runs `pgbouncer --version`; the `build` job pushes `1.26.0` and `latest` from `master` only. - -## Conventions - -- The image tag is the upstream version. `DOCKER_TAG` is passed to the build as `PGBOUNCER_VERSION`. -- Labels follow IMAGES.md; `org.opencontainers.image.version` comes from `PGBOUNCER_VERSION`. -- The `Dockerfile` holds only `ARG`, `FROM` and `LABEL`. Anything more makes it a build image, not a - republish. - -## Traps - -- **A version bump touches four places:** `DOCKER_TAG` in the `Makefile`, `PGBOUNCER_VERSION` in the workflow - `env`, the `Dockerfile` default `ARG` and the README Versions table. Missing one publishes a tag that the - README doesn't list. -- **The Makefile has no `help` and no `push` target.** Plain `make` runs `build`, because it is the first - target. Pushing happens only in CI. -- **CI smoke test and `make test` are separate copies.** The workflow runs its own `docker run ... pgbouncer - --version`; a change to `make test` does not reach CI, so update both. -- **`PGBOUNCER_CONFIG` must be an absolute path.** Its default `pgbouncer.ini` is relative, so Docker reads it - as a named volume and mounts an empty directory instead of the file. -- **The weekly rebuild pulls the same pinned upstream tag.** A new upstream release is not picked up on its - own; bump the version. -- **Old version tags stay on Docker Hub.** Don't delete them after a bump; users pin them (IMAGES.md, Tag - Naming). -- **`linux/arm64` is not built.** Both workflow jobs set `platforms: linux/amd64`. Check that upstream - publishes `arm64` before adding it there and to `DOCKER_PLATFORMS`. -- Usage for image users (config file, port, userlist) lives in `README.md`, not here. +- KISS: one image does one job; no extra services or tools. +- DRY: shared steps live in the base image, not copied into every Dockerfile. +- YAGNI: add a package only when the image needs it. +- Pin versions, keep layers small, clean package caches in the same `RUN`. +- Every change is built and smoke tested with `make build test` before a commit. From bcaef727505ca70e9647459dc3ab7cf0336170fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Tue, 29 Sep 2026 20:20:25 +0000 Subject: [PATCH 6/9] Readme: keep development section high level Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012DSzm4XsY7riy11DAVjsaB --- README.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6567828..20da03c 100644 --- a/README.md +++ b/README.md @@ -70,17 +70,13 @@ them. ## Development -Build the image, run the smoke test (`pgbouncer --version`) and start it on port `6432`: - ```sh -make build -make test -make run PGBOUNCER_CONFIG=$(pwd)/pgbouncer.ini +make build # build the image +make test # smoke test it +make run # run it locally ``` -`DOCKER_TAG` selects another upstream version for `make build`, `make test` and `make run`. `make run` also accepts -`PGBOUNCER_USERLIST=$(pwd)/userlist.txt`. Both paths must be absolute, or Docker mounts an empty named volume -instead of the file. +Run `make` to list every target. ## Maintenance From 04e670d3263a5101b2982ea8da4a4e76c62dd17a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Tue, 29 Sep 2026 20:20:48 +0000 Subject: [PATCH 7/9] Makefile: add help target and target descriptions Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012DSzm4XsY7riy11DAVjsaB --- Makefile | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 7afbe95..3c5b056 100644 --- a/Makefile +++ b/Makefile @@ -5,17 +5,27 @@ PGBOUNCER_CONFIG?=pgbouncer.ini PGBOUNCER_USERLIST?= PGBOUNCER_USERLIST_VOLUME=$(if $(PGBOUNCER_USERLIST),-v "${PGBOUNCER_USERLIST}:/etc/pgbouncer/userlist.txt:ro") +.DEFAULT_GOAL := help + +##@ Help + +.PHONY: help +help: ## Show this help + @awk 'BEGIN {FS = ":.*##"; printf "Usage: make \033[36m\033[0m\n"} /^[a-zA-Z0-9_.-]+:.*##/ { sub(/^ +/, "", $$2); printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(firstword $(MAKEFILE_LIST)) + +##@ Docker + .PHONY: build -build: +build: ## Build the image docker buildx build --platform ${DOCKER_PLATFORMS} \ --build-arg PGBOUNCER_VERSION=${DOCKER_TAG} \ -t ${DOCKER_IMAGE}:${DOCKER_TAG} \ . .PHONY: test -test: +test: ## Smoke test the image docker run --rm --platform ${DOCKER_PLATFORMS} ${DOCKER_IMAGE}:${DOCKER_TAG} pgbouncer --version .PHONY: run -run: +run: ## Run the image locally (PGBOUNCER_CONFIG=/abs/path/pgbouncer.ini) docker run --rm -it --platform ${DOCKER_PLATFORMS} -p 6432:6432 -v "${PGBOUNCER_CONFIG}:/etc/pgbouncer/pgbouncer.ini:ro" ${PGBOUNCER_USERLIST_VOLUME} ${DOCKER_IMAGE}:${DOCKER_TAG} From 5bbb8636c2fd89f314fda8ef82cb4ef9d76fb0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Tue, 29 Sep 2026 20:32:29 +0000 Subject: [PATCH 8/9] AI: drop CLAUDE.md, agents read AGENTS.md directly Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012DSzm4XsY7riy11DAVjsaB --- CLAUDE.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 43c994c..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1 +0,0 @@ -@AGENTS.md From 57cb87484f81d37f868c4b7bd453ee2509ef4bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Tue, 29 Sep 2026 20:33:09 +0000 Subject: [PATCH 9/9] Readme: simplify usage Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012DSzm4XsY7riy11DAVjsaB --- README.md | 39 +++++---------------------------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 20da03c..811fc12 100644 --- a/README.md +++ b/README.md @@ -19,44 +19,15 @@ ## Usage -Run PgBouncer on port `6432` with your own `pgbouncer.ini` from the current folder: +Mount your own `pgbouncer.ini` and run it: ```sh -docker run --name some-pgbouncer -p 6432:6432 \ - -v "$(pwd)/pgbouncer.ini:/etc/pgbouncer/pgbouncer.ini:ro" \ - dockette/pgbouncer:1.26.0 +docker run -v ./pgbouncer.ini:/etc/pgbouncer/pgbouncer.ini:ro dockette/pgbouncer ``` -Based on `dhi.io/pgbouncer:1.26.0`, with no changes on top: the image adds no entrypoint, no default config and no -environment variables. You configure it with a mounted `pgbouncer.ini` and, when it uses `auth_file`, a mounted -`userlist.txt`. `linux/amd64` only. - -A minimal `pgbouncer.ini` looks like this. Adjust `host`, the auth settings and the pool sizes for your database: - -```ini -[databases] -* = host=postgres port=5432 - -[pgbouncer] -listen_addr = 0.0.0.0 -listen_port = 6432 -auth_type = md5 -auth_file = /etc/pgbouncer/userlist.txt -pool_mode = transaction -max_client_conn = 100 -default_pool_size = 20 -``` - -The config above reads users from `auth_file`. Mount your userlist next to the config: - -```sh -docker run --name some-pgbouncer -p 6432:6432 \ - -v "$(pwd)/pgbouncer.ini:/etc/pgbouncer/pgbouncer.ini:ro" \ - -v "$(pwd)/userlist.txt:/etc/pgbouncer/userlist.txt:ro" \ - dockette/pgbouncer:1.26.0 -``` - -See the [PgBouncer configuration reference](https://www.pgbouncer.org/config.html) for all settings. +The image is upstream PgBouncer with nothing added: no default config, no environment variables. Everything is +set in `pgbouncer.ini` (plus `userlist.txt` if you use `auth_file`), see the +[configuration reference](https://www.pgbouncer.org/config.html). ## Versions