# zero-cache Config

`zero-cache` is configured either via CLI flag or environment variable. There is no separate `zero.config` file.

You can also see all available flags by running `zero-cache --help`.

## Required Flags

### Upstream DB

The "upstream" authoritative postgres database. In the future we will support other types of upstream besides PG.

flag: `--upstream-db`env: `ZERO_UPSTREAM_DB`required: `true`

### Admin Password

A password used to administer zero-cache server, for example to access the `/statz` endpoint and the [inspector](https://zero.rocicorp.dev/docs/debug/inspector).

This is required in production (when `NODE_ENV=production`) because we want all Zero servers to be debuggable using admin tools by default, without needing a restart. But we also don't want to expose sensitive data using them.

flag: `--admin-password`env: `ZERO_ADMIN_PASSWORD`required: in production (when `NODE_ENV=production`)

## Optional Flags

### App ID

Unique identifier for the app.

Multiple zero-cache apps can run on a single upstream database, each of which is isolated from the others, with its own permissions, sharding (future feature), and change/cvr databases.

The metadata of an app is stored in an upstream schema with the same name, e.g. `zero`, and the metadata for each app shard, e.g. client and mutation ids, is stored in the `{app-id}_{#}` schema. (Currently there is only a single "0" shard, but this will change with sharding).

The CVR and Change data are managed in schemas named `{app-id}_{shard-num}/cvr` and `{app-id}_{shard-num}/cdc`, respectively, allowing multiple apps and shards to share the same database instance (e.g. a Postgres "cluster") for CVR and Change management.

Due to constraints on replication slot names, an App ID may only consist of lower-case letters, numbers, and the underscore character.

Note that this option is used by both `zero-cache` and `zero-deploy-permissions`.

flag: `--app-id`env: `ZERO_APP_ID`default: `zero`

### App Publications

Postgres PUBLICATIONs that define the tables and columns to replicate. Publication names may not begin with an underscore, as zero reserves that prefix for internal use.

If unspecified, zero-cache will create and use an internal publication that publishes all tables in the public schema, i.e.:

```
CREATE PUBLICATION _{app-id}_public_0 FOR TABLES IN SCHEMA public;
```

Note that changing the set of publications will result in resyncing the replica, which may involve downtime (replication lag) while the new replica is initializing. To change the set of publications without disrupting an existing app, a new app should be created.

To use a custom publication, you can create one with:

```sql
CREATE PUBLICATION zero_data FOR TABLES IN SCHEMA public;
-- or, more selectively:
CREATE PUBLICATION zero_data FOR TABLE users, orders;
```

Then set the flag to that publication name, e.g.: `ZERO_APP_PUBLICATIONS=zero_data`. To specify multiple publications, separate them with commas, e.g.: `ZERO_APP_PUBLICATIONS=zero_data1,zero_data2`.

flag: `--app-publications`env: `ZERO_APP_PUBLICATIONS`default: `_{app-id}_public_0`

### Auth Revalidate Interval Seconds

How often `zero-cache` re-checks that each live connection is still authorized to use your `/query` endpoint.

On each interval, `zero-cache` sends a lightweight validation request using that connection's current auth context, such as forwarded cookies or an opaque auth token. If your query endpoint rejects that auth with a 401/403, the connection is disconnected.

Use this to bound how long already-open connections can continue after logout, session expiry, token revocation, or other server-side auth changes that happen without a reconnect. Lower values enforce auth changes faster, but send more validation requests to `/query`.

flag: `--auth-revalidate-interval-seconds`env: `ZERO_AUTH_REVALIDATE_INTERVAL_SECONDS`default: `unset`

### Auth Retransform Interval Seconds

How often `zero-cache` refreshes a client group's synced or named query transformations using one validated connection from that group.

This re-runs auth-sensitive query expansion even when the query set itself has not changed. It is useful when your query endpoint generates different ZQL based on current auth or server-side session state, such as roles, organization membership, feature flags, or other permissions-derived context.

Use this to bound how long a client group can keep using stale auth-derived query shapes after backend auth state changes. Lower values pick up those changes faster, but do more `/query` transform work.

If clients already call `updateAuth` whenever auth changes, this mainly serves as a background safety net for out-of-band auth changes.

flag: `--auth-retransform-interval-seconds`env: `ZERO_AUTH_RETRANSFORM_INTERVAL_SECONDS`default: `unset`

### Auto Reset

Automatically wipe and resync the replica when replication is halted. This situation can occur for configurations in which the upstream database provider prohibits event trigger creation, preventing the zero-cache from being able to correctly replicate schema changes. For such configurations, an upstream schema change will instead result in halting replication with an error indicating that the replica needs to be reset. When auto-reset is enabled, zero-cache will respond to such situations by shutting down, and when restarted, resetting the replica and all synced clients. This is a heavy-weight operation and can result in user-visible slowness or downtime if compute resources are scarce.

flag: `--auto-reset`env: `ZERO_AUTO_RESET`default: `true`

### Change DB

The Postgres database used to store recent replication log entries, in order to sync multiple view-syncers without requiring multiple replication slots on the upstream database. If unspecified, the upstream-db will be used.

flag: `--change-db`env: `ZERO_CHANGE_DB`

### Change Max Connections

The maximum number of connections to open to the change database. This is used by the change-streamer for catching up zero-cache replication subscriptions.

flag: `--change-max-conns`env: `ZERO_CHANGE_MAX_CONNS`default: `5`

### Change Streamer Back Pressure Limit Heap Proportion

The percentage of `--max-old-space-size` to use as a buffer for absorbing replication stream spikes. When the estimated amount of queued data exceeds this threshold, back pressure is applied to the replication stream, delaying downstream sync as a result.

The threshold was determined empirically with load testing. Higher thresholds have resulted in OOMs. Note also that the byte-counting logic in the queue is strictly an underestimate of actual memory usage (but importantly, proportionally correct), so the queue is actually using more than what this proportion suggests.

This parameter is exported as an emergency knob to reduce the size of the buffer in the event that the server OOMs from back pressure. Resist the urge to increase this proportion, as it is mainly useful for absorbing periodic spikes and does not meaningfully affect steady-state replication throughput; the latter is determined by other factors such as object serialization and PG throughput.

In other words, the back pressure limit does not constrain replication throughput; rather, it protects the system when the upstream throughput exceeds the downstream throughput.

flag: `--change-streamer-back-pressure-limit-heap-proportion`env: `ZERO_CHANGE_STREAMER_BACK_PRESSURE_LIMIT_HEAP_PROPORTION`default: `0.04`

### Change Streamer Flow Control Consensus Padding Seconds

During periodic flow control checks (every 64kb), this is the amount of time to wait after the majority of subscribers have acked, after which replication continues even if some subscribers have yet to ack. This is not a timeout for the entire send; it starts only after the majority of receivers have acked.

This allows a bounded amount of time for backlogged subscribers to catch up on each flush without forcing all subscribers to wait for the entire backlog to be processed. It is also useful for mitigating the effect of unresponsive subscribers due to severed WebSocket connections until liveness checks disconnect them.

Set this to a negative number to disable early flow control releases.

flag: `--change-streamer-flow-control-consensus-padding-seconds`env: `ZERO_CHANGE_STREAMER_FLOW_CONTROL_CONSENSUS_PADDING_SECONDS`default: `1`

### Change Streamer Mode

The mode for running or connecting to the change-streamer:

* `dedicated`: runs the change-streamer and shuts down when another change-streamer takes over the replication slot. This is appropriate in a single-node configuration, or for the replication-manager in a multi-node configuration.
* `discover`: connects to the change-streamer as internally advertised in the change-db. This is appropriate for the view-syncers in a multi-node setup. This may not work in all networking configurations (e.g., some private networking or port forwarding setups). Using `ZERO_CHANGE_STREAMER_URI` with an explicit routable hostname is recommended instead.

This option is ignored if `ZERO_CHANGE_STREAMER_URI` is set.

flag: `--change-streamer-mode`env: `ZERO_CHANGE_STREAMER_MODE`default: `dedicated`

### Change Streamer Port

The port on which the change-streamer runs. This is an internal protocol between the replication-manager and view-syncers, which runs in the same process tree in local development or a single-node configuration. If unspecified, defaults to `--port + 1`.

flag: `--change-streamer-port`env: `ZERO_CHANGE_STREAMER_PORT`default: `--port + 1`

### Change Streamer Startup Delay (ms)

The delay to wait before the change-streamer takes over the replication stream (i.e. the handoff during replication-manager updates), to allow load balancers to register the task as healthy based on healthcheck parameters. If a change stream request is received during this interval, the delay will be canceled and the takeover will happen immediately, since the incoming request indicates that the task is registered as a target.

flag: `--change-streamer-startup-delay-ms`env: `ZERO_CHANGE_STREAMER_STARTUP_DELAY_MS`default: `15000`

### Change Streamer URI

When set, connects to the change-streamer at the given URI. In a multi-node setup, this should be specified in view-syncer options, pointing to the replication-manager URI, which runs a change-streamer on port 4849.

flag: `--change-streamer-uri`env: `ZERO_CHANGE_STREAMER_URI`

### CVR DB

The Postgres database used to store CVRs. CVRs (client view records) keep track of the data synced to clients in order to determine the diff to send on reconnect. If unspecified, the upstream-db will be used.

flag: `--cvr-db`env: `ZERO_CVR_DB`

### CVR Garbage Collection Inactivity Threshold Hours

The duration after which an inactive CVR is eligible for garbage collection. Garbage collection is incremental and periodic, so eligible CVRs are not necessarily purged immediately.

flag: `--cvr-garbage-collection-inactivity-threshold-hours`env: `ZERO_CVR_GARBAGE_COLLECTION_INACTIVITY_THRESHOLD_HOURS`default: `48`

### CVR Garbage Collection Initial Batch Size

The initial number of CVRs to purge per garbage collection interval. This number is increased linearly if the rate of new CVRs exceeds the rate of purged CVRs, in order to reach a steady state. Setting this to 0 effectively disables CVR garbage collection.

flag: `--cvr-garbage-collection-initial-batch-size`env: `ZERO_CVR_GARBAGE_COLLECTION_INITIAL_BATCH_SIZE`default: `25`

### CVR Garbage Collection Initial Interval Seconds

The initial interval at which to check and garbage collect inactive CVRs. This interval is increased exponentially (up to 16 minutes) when there is nothing to purge.

flag: `--cvr-garbage-collection-initial-interval-seconds`env: `ZERO_CVR_GARBAGE_COLLECTION_INITIAL_INTERVAL_SECONDS`default: `60`

### CVR Max Connections

The maximum number of connections to open to the CVR database. This is divided evenly amongst sync workers.

Note that this number must allow for at least one connection per sync worker, or zero-cache will fail to start. See num-sync-workers.

flag: `--cvr-max-conns`env: `ZERO_CVR_MAX_CONNS`default: `30`

### Enable Query Planner

Enable the query planner for optimizing ZQL queries.

The query planner analyzes and optimizes query execution by determining the most efficient join strategies.

You can disable the planner if it is picking bad strategies.

flag: `--enable-query-planner`env: `ZERO_ENABLE_QUERY_PLANNER`default: `true`

### Enable CRUD Mutations

Enables support for legacy CRUD mutations. When this is `false`, view-syncers do not connect to the upstream database for CRUD writes, and push messages with CRUD mutations return an error response.

flag: `--enable-crud-mutations`env: `ZERO_ENABLE_CRUD_MUTATIONS`default: `true`

### Enable Telemetry

Zero collects anonymous telemetry data to help us understand usage. We collect:

* Zero version
* Uptime
* General machine information, like the number of CPUs, OS, CI/CD environment, etc.
* Information about usage, such as number of queries or mutations processed per hour.

This is completely optional and can be disabled at any time. You can also opt-out by setting `DO_NOT_TRACK=1`.

flag: `--enable-telemetry`env: `ZERO_ENABLE_TELEMETRY`default: `true`

### Initial Sync Table Copy Workers

The number of parallel workers used to copy tables during initial sync. Each worker uses a database connection, copies a single table at a time, and buffers up to (approximately) 10 MB of table data in memory during initial sync. Increasing the number of workers may improve initial sync speed; however, local disk throughput (IOPS), upstream CPU, and network bandwidth may also be bottlenecks.

flag: `--initial-sync-table-copy-workers`env: `ZERO_INITIAL_SYNC_TABLE_COPY_WORKERS`default: `5`

### Lazy Startup

Delay starting the majority of zero-cache until first request.

This is mainly intended to avoid connecting to Postgres replication stream until the first request is received, which can be useful i.e., for preview instances.

Currently only supported in single-node mode.

flag: `--lazy-startup`env: `ZERO_LAZY_STARTUP`default: `false`

### Litestream Backup URL

The location of the litestream backup, usually an s3:// URL. This is only consulted by the replication-manager. view-syncers receive this information from the replication-manager.

In multi-node deployments, this is required on the replication-manager so view-syncers can reserve snapshots; in single-node deployments it is optional.

flag: `--litestream-backup-url`env: `ZERO_LITESTREAM_BACKUP_URL`

### Litestream Endpoint

The S3-compatible endpoint URL to use for the litestream backup. This is only required for non-AWS services. The replication-manager and view-syncers must have the same endpoint.

For example, to use Cloudflare R2: `https://<id>.r2.cloudflarestorage.com`.

flag: `--litestream-endpoint`env: `ZERO_LITESTREAM_ENDPOINT`

### Litestream Checkpoint Threshold MB

The size of the WAL file at which to perform an SQlite checkpoint to apply the writes in the WAL to the main database file. Each checkpoint creates a new WAL segment file that will be backed up by litestream. Smaller thresholds may improve read performance, at the expense of creating more files to download when restoring the replica from the backup.

flag: `--litestream-checkpoint-threshold-mb`env: `ZERO_LITESTREAM_CHECKPOINT_THRESHOLD_MB`default: `40`

### Litestream Config Path

Path to the litestream yaml config file. zero-cache will run this with its environment variables, which can be referenced in the file via `${ENV}` substitution, for example:

* ZERO\_REPLICA\_FILE for the db Path
* ZERO\_LITESTREAM\_BACKUP\_LOCATION for the db replica url
* ZERO\_LITESTREAM\_LOG\_LEVEL for the log Level
* ZERO\_LOG\_FORMAT for the log type

flag: `--litestream-config-path`env: `ZERO_LITESTREAM_CONFIG_PATH`default: `./src/services/litestream/config.yml`

### Litestream Executable

Path to the litestream executable. This must be built from the `rocicorp/litestream` fork. This option has no effect if litestream-backup-url is unspecified.

flag: `--litestream-executable`env: `ZERO_LITESTREAM_EXECUTABLE`

### Litestream V5 Executable

Path to the official Litestream v0.5.x executable used for restores when `ZERO_LITESTREAM_RESTORE_USING_V5` is enabled. Litestream v0.5.8 and later can restore both legacy WAL backups and LTX backups, choosing the format with the latest data. The official Zero Docker image includes Litestream 0.5.15 at this path.

flag: `--litestream-executable-v5`env: `ZERO_LITESTREAM_EXECUTABLE_V5`

### Litestream Restore Using V5

Use `ZERO_LITESTREAM_EXECUTABLE_V5` for restores when that executable is configured. If it is unavailable, Zero falls back to the legacy executable. Set this to `false` to force legacy restore behavior.

Litestream v0.5 cannot restore legacy backups encrypted with Age. Keep legacy restore enabled for those backups or migrate them before enabling v5 restore.

flag: `--litestream-restore-using-v5`env: `ZERO_LITESTREAM_RESTORE_USING_V5`default: `true`

### Litestream Backup Using V5

Write LTX backups with Litestream v0.5.x. This is disabled by default to continue writing legacy WAL backups. Enabling it requires v5 restore and makes rollback difficult because older versions cannot restore an LTX-only backup.

flag: `--litestream-backup-using-v5`env: `ZERO_LITESTREAM_BACKUP_USING_V5`default: `false`

### Litestream Incremental Backup Interval Minutes

The interval between incremental backups of the replica. Shorter intervals reduce the amount of change history that needs to be replayed when catching up a new view-syncer, at the expense of increasing the number of files needed to download for the initial litestream restore.

flag: `--litestream-incremental-backup-interval-minutes`env: `ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_MINUTES`default: `15`

### Litestream Maximum Checkpoint Page Count

The WAL page count at which SQLite performs a RESTART checkpoint, which blocks writers until complete. Defaults to `minCheckpointPageCount * 10`. Set to `0` to disable RESTART checkpoints entirely.

flag: `--litestream-max-checkpoint-page-count`env: `ZERO_LITESTREAM_MAX_CHECKPOINT_PAGE_COUNT`default: `minCheckpointPageCount * 10`

### Litestream Minimum Checkpoint Page Count

The WAL page count at which SQLite attempts a PASSIVE checkpoint, which transfers pages to the main database file without blocking writers. Defaults to `checkpointThresholdMB * 250` (since SQLite page size is 4KB).

flag: `--litestream-min-checkpoint-page-count`env: `ZERO_LITESTREAM_MIN_CHECKPOINT_PAGE_COUNT`default: `checkpointThresholdMB * 250`

### Litestream Multipart Concurrency

The number of parts (of size --litestream-multipart-size bytes) to upload or download in parallel when backing up or restoring the snapshot.

flag: `--litestream-multipart-concurrency`env: `ZERO_LITESTREAM_MULTIPART_CONCURRENCY`default: `48`

### Litestream Multipart Size

The size of each part when uploading or downloading the snapshot with `--litestream-multipart-concurrency`. Note that up to `concurrency * size`bytes of memory are used when backing up or restoring the snapshot.

flag: `--litestream-multipart-size`env: `ZERO_LITESTREAM_MULTIPART_SIZE`default: `16777216` (16 MiB)

### Litestream Log Level

flag: `--litestream-log-level`env: `ZERO_LITESTREAM_LOG_LEVEL`default: `warn`values: `debug`, `info`, `warn`, `error`

### Litestream Port

Port on which litestream exports metrics, used to determine the replication watermark up to which it is safe to purge change log records.

flag: `--litestream-port`env: `ZERO_LITESTREAM_PORT`default: `--port + 2`

### Litestream Region

The AWS region for the litestream backup bucket. Required for non-standard AWS partitions (e.g. GovCloud `us-gov-west-1`) where Litestream cannot auto-detect the region. The replication-manager and view-syncers must have the same region.

flag: `--litestream-region`env: `ZERO_LITESTREAM_REGION`

### Litestream Restore Parallelism

The number of WAL files to download in parallel when performing the initial restore of the replica from the backup.

flag: `--litestream-restore-parallelism`env: `ZERO_LITESTREAM_RESTORE_PARALLELISM`default: `48`

### Litestream Snapshot Backup Interval Hours

The interval between snapshot backups of the replica. Snapshot backups make a full copy of the database to a new litestream generation. Zero retains the previous generation for six additional hours so an active restore can finish before its snapshot and WAL files are removed. This improves restore time and safety at the expense of bandwidth and temporary backup storage. Applications with a large database and low write rate can increase this interval to reduce network usage for backups (litestream defaults to 24 hours).

flag: `--litestream-snapshot-backup-interval-hours`env: `ZERO_LITESTREAM_SNAPSHOT_BACKUP_INTERVAL_HOURS`default: `12`

### Log Format

Use text for developer-friendly console logging and json for consumption by structured-logging services.

flag: `--log-format`env: `ZERO_LOG_FORMAT`default: `"text"`values: `text`, `json`

### Log IVM Sampling

How often to collect IVM metrics. 1 out of N requests will be sampled where N is this value.

flag: `--log-ivm-sampling`env: `ZERO_LOG_IVM_SAMPLING`default: `5000`

### Log Level

Sets the logging level for the application.

flag: `--log-level`env: `ZERO_LOG_LEVEL`default: `"info"`values: `debug`, `info`, `warn`, `error`

### Log Slow Hydrate Threshold

The number of milliseconds a query hydration must take to print a slow warning.

flag: `--log-slow-hydrate-threshold`env: `ZERO_LOG_SLOW_HYDRATE_THRESHOLD`default: `100`

### Log Slow Row Threshold

The number of ms a row must take to fetch from table-source before it is considered slow.

flag: `--log-slow-row-threshold`env: `ZERO_LOG_SLOW_ROW_THRESHOLD`default: `2`

### Mutate API Key

An optional secret used to authorize zero-cache to call the API server handling writes. This is sent from zero-cache to your mutate endpoint in an `X-Api-Key` header.

flag: `--mutate-api-key`env: `ZERO_MUTATE_API_KEY`

### Mutate Allowed Client Headers

Comma-separated allowlist of client-provided custom headers to forward to your mutate endpoint. Header names are matched case-insensitively.

By default, no client-provided custom headers are forwarded.

flag: `--mutate-allowed-client-headers`env: `ZERO_MUTATE_ALLOWED_CLIENT_HEADERS`default: `none`

### Mutate Allowed Request Headers

Comma-separated allowlist of HTTP headers from the request that opened the WebSocket to forward to your mutate endpoint. Use this for proxy- or load-balancer-injected headers such as `x-forwarded-for` or `cf-ray`.

Unlike [mutate allowed client headers](#mutate-allowed-client-headers), these values come from the request that established the connection. Header names are matched case-insensitively.

Values are retained for the WebSocket's lifetime, so clients must reconnect to receive changes. The allowlist does not verify the header source - only allow headers that a trusted proxy overwrites or removes from untrusted requests.

No request headers are forwarded by default.

flag: `--mutate-allowed-request-headers`env: `ZERO_MUTATE_ALLOWED_REQUEST_HEADERS`default: `none`

### Mutate Forward Cookies

If true, zero-cache will forward cookies from the request to zero-cache to your mutate endpoint. This is useful for passing authentication cookies to the API server. If false, cookies are not forwarded.

flag: `--mutate-forward-cookies`env: `ZERO_MUTATE_FORWARD_COOKIES`default: `false`

### Mutate URL

The URL of the API server to which zero-cache will push mutations. URLs are matched using URLPattern, a standard Web API.

Pattern syntax (similar to Express routes):

* Exact URL match: `"https://api.example.com/mutate"`
* Any subdomain using wildcard: `"https://*.example.com/mutate"`
* Multiple subdomain levels: `"https://*.*.example.com/mutate"`
* Any path under a domain: `"https://api.example.com/*"`
* Named path parameters: `"https://api.example.com/:version/mutate"`
  * Matches `https://api.example.com/v1/mutate`, `https://api.example.com/v2/mutate`, etc.

Advanced patterns:

* Optional path segments: `"https://api.example.com/:path?"`
* Regex in segments (for specific patterns): `"https://api.example.com/:version(v\\d+)/mutate"` matches only `v` followed by digits.

Multiple patterns can be specified, for example:

* `https://api1.example.com/mutate,https://api2.example.com/mutate`

Query parameters and URL fragments (`#`) are ignored during matching. See [URLPattern](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) for full syntax.

flag: `--mutate-url`env: `ZERO_MUTATE_URL`

### Number of Sync Workers

The number of processes to use for view syncing. Leave this unset to use `max(1, availableParallelism() - 1)`, reserving one core for the replicator. If set to `0`, the server runs without sync workers, which is the configuration for running the replication-manager in multi-node deployments.

flag: `--num-sync-workers`env: `ZERO_NUM_SYNC_WORKERS`

### Per User Mutation Limit Max

The maximum mutations per user within the specified windowMs.

flag: `--per-user-mutation-limit-max`env: `ZERO_PER_USER_MUTATION_LIMIT_MAX`

### Per User Mutation Limit Window (ms)

The sliding window over which the perUserMutationLimitMax is enforced.

flag: `--per-user-mutation-limit-window-ms`env: `ZERO_PER_USER_MUTATION_LIMIT_WINDOW_MS`default: `60000`

### PG Replication Slot Failover

For upstream Postgres 17+, creates replication slots with the `failover` flag enabled so they can be synchronized to a standby and survive a failover. This requires additional Postgres-side configuration on your provider; see [High Availability](https://zero.rocicorp.dev/docs/connecting-to-postgres#high-availability). Has no effect on Postgres versions before 17.

flag: `--upstream-pg-replication-slot-failover`env: `ZERO_UPSTREAM_PG_REPLICATION_SLOT_FAILOVER`default: `false`

### Port

The port for sync connections.

flag: `--port`env: `ZERO_PORT`default: `4848`

### Query API Key

An optional secret used to authorize zero-cache to call the API server handling queries. This is sent from zero-cache to your query endpoint in an `X-Api-Key` header.

flag: `--query-api-key`env: `ZERO_QUERY_API_KEY`

### Query Allowed Client Headers

Comma-separated allowlist of client-provided custom headers to forward to your query endpoint. Header names are matched case-insensitively.

By default, no client-provided custom headers are forwarded.

flag: `--query-allowed-client-headers`env: `ZERO_QUERY_ALLOWED_CLIENT_HEADERS`default: `none`

### Query Allowed Request Headers

Comma-separated allowlist of HTTP headers from the request that opened the WebSocket to forward to your query endpoint. Use this for proxy- or load-balancer-injected headers such as `x-forwarded-for` or `cf-ray`.

Unlike [query allowed client headers](#query-allowed-client-headers), these values come from the request that established the connection. Header names are matched case-insensitively.

Values are retained for the WebSocket's lifetime, so clients must reconnect to receive changes. The allowlist does not verify the header source - only allow headers that a trusted proxy overwrites or removes from untrusted requests.

No request headers are forwarded by default.

flag: `--query-allowed-request-headers`env: `ZERO_QUERY_ALLOWED_REQUEST_HEADERS`default: `none`

### Query Forward Cookies

If true, zero-cache will forward cookies from the request to zero-cache to your query endpoint. This is useful for passing authentication cookies to the API server. If false, cookies are not forwarded.

flag: `--query-forward-cookies`env: `ZERO_QUERY_FORWARD_COOKIES`default: `false`

### Query Hydration Stats

Track and log the number of rows considered by query hydrations which take longer than **log-slow-hydrate-threshold** milliseconds.

This is useful for debugging and performance tuning.

flag: `--query-hydration-stats`env: `ZERO_QUERY_HYDRATION_STATS`

### Query URL

The URL of the API server to which zero-cache will send synced queries. URLs are matched using URLPattern, a standard Web API.

Pattern syntax (similar to Express routes):

* Exact URL match: `"https://api.example.com/query"`
* Any subdomain using wildcard: `"https://*.example.com/query"`
* Multiple subdomain levels: `"https://*.*.example.com/query"`
* Any path under a domain: `"https://api.example.com/*"`
* Named path parameters: `"https://api.example.com/:version/query"`
  * Matches `https://api.example.com/v1/query`, `https://api.example.com/v2/query`, etc.

Advanced patterns:

* Optional path segments: `"https://api.example.com/:path?"`
* Regex in segments (for specific patterns): `"https://api.example.com/:version(v\\d+)/query"` matches only `v` followed by digits.

Multiple patterns can be specified, for example:

* `https://api1.example.com/query,https://api2.example.com/query`

Query parameters and URL fragments (`#`) are ignored during matching. See [URLPattern](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) for full syntax.

flag: `--query-url`env: `ZERO_QUERY_URL`

### Replica File

File path to the SQLite replica that zero-cache maintains. This can be lost, but if it is, zero-cache will have to re-replicate next time it starts up.

flag: `--replica-file`env: `ZERO_REPLICA_FILE`default: `"zero.db"`

### Replica Vacuum Interval Hours

Performs a VACUUM at server startup if the specified number of hours has elapsed since the last VACUUM (or initial-sync). The VACUUM operation is heavyweight and requires double the size of the db in disk space. If unspecified, VACUUM operations are not performed.

flag: `--replica-vacuum-interval-hours`env: `ZERO_REPLICA_VACUUM_INTERVAL_HOURS`

### Replication Lag Report Interval (ms)

The minimum interval at which replication lag reports are written upstream and reported via the `zero.replication.total_lag` [OpenTelemetry metric](https://zero.rocicorp.dev/docs/otel). If an expected report is not received before the next interval, Zero emits a new report and increments `zero.replication.lag_report_retries`.

This feature requires write access to upstream Postgres (uses `pg_logical_emit_message()`). For PostgreSQL 17+, lag measurements accurately reflect committed write latency (single-digit milliseconds). For PostgreSQL 16 and earlier, measurements may appear 50-100ms longer due to flush behavior. A negative or 0 value disables lag reporting.

Even if otel is not enabled, info and warn-level logs are emitted for large lag values.

flag: `--replication-lag-report-interval-ms`env: `ZERO_REPLICATION_LAG_REPORT_INTERVAL_MS`default: `30_000`

### Server Version

The version string outputted to logs when the server starts up.

flag: `--server-version`env: `ZERO_SERVER_VERSION`

### Shadow Sync Enabled

Periodically exercises the initial-sync code path against a sample of rows from every published table, writing to a throwaway SQLite database. This acts as a canary: if the real initial-sync path breaks because of schema drift, Postgres version quirks, or another full-resync issue, the shadow run fails before a customer actually needs a full reset.

flag: `--shadow-sync-enabled`env: `ZERO_SHADOW_SYNC_ENABLED`default: `false`

### Shadow Sync Interval Hours

The interval between shadow initial-sync runs, in hours. The first run fires within `[2/3, 1)` of this interval after startup, so the canary completes at least once per task lifetime while still jittering fleet restarts.

flag: `--shadow-sync-interval-hours`env: `ZERO_SHADOW_SYNC_INTERVAL_HOURS`default: `12`

### Shadow Sync Sample Rate

The Bernoulli sampling rate for each table, where `0 < rate <= 1`. A value of `1` disables sampling and copies all rows, still subject to `--shadow-sync-max-rows-per-table`.

flag: `--shadow-sync-sample-rate`env: `ZERO_SHADOW_SYNC_SAMPLE_RATE`default: `0.1`

### Shadow Sync Max Rows Per Table

The hard upper bound on rows copied per table per shadow run. This guards against unexpectedly large tables consuming too much disk or upstream bandwidth.

flag: `--shadow-sync-max-rows-per-table`env: `ZERO_SHADOW_SYNC_MAX_ROWS_PER_TABLE`default: `10000`

### Storage DB Temp Dir

Temporary directory for IVM operator storage. Leave unset to use `os.tmpdir()`.

flag: `--storage-db-tmp-dir`env: `ZERO_STORAGE_DB_TMP_DIR`

### Task ID

Globally unique identifier for the zero-cache instance. Setting this to a platform specific task identifier can be useful for debugging. If unspecified, zero-cache will attempt to extract the TaskARN if run from within an AWS ECS container, and otherwise use a random string.

flag: `--task-id`env: `ZERO_TASK_ID`

### Upstream Max Connections

The maximum number of connections to open to the upstream database for committing mutations. This is divided evenly amongst sync workers. In addition to this number, zero-cache uses one connection for the replication stream.

Note that this number must allow for at least one connection per sync worker, or zero-cache will fail to start. See num-sync-workers.

flag: `--upstream-max-conns`env: `ZERO_UPSTREAM_MAX_CONNS`default: `20`

### Upstream PG Replication Slot Failover

For upstream PostgreSQL 17 and later, create replication slots with the `failover` parameter set to `true` to enable slot synchronization and failover. Additional Postgres-level configuration is required when enabling this option. This option has no effect for PostgreSQL versions before 17.

See the PostgreSQL docs for details: [https://www.postgresql.org/docs/current/logicaldecoding-explanation.html#LOGICALDECODING-REPLICATION-SLOTS-SYNCHRONIZATION](https://www.postgresql.org/docs/current/logicaldecoding-explanation.html#LOGICALDECODING-REPLICATION-SLOTS-SYNCHRONIZATION)

flag: `--upstream-pg-replication-slot-failover`env: `ZERO_UPSTREAM_PG_REPLICATION_SLOT_FAILOVER`default: `false`

### Upstream PG Stream Inbound Timeout

The time, in milliseconds, without an inbound message from the upstream WAL sender after which `zero-cache` tears down the replication stream to force a reconnect. By default, the threshold is twice the server's `wal_sender_timeout`.

Increase this value when a healthy WAL sender can remain silent while decoding unpublished WAL or assembling a large transaction. This changes only Zero's inbound timeout; keepalive timing remains derived from `wal_sender_timeout`. The option has no effect when `wal_sender_timeout` is `0`, which disables inbound liveness detection.

See [WAL Sender Timeout](https://zero.rocicorp.dev/docs/connecting-to-postgres#wal-sender-timeout).

flag: `--upstream-pg-stream-inbound-timeout-ms`env: `ZERO_UPSTREAM_PG_STREAM_INBOUND_TIMEOUT_MS`

### Websocket Compression

Enable WebSocket per-message deflate compression. Compression can reduce bandwidth usage for sync traffic but increases CPU usage on both client and server. Disabled by default. See: [https://github.com/websockets/ws#websocket-compression](https://github.com/websockets/ws#websocket-compression)

flag: `--websocket-compression`env: `ZERO_WEBSOCKET_COMPRESSION`default: `false`

### Websocket Compression Options

JSON string containing WebSocket compression options. Only used if websocket-compression is enabled. Example: `{"zlibDeflateOptions":{"level":3},"threshold":1024}`. See [https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback](https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback) for available options.

flag: `--websocket-compression-options`env: `ZERO_WEBSOCKET_COMPRESSION_OPTIONS`

### Websocket Max Payload Bytes

Maximum size of incoming WebSocket messages in bytes. Messages exceeding this limit are rejected before parsing.

flag: `--websocket-max-payload-bytes`env: `ZERO_WEBSOCKET_MAX_PAYLOAD_BYTES`default: `10485760` (10 MiB)

### Yield Threshold (ms)

The maximum amount of time in milliseconds that a sync worker will spend in IVM (processing query hydration and advancement) before yielding to the event loop. Lower values increase responsiveness and fairness at the cost of reduced throughput.

flag: `--yield-threshold-ms`env: `ZERO_YIELD_THRESHOLD_MS`default: `10`

## Deprecated Flags

### Auth JWK

A public key in JWK format used to verify JWTs. Only one of jwk, jwksUrl and secret may be set.

flag: `--auth-jwk`env: `ZERO_AUTH_JWK`

### Auth JWKS URL

A URL that returns a JWK set used to verify JWTs. Only one of jwk, jwksUrl and secret may be set.

flag: `--auth-jwks-url`env: `ZERO_AUTH_JWKS_URL`

### Auth Secret

A symmetric key used to verify JWTs. Only one of jwk, jwksUrl and secret may be set.

flag: `--auth-secret`env: `ZERO_AUTH_SECRET`

**For AI agents**: to view all the available documentation, visit https://zero.rocicorp.dev/llms.txt