paagan is a lightweight CLI tool to manage multiple PostgreSQL versions and instances locally using Docker.
The primary motivation for this project is to have a developer experience similar to CloudNativePG (CNPG) but running locally on a development machine. It simplifies tasks like running specific PostgreSQL versions, managing multiple isolated databases, and performing Point-In-Time Recovery (PITR) through forking.
- Multi-Version Support: Run any PostgreSQL version (including 15, 16, 17, and the new 18+ structure).
- Isolation: Each instance gets its own data, WAL archive, and backup directories.
- Dynamic Port Management: Automatically assigns and remembers unused ports for each instance.
- Local PITR: Fork an existing database to a new instance at a specific point in time.
- Cloud-Native Logic: Mimics production-grade database management patterns (WAL archiving, base backups).
- Portable: Automatically handles UID/GID mapping to ensure Docker has correct permissions on your local filesystem.
Ensure you have Rust and Docker installed.
cargo build --release
cp target/release/paagan /usr/local/bin/ # or wherever you want itNote: Ensure your DOCKER_HOST is correctly set if you are using Colima or a non-standard Docker socket.
All configuration and data are stored in ~/.paagan:
instances.json: Metadata for all managed instances.instances/<name>/data: PostgreSQL data directory.instances/<name>/archive: WAL archive directory.instances/<name>/backups: Base backups used for forking.
Lists all database containers managed by paagan along with their versions, ports, and current Docker status.
paagan listCreates a new PostgreSQL instance. It pulls the required image, sets up the directory structure, and starts the container.
paagan create --version 18-alpine my-dbYou can run any PostgreSQL-compatible image (extension bundles like
searchbase, pgvector builds, etc.) by
passing --image and selecting --init-mode cnpg. The cnpg mode runs an
explicit initdb step and invokes postgres -D ... directly, mirroring how
CloudNativePG-style images expect to be started; the default standard mode
continues to use the official postgres entrypoint.
--shared-preload-libraries is passed through as -c shared_preload_libraries=...,
needed for extensions like pg_textsearch that must be loaded at server start.
paagan create \
--image ghcr.io/vagmi/searchbase:latest \
--shared-preload-libraries pg_textsearch \
--init-mode cnpg \
searchdb
# Then create the bundled extensions in your database:
paagan psql searchdb
# postgres=# CREATE EXTENSION vector;
# postgres=# CREATE EXTENSION vectorscale CASCADE;
# postgres=# CREATE EXTENSION pg_textsearch;The image, init mode, and shared_preload_libraries are persisted with the
instance, so start (after a manual container removal) and fork will reuse
the same configuration automatically.
Starts or stops an existing database instance. start will recreate the container if it was manually removed, ensuring your data is always accessible.
paagan stop my-db
paagan start my-dbOpens an interactive psql session to the specified instance.
paagan psql my-dbShows detailed information about an instance, including the connection string and data directory paths.
paagan show my-dbCreates a new instance from an existing one. If --at is provided, it performs a Point-In-Time Recovery (PITR).
# Direct fork
paagan fork source-db forked-db
# Fork to a specific timestamp (PITR)
paagan fork --at "2026-03-10 14:30:00+00" source-db recovery-dbRemoves the database instance, its Docker container, and all associated data on disk.
paagan delete my-dbpaagantriggers apg_basebackupon the source instance.- It ensures all pending WAL logs are archived.
- It extracts the backup into the new instance's data directory.
- It configures a
restore_commandto pull logs from the source's archive. - If a timestamp is provided, it sets
recovery_target_time. - The new instance starts in recovery mode, reaches the target, promotes itself, and becomes a standalone database.