feat(sites): add PostgreSQL as a selectable database engine#119
feat(sites): add PostgreSQL as a selectable database engine#119mihir-kandoi wants to merge 9 commits into
Conversation
|
Can someone pull and test this? Works on my machine |
df15212 to
2223146
Compare
| # data_dir = "/var/lib/mysql-my-bench" # per-instance datadir (auto-derived; used as bind-mount target with ZFS) | ||
|
|
||
| # ── PostgreSQL (opt-in per-site engine; installed & provisioned by init) ────── | ||
| # Shared across benches (one server, port 5432); `bench new` generates the password. |
There was a problem hiding this comment.
DB can be dedicated per bench also.
Not sure, if we should give choice of db per site.
It will be simple to keep it at bench level only. We can ask them the choice in bench's setup wizard.
There was a problem hiding this comment.
The current bench supports postgres and mariadb on the same bench so I just copied that. Shall I make it per bench?
There was a problem hiding this comment.
in first version, we can make it per bench, as bench is responsible for managing the db also.
For SQLite it's fine to give the choices, as bench doesn't need to take care of the db much.
There was a problem hiding this comment.
We have only one DB support per-bench, either dedicated or shared.
There was a problem hiding this comment.
@Aradhya-Tripathi is this a new bench thing? shifting to per bench anyways. will update.
There was a problem hiding this comment.
There is an option to users whether they want shared or dedicated per bench db in bench's setup wizard.
Create a new bench and check the setup wizard. That has been updated in last few days.
Can you add playwright with this integration as well, we recently added for e2e setup. |
d526055 to
f636739
Compare
|
@Aradhya-Tripathi @tanmoysrt done. let me know if you need me to test anything. Works fine in dev mode. Will rebase on your green light (did it twice, kept getting new conflicts on each push 😢 ) |
|
@mihir-kandoi the repo renamed, so you have to modify the code ig. |
|
@tanmoysrt will do, anything else pending? code look right? |
Frappe/ERPNext support PostgreSQL, but bench could only create MariaDB sites. This adds PostgreSQL end-to-end while keeping MariaDB the default. What's included: - New [postgres] config section (PostgresConfig) parsed/serialized in bench.toml and editable on the admin Settings page (write-only password). - PostgresManager: bench init installs and provisions a shared PostgreSQL server (apt/Homebrew/apk), starts/enables the service, and sets the superuser password. bench new generates the password. - Engine choice (db_type) plumbed through the admin Create Site dialog, bench new-site, the task runner, and Site.create(). Empty Postgres password falls back to a placeholder so frappe never hangs on getpass. - Restore/reinstall are engine-aware: restore-from-backup, create-from- upload, and reinstall use the right root credentials per engine (engine inferred from the source site / existing site, or chosen for uploads). - Per-site engine logos (MariaDB/PostgreSQL) in the sites list + detail, with db_type exposed by SiteReader. - build-admin now fails with a clear message on Node < 20.11 instead of an opaque toolchain stack trace. MariaDB remains the default engine; PostgreSQL is opt-in per site. Tests added across config, core, tasks, managers, and admin endpoints. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A bench now runs exactly one engine, selected at creation via `bench new --database mariadb|postgres` (default mariadb) and stored as bench.db_type. Previously every bench co-provisioned a shared PostgreSQL server alongside MariaDB and the engine was chosen per-site. - init installs/provisions only the bench's engine and its client/build headers (libmariadb-dev/mariadb-dev vs libpq-dev/postgresql-dev); a dedicated MariaDB instance is only created for MariaDB benches. - Drop the per-site db_type: Site reads the engine from the bench config, so new-site/reinstall/restore no longer thread it through. - Admin: wizard picks the engine at bench creation, adds a /validate-postgres endpoint, and surfaces db_type in settings. - Add the postgres DB lifecycle e2e spec and update unit tests.
DropSiteCommand only ever passed MariaDB credentials, so dropping a site on a PostgreSQL bench connected as `postgres` with no password and failed under password auth — it passed on trust-auth setups (e.g. Homebrew) which masked it locally and only surfaced in CI. Move the engine-keyed root args onto Bench.db_root_args() so drop/restore/reinstall share one source of truth. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The engine is a bench-wide property, so drop the per-site engine badges in the site list and detail views and surface it once in the sidebar footer, fed by a new db_type field on /api/status. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
In a source checkout, `bench start` served whatever was already in dist, so local admin UI edits never appeared without a manual `build-admin --force`. Rebuild from source when the frontend is newer than the built bundle; best-effort so a build failure (e.g. old Node) still serves the existing dist. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
f636739 to
ffa4e6b
Compare
The bench_cli -> pilot rename moved the package dir and pyproject, but feature code added afterward still imported the old bench_cli package, breaking `pip install -e .` and the unit-test CI run. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GitHub runners preconfigure packages.microsoft.com / azure-cli apt repos that intermittently 403, failing `apt-get update` for the whole step even though none of the installed packages come from them. Remove them first. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@greptile review |
Confidence Score: 4/5PostgreSQL init is broken on Alpine until the package names are corrected.
pilot/managers/postgres_manager.py, pilot/commands/init.py, admin/backend/views/setup.py
|
| Filename | Overview |
|---|---|
| pilot/managers/postgres_manager.py | Adds PostgreSQL install, service control, provisioning, and credential checks; Alpine server package naming needs a fix. |
| pilot/commands/init.py | Switches init to install and provision only the selected database engine; Alpine PostgreSQL build package naming needs a fix. |
| admin/backend/views/setup.py | Adds PostgreSQL setup validation and relaxes password requirements for PostgreSQL benches. |
| pilot/core/site.py | Builds database-specific arguments for site creation, restore, and reinstall. |
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 3
pilot/managers/postgres_manager.py:47
**Alpine Server Package Missing**
When a PostgreSQL bench is initialized on Alpine, this passes the unversioned `postgresql` package name to `apk`. Alpine PostgreSQL packages are versioned, so the install step fails before provisioning can start.
### Issue 2 of 3
pilot/commands/init.py:202
**Alpine Header Package Missing**
The PostgreSQL dev package is versioned on Alpine too, so `apk add postgresql-dev` fails for PostgreSQL benches. That aborts init before the Frappe environment can install its PostgreSQL client dependencies.
### Issue 3 of 3
admin/backend/views/setup.py:190
**Blank Password Reaches Site Creation**
The setup API allows PostgreSQL benches to save an empty superuser password. On a password-auth PostgreSQL server, init skips setting the role password and later `new-site` sends the `trust_auth` placeholder, so the wizard can complete setup but fail on the first site creation.
Reviews (1): Last reviewed commit: "ci(e2e): drop flaky Microsoft apt repos ..." | Re-trigger Greptile
| if is_macos(): | ||
| get_package_manager().install(self._brew_package()) | ||
| return | ||
| get_package_manager().install("postgresql", "postgresql-client") |
There was a problem hiding this comment.
When a PostgreSQL bench is initialized on Alpine, this passes the unversioned postgresql package name to apk. Alpine PostgreSQL packages are versioned, so the install step fails before provisioning can start.
Prompt To Fix With AI
This is a comment left during a code review.
Path: pilot/managers/postgres_manager.py
Line: 47
Comment:
**Alpine Server Package Missing**
When a PostgreSQL bench is initialized on Alpine, this passes the unversioned `postgresql` package name to `apk`. Alpine PostgreSQL packages are versioned, so the install step fails before provisioning can start.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Fixed in 171cdd2. Verified against the Alpine package index — there's no unversioned postgresql/postgresql-client, only versioned postgresql<N> (e.g. postgresql17). install() now resolves the major (the configured postgres.version, else the newest apk offers) and installs postgresql<N> + postgresql<N>-client.
| # Engine-specific client/build headers — only the bench's engine is installed. | ||
| _DB_BUILD_PACKAGES = { | ||
| "mariadb": {"debian": "libmariadb-dev", "alpine": "mariadb-dev"}, | ||
| "postgres": {"debian": "libpq-dev", "alpine": "postgresql-dev"}, |
There was a problem hiding this comment.
The PostgreSQL dev package is versioned on Alpine too, so apk add postgresql-dev fails for PostgreSQL benches. That aborts init before the Frappe environment can install its PostgreSQL client dependencies.
Prompt To Fix With AI
This is a comment left during a code review.
Path: pilot/commands/init.py
Line: 202
Comment:
**Alpine Header Package Missing**
The PostgreSQL dev package is versioned on Alpine too, so `apk add postgresql-dev` fails for PostgreSQL benches. That aborts init before the Frappe environment can install its PostgreSQL client dependencies.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Fixed in 171cdd2. The dev headers are versioned on Alpine too, so init now installs postgresql<N>-dev via PostgresManager.alpine_dev_package(), and only for postgres benches. (Related: the MariaDB client headers are now installed for every bench, since frappe imports mysqlclient in its __init__.py regardless of engine.)
| # A MariaDB bench needs a root password; PostgreSQL benches don't (init sets | ||
| # the superuser password, falling back to a placeholder for trust/peer auth). | ||
| if data.get("db_type", "mariadb") == "mariadb" and not data.get("mariadb_password"): | ||
| return "mariadb_password is required" |
There was a problem hiding this comment.
Blank Password Reaches Site Creation
The setup API allows PostgreSQL benches to save an empty superuser password. On a password-auth PostgreSQL server, init skips setting the role password and later new-site sends the trust_auth placeholder, so the wizard can complete setup but fail on the first site creation.
Prompt To Fix With AI
This is a comment left during a code review.
Path: admin/backend/views/setup.py
Line: 190
Comment:
**Blank Password Reaches Site Creation**
The setup API allows PostgreSQL benches to save an empty superuser password. On a password-auth PostgreSQL server, init skips setting the role password and later `new-site` sends the `trust_auth` placeholder, so the wizard can complete setup but fail on the first site creation.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Fixed in 171cdd2. _validate now rejects a blank PostgreSQL superuser password (and the wizard's Database step does too), so a password-less postgres bench can no longer reach site creation. On a fresh/dedicated server, init sets this password during provisioning.
|
@tanmoysrt the UI was mostly me not Claude, I really do have shit UI taste 🤣 Will update the PR in a while |
Add dedicated PostgreSQL clusters and apply the review feedback from Tanmoy and Greptile. Dedicated Postgres: - A postgres bench can now run its own cluster on its own port (pg_createcluster/pg_dropcluster/pg_ctlcluster) on systemd Linux, or share the system server (Alpine/macOS always shared). Plumbed through postgres config, `bench new`, the setup wizard, init, and `bench drop` (engine-aware teardown). The cluster version is detected, not hardcoded. UI / review fixes: - Wizard: PostgreSQL gets a dedicated/shared selector (systemd Linux); dropped the explanatory line under the engine selector. - Bench Manager (New Bench dialog) no longer asks for the engine — the new bench's own setup wizard does. - Show the engine in Settings (read-only) instead of a sidebar logo, and make the PostgreSQL settings fields read-only. Provisioning fixes: - Always install the MariaDB client headers (frappe imports mysqlclient in its __init__.py for every engine); add libpq only for postgres. - Greptile: use versioned Alpine postgres package names; reject a blank postgres superuser password in the setup wizard. Tests: - Dedicated-cluster unit coverage (manager, config, new, drop, setup) and a `postgres-dedicated` e2e CI variant. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pg_createcluster --start starts the cluster via `systemctl start postgresql@<ver>-<cluster>`, whose templated unit fails with "Assertion failed on job" on the GitHub Actions runner (and in containers). Create the cluster, then start it directly with `pg_ctlcluster --skip-systemctl-redirect`. Boot autostart still works via the enabled postgresql meta-service (the cluster's start.conf stays auto). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@tanmoysrt @Aradhya-Tripathi done. I couldnt check the new bench manager UI as production is Linux only. Please test on my behalf... |
|
@mihir-kandoi ping me if you don't have hetzner or digitalocean access. |
|
@tanmoysrt works on my VPS |





Overview
Frappe and ERPNext support PostgreSQL, but bench could only create MariaDB sites. This adds PostgreSQL as a selectable database engine end to end — installation, provisioning, site creation, restore/reinstall, and the admin UI — while keeping MariaDB the default.
The engine is a per-bench choice (every site on a bench uses it), selected at
bench newor in the setup wizard — matching how a bench already owns one database. A PostgreSQL bench can run a dedicated per-bench cluster (its own port, systemd Linux) or share the system server.What changed
Config
bench.db_type(mariadb|postgres) selects the engine for the whole bench. New[postgres]section (PostgresConfig: host, port, root_password, admin_user, version, instance) parsed/serialized inbench.tomland validated.Provisioning
PostgresManagermirrorsMariaDBManager.bench initinstalls PostgreSQL (apt / Homebrew / apk) and provisions it: ensures the superuser role exists and sets its password.pg_createclusteron its own (collision-checked) port;bench droptears it down withpg_dropcluster. Alpine/macOS use the shared system server. The cluster version is detected from the installed server, not hardcoded.mysqlclientin its__init__.pyfor every engine, so the MariaDB client headers are always installed;libpqheaders are added only for postgres benches.Site lifecycle (engine-aware)
Site.create()passes--db-type postgres+ the bench's postgres root connection.restore/reinstall/drop-siteuse the right root credentials per the bench's engine (frappe reads the engine fromsite_config.json, so no--db-typeflag is needed there).Admin UI
Tooling
bench build-adminfails with a clear message on Node < 20.11 instead of an opaqueunpluginstack trace.Review feedback addressed
@tanmoysrt
mysqlclient==2.2.8build failure on postgres benches.Greptile
-devheaders).Tests
Siteargument building,PostgresManager(shared + dedicated install/secure/provision/remove), the setup-wizard validation + port assignment, engine-awarebench drop,SiteReader.db_type, and the admin endpoints.postgres-dedicatedCI variant runs the full create → wizard → site → drop lifecycle (alongsidepostgresshared and the MariaDB variants).Reviewer notes
postgres-dedicatede2e variant is its first real validation on a clean Ubuntu runner.admin/backend/static/dist/) are gitignored; rebuild the frontend (Node ≥ 20.11) or download prebuilt.🤖 Generated with Claude Code