Getting Started with pgmi#
This guide takes you from zero to a working deployment in about 10 minutes. The commands are concrete — the only values you supply are your local PostgreSQL password and the database name.
What you’ll do:
- Install pgmi
- Make sure PostgreSQL is reachable
- Create a project
- Deploy it
- Verify the deployment
What you need:
- PostgreSQL running on
localhost:5432(the default), reachable over a direct connection or a session-mode pooler — transaction-mode poolers (PgBouncer txn mode, RDS Proxy) break pgmi’s session temp tables; see Connection Requirements - A PostgreSQL user with database creation rights (typically
postgres)
No Go toolchain required — the quickstart installs a prebuilt binary. (Installing from source with go install is an optional alternative; see Step 1.)
Step 1: Install pgmi#
Install the prebuilt binary — no Go toolchain needed.
macOS / Linux:
curl -sSL https://raw.githubusercontent.com/vvka-141/pgmi/main/scripts/install.sh | bashWindows (PowerShell):
irm https://raw.githubusercontent.com/vvka-141/pgmi/main/scripts/install.ps1 | iexPrefer a package manager or a checksum-verified download? brew install --cask vvka-141/pgmi/pgmi (macOS), APT, or a GitHub Releases
archive all work — see the full install options
.
Verify the installation:
pgmi --versionYou should see output like:
pgmi 0.x.x (compat 1)
Commit: <sha>, Built: <date>, Platform: <os>/<arch>If
pgmiis not found, the install directory is not on your PATH. The install script prints the location it used; add that directory to your PATH and restart your terminal.
Install from source (optional — requires Go 1.25+)
go install github.com/vvka-141/pgmi/cmd/pgmi@latestIf pgmi is then not found, your Go bin directory is not on your PATH:
- Linux/macOS: add
export PATH="$PATH:$(go env GOPATH)/bin"to your~/.bashrcor~/.zshrc, then restart your terminal. - Windows PowerShell: add
$env:Path += ";$(go env GOPATH)\bin", or set it permanently via System Settings → Environment Variables.
Run go env GOPATH to see where Go installs binaries; pgmi is in the bin subfolder.
Alternative installation methods:
macOS (Homebrew)
brew install --cask vvka-141/pgmi/pgmiDebian/Ubuntu (APT)
curl -1sLf 'https://dl.cloudsmith.io/public/vvka-141/pgmi/setup.deb.sh' | sudo bash
sudo apt update && sudo apt install pgmiThe script configures the Cloudsmith APT repository
and GPG key. The pgmi binary is then installed via standard apt install with package signature verification.
Windows (direct download)
Download the latest .zip from GitHub Releases
, extract it, and add the folder to your PATH.
Step 2: Make sure PostgreSQL is reachable#
pgmi needs to connect to PostgreSQL. Let’s verify your server is running and accepting connections.
Using pgAdmin:
- Open pgAdmin
- In the left panel, you should see a server (usually called “PostgreSQL” or “Local”)
- Click on it — if it connects without errors, you’re good
- Note the connection details: right-click the server → Properties → Connection tab. You’ll see the host, port, and username
Using the terminal:
psql -h localhost -U postgres -c "SELECT version();"If prompted for a password, enter your postgres user password. You should see something like:
version
------------------------------------------------------------
PostgreSQL 16.x on x86_64-...Common issues:
- “Connection refused”: PostgreSQL is not running. Start it via your OS service manager, or open pgAdmin — it often shows a clear error about the server being down.
- “Password authentication failed”: Wrong password. If you just installed PostgreSQL, the default password is whatever you set during installation. On some Linux installs, local connections use
peerauthentication — trysudo -u postgres psqlinstead.- “Could not connect to server”: Check that PostgreSQL is listening on
localhost:5432. In pgAdmin: right-click server → Properties → Connection tab.
Step 3: Create a project#
pgmi init myapp --template basic
cd myappInteractive setup: if you run
pgmi init myapp(without flags) on a TTY, an interactive wizard walks you through template choice and connection setup, saving apgmi.yamlfor laterpgmi deployruns. Add--template <name>to skip the template prompt, or setPGMI_NON_INTERACTIVE=1/CI=1to bypass wizards entirely.If you are using the advanced template (or adopting metadata-driven ordering) and already have SQL files without
<pgmi-meta>blocks, runpgmi metadata scaffoldfrom the project root to auto-generate them. Basic template users can ignore this.
This creates a ready-to-deploy project:
myapp/
├── deploy.sql ← Your deployment logic (the brain)
├── project.json ← Project metadata (read by deploy.sql, not by pgmi)
├── pgmi.yaml ← Connection defaults (the config)
├── migrations/ ← Your SQL files go here
│ ├── 001_users.sql
│ └── 002_user_crud.sql
├── __test__/ ← Your test files (or __tests__/)
│ ├── _setup.sql ← Test fixture (seed data)
│ └── test_user_crud.sql
└── README.mdNote: Both
__test__/and__tests__/work identically. Use whichever matches your team’s convention.
Let’s look at what was generated.
pgmi.yaml — your project config#
Open pgmi.yaml. It looks like this:
connection:
database: mydb
host: localhost
port: 5432
sslmode: prefer
params:
env: development
timeout: 3mThis tells pgmi: “when I deploy, connect to localhost:5432 and use a database called mydb”. No JSON, no XML — just this small YAML file for connection defaults.
Update the database name and add your username:
connection:
host: localhost
port: 5432
username: postgres
database: myapp
params:
env: development
timeout: 3mWhere does the password go? Not in
pgmi.yaml— that file is meant to be committed to Git. Set the password as an environment variable instead:# Linux/macOS export PGPASSWORD="your-postgres-password" # Windows PowerShell $env:PGPASSWORD="your-postgres-password" # Windows CMD set PGPASSWORD=your-postgres-passwordAlternatively, use a full connection string:
# Linux/macOS export PGMI_CONNECTION_STRING="postgresql://postgres:your-postgres-password@localhost:5432/postgres" # Windows PowerShell $env:PGMI_CONNECTION_STRING="postgresql://postgres:your-postgres-password@localhost:5432/postgres"When
PGMI_CONNECTION_STRINGis set, it provides the host, port, username, and password. pgmi connects to the database in the connection string first (typicallypostgres) to runCREATE DATABASE, then switches to the target database frompgmi.yamlto deploy. This is why the connection string points topostgreswhilepgmi.yamlsaysmyapp— they serve different purposes.
deploy.sql — your deployment logic#
Open deploy.sql. This is the only file that controls what happens during deployment. Not a config file. Not a framework. Just PostgreSQL — the templates use PL/pgSQL, PostgreSQL’s procedural language, for loops and conditionals.
pgmi loads your project files into session-scoped views, then runs deploy.sql:
pg_temp.pgmi_source_view— raw access to all files (for introspection)pg_temp.pgmi_plan_view— files sorted by execution order (for deployment)
The basic template queries pg_temp.pgmi_source_view (raw file access); the advanced template uses pg_temp.pgmi_plan_view (metadata-driven ordering).
migrations/ — your SQL files#
These are regular SQL files — no annotations or special comments required. The basic template creates a "user" table (001_users.sql) and CRUD functions (002_user_crud.sql).
Step 4: Deploy#
Make sure your password is set (see Step 3), then:
pgmi deploy . --overwrite --forceWhat this does:
.means “this directory” (where pgmi.yaml and deploy.sql are)--overwriteallows dropping and recreating the database if it already exists--forcereplaces interactive confirmation with a 5-second countdown (you can still press Ctrl+C to cancel)
Note: --overwrite is for local development. In production, deploy incrementally without this flag.
CI/CD: pin the pgmi session API with
--compat 1so a pgmi upgrade can’t change your deploy’s behavior, and drop--overwritefor incremental deploys:pgmi deploy . -d myapp --compat 1 --forceSee the CI/CD Guide for a complete pipeline example.
You should see output including test notices and ending with an ASCII art “DONE” banner:
NOTICE: Dev seed: admin user ready (admin@example.com id=1)
NOTICE: [pgmi] Test suite started
NOTICE: [pgmi] Fixture: ./__test__/_setup.sql
NOTICE: [pgmi] Test: ./__test__/test_user_crud.sql
NOTICE: [pgmi] Test suite completed (...)
___ ___ _ _ ___
| \ / _ \| \| | __|
| |) | (_) | .` | _|
|___/ \___/|_|\_|___|Your database myapp now exists with a "user" table, CRUD functions, and the built-in tests have already verified they work.
Step 5: Verify the deployment#
Using pgAdmin#
- In the left panel, right-click “Databases” → Refresh
- You should see a new database called myapp
- Open the Query Tool (Tools → Query Tool) and run:
SELECT * FROM get_user('admin@example.com');You should see the admin user record with email = 'admin@example.com' and name = 'Administrator'.
Using the terminal#
psql -h localhost -U postgres -d myapp -c "SELECT * FROM get_user('admin@example.com');" id | email | name | created_at
----+---------------------+---------------+-------------------------------
1 | admin@example.com | Administrator | 2026-...Try it with a custom admin email parameter:
pgmi deploy . --overwrite --force --param admin_email=you@example.com
psql -h localhost -U postgres -d myapp -c "SELECT * FROM get_user('you@example.com');"You just deployed a PostgreSQL project with pgmi.
Step 6: Add a new migration#
Create a new file migrations/003_orders.sql:
CREATE TABLE IF NOT EXISTS "order" (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES "user"(id),
total NUMERIC(10,2) NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW()
);Deploy again:
pgmi deploy . --overwrite --forceCheck the database (pgAdmin: refresh, or terminal):
psql -h localhost -U postgres -d myapp -c "\dt"You should see both the user and order tables. The basic template uses CREATE OR REPLACE / IF NOT EXISTS patterns, so you can also deploy without --overwrite for incremental changes — though during early development, --overwrite --force is the simplest approach.
What just happened?#
Here’s the entire model in four points:
- pgmi loaded your files (everything in the project folder) into PostgreSQL temporary tables and views (
pg_temp.pgmi_source_view,pg_temp.pgmi_plan_view) - pgmi ran
deploy.sql, which queries those views and directly executes files usingEXECUTE v_file.content - Your SQL files are regular SQL — no framework conventions, no special syntax
deploy.sqlis the only thing that decides what runs, in what order, with what transaction boundaries. Not a config file. Not pgmi. Your SQL.
This is what makes pgmi different from migration tools: PostgreSQL itself is the deployment engine. You write the logic in SQL, and pgmi just provides the infrastructure to get your files into the database session.
Choosing a template#
pgmi provides two templates for pgmi init. Start with basic when you want a small migration scaffold. Reach for advanced when you want a large, editable reference system — metadata tracking, roles, schemas, audit logging, and API/MCP patterns you adapt to your needs. Advanced is not a “more production” version of basic; it’s a different, richer starting point you own and trim. Either can be production depending on the project — advanced is more complete, not more production.
| Feature | Basic | Advanced |
|---|---|---|
| Learning curve | Minimal — read the code, understand it | Moderate — more moving parts |
| File ordering | Path-based (001_, 002_, …) | Metadata-driven via <pgmi-meta> sort keys |
| Execution view | pg_temp.pgmi_source_view | pg_temp.pgmi_plan_view (with multi-phase support) |
| Idempotency control | Manual (CREATE OR REPLACE, IF NOT EXISTS) | Metadata-driven (idempotent="true/false") |
| Script tracking | None (stateless) | UUID-based tracking in internal.deployment_script_execution_log |
| Testing | CALL pgmi_test() with savepoints | Same, plus hierarchical fixtures |
| Project structure | Flat: migrations/, __test__/ | Multi-module: api/, lib/ (core, utils, api), membership/, tools/ |
| Parameters | current_setting('pgmi.key', true) | Same, plus deployment_setting() helper with defaults |
| MCP integration | None | Full MCP server for AI assistants |
When to use basic:
- Learning pgmi
- Simple projects with < 20 SQL files
- Linear migrations without complex ordering needs
When to use advanced:
- You want pre-built patterns for script tracking, idempotency, role separation, audit logging, or API/MCP integration — and you’re comfortable adapting or removing what doesn’t apply
- Large projects with explicit execution phases
- Teams that benefit from script tracking and audit logging
- Projects integrating with AI assistants via MCP
Advanced template requirements:
- PostgreSQL 15+ (the RLS model relies on
security_invokerviews) - A role with
CREATEROLE+CREATE EXTENSIONfor initial setup — no superuser (creates owner, admin, api, customer roles). The admin role on managed providers (rds_superuser,azure_pg_admin,supabase_admin, …) carries these grants. - Extensions:
uuid-ossp,pgcrypto,pg_trgm,hstore(all available on managed clouds — RDS, Azure Flexible Server, Cloud SQL, Supabase, Neon)
Switching templates:
You can migrate from basic to advanced later:
- Run
pgmi metadata scaffold . --writeto add metadata blocks to existing files - Adjust
idempotentflags (migrations → false, functions → true) - Update
deploy.sqlto querypg_temp.pgmi_plan_viewinstead ofpg_temp.pgmi_source_view
See Metadata Guide for details on the migration process.
Next steps#
Now that you have a working project:
| Want to… | Read |
|---|---|
| Understand when pgmi makes sense | Why pgmi? |
| Migrate from Flyway or Liquibase | Coming from Other Tools |
| Pass parameters to your deployment | Configuration Reference |
| Write database tests that run inside transactions | Testing Guide |
| Understand the session tables and helper functions | Session API Reference |
| Use metadata for script tracking and ordering | Metadata Guide |
| Prepare for production deployment | Production Guide |
| Deploy from a CI/CD pipeline | CI/CD Guide |
| Handle secrets in CI/CD | Security Guide |
Troubleshooting#
“pgmi: command not found”#
Your Go bin directory is not in your PATH. See Step 1 for instructions.
“connection refused” or “could not connect to server”#
PostgreSQL is not running or not listening on the expected host/port. Verify in pgAdmin (see Step 2 ) or check your OS service manager.
“password authentication failed”#
The password you set via PGPASSWORD or PGMI_CONNECTION_STRING doesn’t match your PostgreSQL user. Try connecting with pgAdmin or psql first to confirm the correct password.
“database already exists” (without –overwrite)#
Without --overwrite, pgmi creates the database if it doesn’t exist and deploys to it if it does — this is normal incremental deployment. The --overwrite flag is only needed when you want to drop and recreate the database from scratch (local development, CI).
“deploy.sql not found”#
You’re running pgmi deploy from the wrong directory. Make sure you’re inside your project folder (where deploy.sql is), or pass the path explicitly: pgmi deploy ./myapp.
“permission denied” when creating database#
Your PostgreSQL user needs the CREATEDB privilege. In pgAdmin: right-click your login role → Properties → Privileges → check “Can create databases”. Or via SQL:
ALTER ROLE postgres CREATEDB;