A script for automating local PostgreSQL development environment setup — cloning, building, initializing, and starting one or more PostgreSQL instances from source using Git worktrees.
- Clones the PostgreSQL source repository and manages Git worktrees
- Builds PostgreSQL from source using Meson (default) or Make
- Optionally applies patches via
git am - Initializes and starts a primary PostgreSQL cluster, with optional FDW and replica instances
- Generates a shell activation script per instance with environment variables and helper functions
- Python 3.7+
- Git
meson+ninja(ormake/autoconftoolchain)- Standard PostgreSQL build dependencies (e.g.
libreadline-dev,zlib1g-dev, etc.) fuser(used as a fallback to kill processes on a port)
python pg_build.py [OPTIONS]| Option | Default | Description |
|---|---|---|
--prefix PATH |
~/pgdev/installations |
Root directory for all build artifacts, data, and scripts |
--repo-url URL |
PostgreSQL GitHub mirror | Git URL to clone from |
--branch NAME |
— | Branch to check out (mutually exclusive with --tag and --commit) |
--tag NAME |
— | Tag to check out (mutually exclusive with --branch and --commit) |
--commit HASH |
— | Commit hash to check out (mutually exclusive with --branch and --tag) |
--patch FILES |
— | Patch file(s) or glob pattern to apply via git am --3way |
--meson-flags FLAGS |
— | Extra flags passed to meson setup |
--build-system |
meson |
Build system to use: meson or make |
--worktree-name NAME |
— | Optional prefix for naming worktree directories |
--create-fdw |
off | Also build and start an FDW instance (port + 10) |
--create-replica |
off | Also build and start a replica instance (port + 20) |
--skip-build |
off | Skip the build step (re-init DB only) |
--worktree-only |
off | Only create worktree, skip build and DB initialization |
--force-worktree |
off | Force recreation of worktree even if it exists |
--capture-output |
off | Suppress stdout/stderr from build commands |
--port PORT |
5432 |
Port for the primary instance |
-l, --list-worktrees |
— | List existing worktrees and exit |
--clean-worktrees |
— | Delete all worktrees and exit |
--remove-worktree NAME |
— | Remove a single worktree by name (as shown by --list-worktrees) and exit |
--update-source |
— | Fetch latest changes from all remotes in source directory and exit |
--recreate-activate-script |
off | Only recreate the activation script (cannot be used with other options) |
--continue |
off | Continue a previously failed git am and proceed with the build |
Build from the master branch:
python pg_build.py --branch masterBuild a specific release tag with a custom prefix:
python pg_build.py --tag REL_16_0 --prefix ~/pg/16Build from a specific commit hash:
python pg_build.py --commit abc123def456Build with Meson flags and apply a patch:
python pg_build.py --branch master \
--meson-flags "-Dcassert=true -Dtap_tests=enabled" \
--patch ~/patches/my-feature.patchApply multiple patches (shell glob expansion):
python pg_build.py --branch master --patch ~/Downloads/*.patchIf a patch conflict occurs during --patch, resolve it manually in the worktree, then resume:
# 1. Fix conflicts in the worktree, then:
# git add <resolved files>
# 2. Continue the build:
python pg_build.py --continueBuild primary + FDW + replica instances:
python pg_build.py --branch master --create-fdw --create-replicaRe-initialize the database without rebuilding:
python pg_build.py --branch master --skip-buildList all existing worktrees:
python pg_build.py -l
# or
python pg_build.py --list-worktreesDelete all worktrees:
python pg_build.py --clean-worktreesRemove a single worktree (and its pghome, pgdata, and activation scripts):
python pg_build.py --remove-worktree src_primaryCreate worktree only (no build or DB init):
python pg_build.py --branch master --worktree-onlyUpdate source repository (fetch latest from all remotes):
python pg_build.py --update-sourceForce recreation of worktree (useful when switching branches or after manual changes):
python pg_build.py --branch master --skip-build --force-worktreeRecreate activation script only (useful after changing ports or paths):
python pg_build.py --prefix ~/pgdev/installations --recreate-activate-script --port 5432
# For a named worktree instance
python pg_build.py --prefix ~/pgdev/installations --recreate-activate-script \
--worktree-name pghome_v18 --port 5433After running, the --prefix directory will contain:
<prefix>/
├── source/ # Cloned repository
├── worktrees/
│ └── src_primary/ # Git worktree for the primary build
├── pghome/ # PostgreSQL installations
│ └── primary/ # Installed PostgreSQL binaries
├── pgdata/
│ └── primary/ # Initialized data directory
└── activate_primary.sh # Shell activation script
With --create-fdw or --create-replica, additional pghome/fdw/, pghome/replica/, pgdata/fdw/, pgdata/replica/, and corresponding activation scripts are created.
With --worktree-name, the structure uses the provided name:
<prefix>/
├── worktrees/
│ └── multixact_primary/ # Named worktree
├── pghome/
│ └── multixact_primary/ # Named installation
├── pgdata/
│ └── multixact_primary/ # Named data directory
└── activate_primary_multixact.sh
Each instance gets a generated activation script (e.g. activate_primary.sh) that sets up your shell environment:
source ~/pgdev/installations/activate_primary.shThis exports PGHOME, PGDATA, PGPORT, PATH, LD_LIBRARY_PATH, and several convenience aliases and functions:
| Alias / Function | Description |
|---|---|
PG_START |
Start the cluster |
PG_STOP |
Stop the cluster |
pg_check_extension <name> |
Run setup + extension test suite |
pg_check_world |
Run all tests |
pg_build_docs |
Build documentation via ninja docs |
pg_list_tests |
List all available Meson test targets |
pg_run_suite <name> |
Run setup suite then a named test suite |
| Instance | Port |
|---|---|
| Primary | --port (default 5432) |
| FDW | --port + 10 (default 5442) |
| Replica | --port + 20 (default 5452) |
A helper script to download patch files from the PostgreSQL Commitfest by entry ID (or full URL) and filename prefix.
python patch_download.py <cfentry_or_url> <prefix> [download_dir]| Argument | Required | Default | Description |
|---|---|---|---|
cfentry_or_url |
yes | — | Commitfest patch entry ID or full commitfest URL |
prefix |
yes | — | Filename prefix to match (only links whose filename starts with this are downloaded) |
download_dir |
no | ~/Downloads |
Directory to save downloaded patches |
Download patches from commitfest entry 5338 matching prefix v3-:
python patch_download.py 5338 v3-Download using a full commitfest URL:
python patch_download.py https://commitfest.postgresql.org/patch/5338 v3-Download to a custom directory:
python patch_download.py 5338 v3- ~/patchesCombine with pg_build.py to download and apply in one go:
python patch_download.py 5338 v3- ~/patches
python pg_build.py --branch master --patch ~/patches/v3-*.patch- Each run destroys and recreates the build directory and data directory for the affected instances. It is not intended for production use.
- Worktrees are preserved by default for efficiency. Use
--force-worktreeto recreate them (useful when switching branches or after manual changes). - The script stops any existing PostgreSQL process on the target port before reinitializing.
--patchaccepts multiple files or a glob pattern; patches are applied in sorted order viagit am --3way. If a conflict occurs, resolve it in the worktree and run--continueto finish applying remaining patches and proceed with the build.- Both
--branchand--tagare mapped toorigin/<ref>when creating the worktree.