Getting Your Hands Dirty With Postgres Source
Postgres documentation is written almost entirely for users, not developers. The repository's standard files give little guidance on working with the source, and references to INSTALL in make tasks point to a file that doesn't exist on master—it's only generated for releases. The official docs contain everything you need, but the verbosity can hide the practical steps. Here's a more direct path to a working development setup.
Choosing Your Prefix and Port
If you want a stable Postgres release for everyday work alongside an experimental build, pick non-standard directories and a non-default port. A prefix passed to configure sets the install target. A port can be overridden per command or for a whole session with the PGPORT environment variable. Tools like direnv can manage these variables from an .envrc in the source directory:
export PG_BUILD_DIR="$PWD/build"
export PG_DATA_DIR="$PWD/data/primary"
export PGPORT=5433
Run direnv allow after saving the file.
Building
Clone the repository:
git clone https://github.com/postgres/postgres.git
Run configure with a prefix pointing at your build directory, adding flags to enable debugging with tools like gdb:
./configure --enable-cassert --enable-debug --prefix $PG_BUILD_DIR CFLAGS="-ggdb -Og -g3 -fno-omit-frame-pointer"
Build with parallelism and quiet output:
make -j16 -s
-j: Parallel build. Choose a number based on your cores (e.g., 16 for an 8-core hyper-threaded machine).-s: Quiet build so compiler warnings aren't buried in output.
Install to the prefix:
make install -j16 -s
Running and Testing
Initialize a data directory and start the server in your terminal for visible logging and easy restart with Ctrl+C:
mkdir -p $PG_DATA_DIR
# initialize a data directory
$PG_BUILD_DIR/bin/initdb -D $PG_DATA_DIR
# start the server
$PG_BUILD_DIR/bin/postgres -D $PG_DATA_DIR -p $PGPORT
Create a database and connect:
$PG_BUILD_DIR/bin/createdb -p $PGPORT brandur-test
$PG_BUILD_DIR/bin/psql -p $PGPORT brandur-test
Postgres relies on a regression suite rather than unit tests. The standard run starts and tears down a temporary server:
make check
A faster check uses an already-running server:
# requires $PGPORT to be set in the environment
make installcheck
Prefer the parallel variant for speed:
# requires $PGPORT to be set in the environment
make installcheck-parallel
Speeding Up Iteration
Building and testing Postgres is already quick (a fresh make takes roughly 30 seconds, the test suite around 15), but heavier development benefits from two optimizations.
ccache
ccache caches compiler output so rebuilds with identical inputs return instantly. Configure it as your C compiler:
./configure --enable-cassert --enable-debug --prefix $PG_BUILD_DIR --with-CC="ccache gcc" CFLAGS="-ggdb -Og -g3 -fno-omit-frame-pointer"
After one build to warm the cache, a make clean -j16 -s followed by a rebuild drops runtime from 30 seconds to under 5. Incremental builds are faster still.
The Gold Linker
On Linux, the gold linker outperforms the GNU linker. ELF-only, it isn't available on Mac OS. Export it in $CFLAGS before running configure:
export CFLAGS="-fuse-ld=gold"
./configure ...
Formatting With pgindent
Postgres uses an unusually compact code style that survives from when bytes mattered. pgindent, similar to Go's gofmt, ships with the source to enforce this style. Reviewers may request it if your patch isn't compliant, so run it on changed files before submission.
Install the dependencies listed in src/tools/pgindent/README (note: perltidy has a Homebrew formula), then run it from the source root:
src/tools/pgindent/pgindent src/backend/utils/adt/mac.c
Since pgindent is brittle Perl with no test coverage, commit your changes before running it.
Preparing Patches for the Mailing List
Patches go to the PG Hackers mailing list as email attachments. Context format (from diff -c) was once required; unified diffs are now acceptable. git format-patch produces clean patch files with full commit messages included:
$ git format-patch master...
0001-Implement-SortSupport-for-macaddr-data-type.patch
Good commit hygiene matters regardless of tool—squash and fix commits with git rebase -i before generating files.
Running a Local Replica
Test replica behavior by starting a second instance on a different port with tweaked configuration. A script demonstrates the setup:
#!/bin/sh
set -e
export PG_DIR="$PWD"
export PRIMARY_PORT=5433
export REPLICA_PORT=5434
read -p "Will delete $PG_DIR/data/{primary,replica}. Okay? [Ctrl+C cancels]" yn
rm -rf $PG_DIR/data/primary
rm -rf $PG_DIR/data/replica
# Initialize a new data directory for the primary, then use a bit of a shortcut
# by just copying it for use by the replica.
$PG_DIR/bin/initdb -D $PG_DIR/data/primary/
cp -r $PG_DIR/data/primary/ $PG_DIR/data/replica/
cat <<EOT >> $PG_DIR/data/primary/postgresql.conf
port=$PRIMARY_PORT
EOT
cat <<EOT >> $PG_DIR/data/replica/postgresql.conf
port=$REPLICA_PORT
shared_buffers=500MB
hot_standby=on
hot_standby_feedback=on
EOT
cat <<EOT >> $PG_DIR/data/replica/recovery.conf
standby_mode=on
primary_conninfo='host=127.0.0.1 port=$PRIMARY_PORT user=$USER'
EOT
cat <<EOT >> /dev/stdout
READY!
======
Start primary:
$PG_DIR/bin/postgres -D $PG_DIR/data/primary
Start replica:
$PG_DIR/bin/postgres -D $PG_DIR/data/replica
Create a database:
$PG_DIR/bin/createdb -p $PRIMARY_PORT mydb
Connect to primary:
$PG_DIR/bin/psql -p $PRIMARY_PORT mydb
Connect to replica:
$PG_DIR/bin/psql -p $REPLICA_PORT mydb
EOT



