piyaz
Guides

Self-host operations

Upgrade, back up, restore, and harden a self-hosted Piyaz instance.

Self-host operations

This guide covers day-2 operations for a self-hosted instance: upgrading to new releases, backing up and restoring the database, and hardening the deployment. It assumes you already have Piyaz running from the self-host setup guide.

Upgrading

New releases can add tables or columns to the schema. Before you upgrade, read the changelog or release notes so you know what changed.

Reinstall dependencies

bun install --production

Sync the schema

bun run db:push

This runs drizzle-kit push, which compares your live database to the current schema and applies the difference in place. There is no separate migration runner, and there is no rollback command.

Rebuild and restart

bun run build
bun run start

drizzle-kit push is well suited to additive changes such as new tables and columns. A change that drops or renames a column can lose data. Take a backup before any upgrade (see below), and review the diff that db:push prints before you confirm it.

Backup and restore

Piyaz stores all project data in the Postgres database that runs in the Docker container defined by docker-compose.yml. The container is named piyaz-db-1, and both the database and the owner role are named piyaz.

Back up the database

Run pg_dump inside the container and write the dump to a file on the host:

docker exec piyaz-db-1 pg_dump -U piyaz piyaz > piyaz-backup.sql

Store the dump somewhere off the host. Schedule it (for example with cron) if you want regular snapshots.

Back up more than the database. Your .env.local holds the generated secrets, including BETTER_AUTH_SECRET. If you lose that secret, every existing session is invalidated and users have to sign in again. Keep .env.local in a secure backup, separate from the SQL dump.

Restore the database

Restore a plain SQL dump by piping it back into psql inside the container:

docker exec -i piyaz-db-1 psql -U piyaz piyaz < piyaz-backup.sql

Restore into an empty database. If you took the dump with pg_dump -Fc (custom format) instead of plain SQL, restore it with pg_restore rather than psql:

docker exec -i piyaz-db-1 pg_restore -U piyaz -d piyaz < piyaz-backup.dump

After restoring, confirm the schema is current with bun run db:push.

Security hardening

The three-role database model

Piyaz connects to Postgres with three separate roles, each with its own connection URL in .env.local, and uses row-level security to scope access:

  • app_user (DATABASE_URL): the application role for normal request traffic.
  • service_role (DATABASE_SERVICE_ROLE_URL): the elevated role for operations that run outside a single user's row-level scope.
  • auth_role (DATABASE_AUTH_URL): the role used by the authentication layer.

Keep these roles separate. Do not point every URL at the same superuser.

Generate strong secrets

Generate a distinct password for each database role and a separate auth secret. Never reuse the example values.

openssl rand -hex 32     # each of the three role passwords
openssl rand -base64 32  # BETTER_AUTH_SECRET

Protect the environment file

.env.local contains every secret for the deployment. Keep it out of version control (it is already gitignored in the repository) and restrict its file permissions on the host.

Do not expose Postgres publicly

The compose file maps Postgres to port 5432. Bind it to localhost or a private network, and put a firewall in front of the host. Only the Piyaz application needs to reach the database.

Point the plugin at localhost

Self-hosters connect their agent through the plugin's piyaz-local server, which targets http://localhost:3000/api/mcp. This keeps your data on your own machine instead of routing through the hosted service.

Found a security issue? Do not open a public issue. Report it through GitHub's private vulnerability reporting as described in the project's SECURITY.md.

On this page