Move state off the local disk
By default, pz keeps watermarks, run results, and the event stream under .pz/state/ and
.pz/runs/ on the local filesystem. That breaks on an ephemeral host, such as a container that
disappears between scheduled runs, because the next run has no memory of where the last one
stopped. This guide moves that state into SQL Server or behind an HTTP
endpoint instead. Read it once a project needs to run somewhere with no durable disk.
Prerequisites
Section titled “Prerequisites”- A
pzproject that already runs locally. Follow the quickstart if you don’t have one. - For the
sqlserverbackend: a SQL Server database and an account withdb_ddladminrights on it, or equivalentCREATE SCHEMA/CREATE TABLE/ALTER TABLEpermissions.pzcreates and migrates its own schema; it is never handed a pre-built one. - For the
httpbackend: a service that implements the keyed state contract at a URL you control.
-
Add a
state:block toproject.yml, naming the backend and, forsqlserver, a connection fromconnections.yml:project.yml state:backend: sqlserverconnection: opsschema: pzconnectionmust name aconnections.ymlentry whoseconnector:issqlserver. Everystate:key has an environment-variable default (PZ_STATE_BACKEND,PZ_STATE_SCHEMA, and so on), so a host can set these once for every project it runs instead of editing each project’s file. An explicitproject.ymlkey always wins over its environment counterpart. See Environment variables for the full list. -
Or, for a host that must not hold a database credential, use
backend: httpinstead. The dispatching system servespz’s keyed state over HTTP, andpzreads and writes it through the one endpoint the run already has:project.yml state:backend: httpurl: https://state.example/api/agents/runs/<run-id>/statebackend: httpcovers watermarks and sync state only. Run results and the event stream stay local (run_results.jsonand stdout). SetPZ_STATE_TOKENif the endpoint requires a bearer token;pzsends it only when the variable is set. Settimeout_seconds(orPZ_STATE_TIMEOUT_SECONDS) to bound how long one state request may take (default 100 seconds); a request that runs past it fails withPZ0518. Cancelling the run aborts an in-flight request regardless. Sending a bearer token to a plainhttp://URL warns (PZ0530) unless the URL is a loopback address. -
Grant DDL rights on the target database, under
backend: sqlserver.pzissuesCREATE SCHEMA/CREATE TABLEon first use and migrates forward on every later connection.db_datareader/db_datawriteralone is not enough. -
Run once, and confirm the backend that was actually used:
Terminal window $ pz run --allnote: state backend: sqlserver (from project.yml)The opening line names where the effective backend came from:
project.yml, an environment variable, ordefault. That matters because a container image can setPZ_STATE_BACKENDfor every project it runs, so the source is not always the file you’re looking at.
Verify
Section titled “Verify”Run pz state show from a second host, or after clearing .pz/state/ locally, and confirm the
prior run’s watermarks are still there:
$ pz state show crm.ordersA populated result means the remote store, not the local filesystem, is now the system of record for this project’s state.
What still stays local
Section titled “What still stays local”Moving state into SQL Server or HTTP does not move staging.duckdb. It stays on local disk by
design, because it is a run’s buffer, not state, and it dies with the run’s node exactly as
before. .pz/state/audit.jsonl, the record of every manual pz state edit, also always stays
local and is never touched by retention or pz clean, on any backend.
pz retry still works from a fresh host: it reads the prior run from whichever store
state.artifacts resolved to. What it loses on a new host is reuse of previously staged data,
since no local staging file survives; it re-extracts the failed nodes instead, which is slower
but not incorrect.
Troubleshooting
Section titled “Troubleshooting”| If you see | Do |
|---|---|
PZ0124 |
state: sets a key that belongs to a different backend, such as schema under backend: http. Check which keys each backend accepts in State. |
PZ0125 |
state.connection names a connection that doesn’t exist or isn’t connector: sqlserver, or a backend is missing its required credential (state.connection or PZ_STATE_CONNECTION_STRING for sqlserver; state.url or PZ_STATE_URL for http). |
PZ0518 |
The state database or HTTP endpoint never answered at all — unreachable, or authentication failed before a response came back. Check network access and credentials before anything else. |
PZ0519 |
The connection succeeded but the schema is newer than this build understands, or DDL was refused. Check the account’s DDL rights on that schema first, not the network. |
PZ0520 |
Two runs advanced the same watermark concurrently. Confirm nothing else is scheduled against the same project and state store at the same time. |
PZ0528 |
Another process is migrating (or is stuck holding the migration lock on) the same SQL Server schema. Retry; this isn’t a DDL-rights problem. |
PZ0529 |
The store was reached, but the operation itself failed — a permanent SQL error, an exhausted transient-retry budget, or an unexpected HTTP status. Distinct from PZ0518: connectivity is fine here. |
PZ0530 |
(warning) A bearer token is being sent to a plain http:// state URL. Move the endpoint to https://, or ignore it for a deliberately loopback/VPN-only endpoint. |
Related
Section titled “Related”- State: what pz remembers between runs, the
.pz/layout, and the three state backends in full. - Environment variables: every
PZ_STATE_*variable and theproject.ymlkey it defaults. - project.yml reference: the complete
state:key table. - Run in CI: pointing a CI job’s ephemeral
.pz/at a remote state store. - Error codes: what
PZ0124,PZ0125,PZ0518,PZ0519,PZ0520,PZ0528,PZ0529, andPZ0530mean in full.