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.
Pull the new code
git pullReinstall dependencies
bun install --productionSync the schema
bun run db:pushThis 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 startdrizzle-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.sqlStore 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.sqlRestore 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.dumpAfter 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_SECRETProtect 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.