Repo: github.com/hrodrig/hbactl · Releases: Releases
Lightweight Go CLI to manage PostgreSQL Host-Based Authentication (pg_hba.conf) safely: list, add, remove rules; check syntax; reload config. Auto-discovers the file via the running Postgres instance or use --file.
Documentation: Sequence diagrams (Mermaid) for command flows (general, list, add, remove, check, reload), and terminal demo (recorded with VHS) — see docs/. Scanning before release (govulncheck, Grype): tools/README.md.
- Auto-Discovery: Locates
pg_hba.confvia the running Postgres instance, or use--fileto pass the path. - Safety First: Backup before every edit; validate syntax with
hbactl check(usespg_hba_file_rules). - Reload: Apply changes with
hbactl reload(pg_reload_conf()), no restart. - Single Binary: One executable; no runtime dependencies.
- Formats: Supports both CIDR (e.g.
192.168.1.0/24) and legacy IP+netmask inlistandadd. - Group by user: List with
--group-by userfor visual separators; add with--after-user <name>to insert after that user’s last rule and keep rules grouped.
Homebrew (macOS, Linux):
brew tap hrodrig/hbactl
brew install hbactlFrom Go:
go install github.com/hrodrig/hbactl@latestFrom GitHub Releases:
Pre-built binaries for Linux, macOS (darwin), and Windows (amd64, arm64) are published on Releases. Download the archive for your OS/arch and unpack the hbactl binary.
Build from source:
git clone https://github.com/hrodrig/hbactl.git
cd hbactl
go build -o hbactl .Docker:
Published image (multi-arch from release): ghcr.io/hrodrig/hbactl:latest or ghcr.io/hrodrig/hbactl:v0.2.0. To build from source (same as CI): make docker-build or docker build -t hbactl .
Mount your pg_hba.conf (or the directory that contains it) and pass a connection string so hbactl can discover the file or run reload:
docker run --rm -e DATABASE_URL="postgres://user:pass@host:5432/db?sslmode=disable" -v /path/to/pg_hba.conf:/pg_hba.conf ghcr.io/hrodrig/hbactl:latest list -f /pg_hba.conf
docker run --rm -e DATABASE_URL="postgres://..." -v /path/to/pgdata:/pgdata ghcr.io/hrodrig/hbactl:latest list # auto-discovery from PostgresUse -f /path/to/pg_hba.conf when the file is mounted at a known path; omit it when using auto-discovery and the container can reach the Postgres server.
Global flags (optional):
-c/--conn— PostgreSQL connection string (default:DATABASE_URL).-f/--file— Path topg_hba.conf(forlist,add, andremove; can avoid connection forlist).
Displays a formatted table of your rules with a # column (1-based index in file order; use with remove --index). Use --no-index to omit the # column for copy-paste friendly output. Supports --sort by column: type, database, user, address, method (display only; file order is unchanged). Use --group-by user to print === user: name === separators between users (implies sort by user if --sort is not set).
hbactl list
hbactl list -f /path/to/pg_hba.conf # no connection needed
hbactl list --no-index --sort database # copy-paste friendly, no rule numbers
hbactl list --sort user
hbactl list --group-by user # separators between usersCreates a backup (.bak or .bak.<timestamp>) then appends the rule, or inserts it after the last rule for a given user if --after-user is set (keeps rules grouped by user). Use --dry-run to preview the line (shows “would append” or “would insert after last rule for user …” when using --after-user); no file write or backup. Requires connection or --file when not using --dry-run.
hbactl add --type host --db all --user app --addr 192.168.1.100/32 --method scram-sha-256
hbactl add --type host --db all --user all --addr 10.0.0.0/24 --netmask 255.255.255.0 --method md5 # legacy format
hbactl add -f /path/to/pg_hba.conf --type host --db all --user all --addr 127.0.0.1/32 --method trust
hbactl add --dry-run --type host --db all --user pepe --addr 10.0.0.1/32 --method ident --ident-map my_ident_map # preview only
hbactl add --type host --db all --user pepe --addr 10.0.0.1/32 --method ident --ident-map my_ident_map # ident with user map
hbactl add --type host --db all --user pepe --addr 10.0.0.5/32 --method md5 --after-user pepe # insert after last "pepe" ruleFlags: --type (required), --db, --user, --addr (required for host types), --netmask (optional, legacy), --method (required), --ident-map (optional: for ident method), --after-user (insert after last rule for this user; default appends at end), --dry-run (print line without writing).
Creates a backup then removes one rule by --index or all matching rules by criteria. Use --dry-run to preview. Requires connection or --file.
By index (1-based, same as the # column in hbactl list):
hbactl list # show rules with # column
hbactl remove --index 3 # remove the 3rd rule (in file order)
hbactl remove -f /path/to/pg_hba.conf --index 1
hbactl remove --index 2 --dry-run # preview onlyBy criteria (removes all matching rules in one run):
hbactl remove -f sample-pg_hba.conf --user app_user --dry-run # all rules for user app_user (any database)
hbactl remove -f sample-pg_hba.conf --user app_user --db app_planning --dry-run # only app_user on database app_planning
hbactl remove -f sample-pg_hba.conf --addr 10.0.1.7 --dry-run # all rules from IP 10.0.1.7Flags: --index (1-based rule number; use alone), --user (remove all rules for this user), --db (with --user, limit to this database), --addr (remove all rules matching this address, e.g. 10.0.1.7 or 10.0.1.7/32), --dry-run (print rule(s) that would be removed without writing or backup). Use either --index or one of --user / --addr per run, not both.
Uses pg_hba_file_rules to report syntax errors. Requires a connection.
hbactl checkRuns pg_reload_conf() so the server picks up changes without restart. Requires a connection.
hbactl reloadBy default, hbactl connects to PostgreSQL using the DATABASE_URL environment variable or the --conn / -c flag. Ensure your user has permission to read the HBA file and run pg_reload_conf().
Use --file / -f to pass the path to pg_hba.conf explicitly. With --file, list can run without a connection (it only reads the file). Handy for:
- Managing several servers: pass a different path (or
--conn+ path) per run. - Inspecting a local copy of the file (e.g. from another host).
# List rules from a file (no DB connection)
hbactl list -f /opt/homebrew/var/postgresql@16/pg_hba.conf
# Per-server: connect to server and optionally override path
hbactl list --conn "postgres://user@server1:5432/postgres" -f /var/lib/pgsql/16/data/pg_hba.conf- Port: Something should be listening on
5432(e.g.lsof -i :5432,ss -tlnp | grep 5432, ornc -z localhost 5432). - Client: If you have
psqlorpg_isreadyinstalled, runpg_isready -h localhost -p 5432. - macOS (Homebrew):
brew services list | grep postgres. - Linux (systemd):
systemctl status postgresqlorsystemctl status postgresql@16(depending on distro and version).
If nothing is listening on 5432 and no Postgres service is listed, you don’t have a running server.
Linux (Debian/Ubuntu):
sudo apt update && sudo apt install -y postgresql postgresql-client
sudo systemctl start postgresql
sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'postgres';"
export DATABASE_URL="postgres://postgres:postgres@localhost:5432/postgres"
hbactl listLinux (Fedora/RHEL):
sudo dnf install -y postgresql-server postgresql
sudo postgresql-setup --initdb
sudo systemctl start postgresql
export DATABASE_URL="postgres://postgres@localhost:5432/postgres" # peer auth by default
hbactl listHomebrew (macOS):
brew install postgresql@16
brew services start postgresql@16
export DATABASE_URL="postgres://$(whoami)@localhost:5432/postgres"
hbactl listDocker:
docker run -d --name pg -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres:16
export DATABASE_URL="postgres://postgres:postgres@localhost:5432/postgres"
hbactl listUnit tests (parser and table output) do not require a running PostgreSQL server: run go test ./....
Releases and tags are made only from the main branch. Work on develop (or feature branches), merge to main when ready, then from main:
git checkout main
git pull
git tag v0.1.0
make release
git push origin v0.1.0make release will fail if you are not on main. Use make snapshot on any branch to test builds locally.
Homebrew tap (one-time setup): So that brew install hrodrig/hbactl/hbactl works, create an empty repo github.com/hrodrig/homebrew-hbactl. GoReleaser will push the cask there on each release. If you run make release from your machine (not CI), ensure .goreleaser.yaml has the tap repo and run release; you may need to uncomment token and set HOMEBREW_TAP_TOKEN (GitHub PAT with repo scope) if pushing to the tap from CI.
MIT — see LICENSE. Contributing: CONTRIBUTING.md. Code of Conduct: CODE_OF_CONDUCT.md. Changelog: CHANGELOG.md.
