Deployments
View as Markdownmz-deploy gives you a complete lifecycle from local development through
production deployment:
compile ──▶ test ──▶ dev ──▶ stage ──▶ wait ──▶ promote
local local real env real env
compile and
test run
locally to catch errors fast.
dev builds a per-developer overlay
against real production data so you can validate behavior before staging.
When ready, a deployer runs stage, wait, and promote to ship to
production with zero downtime.
Set up deployment tracking
mz-deploy setup
This creates the _mz_deploy database, its tracking tables, three roles for
access control, and the deployment server cluster that every mz-deploy
connection runs against. The command is idempotent — you can safely run it
again without side effects.
When RBAC is enabled,
setup must be run by a superuser. It grants CREATEDB and
CREATECLUSTER on the system to the deploy roles, and only a superuser can
grant system privileges while RBAC is enforced. On clusters with RBAC
disabled, the check is skipped. Once setup completes, the deployer,
developer, and monitor roles use mz-deploy without any further superuser
involvement.
Roles
setup creates three roles that control who can do what:
| Role | Commands |
|---|---|
materialize_deployer |
delete, stage, promote, abort — full write access |
materialize_developer |
dev, list, describe, log |
materialize_monitor |
list, describe, log — read-only deployment state |
Membership is enforced when the command connects: stage, promote,
abort, and delete require materialize_deployer; dev requires
materialize_developer; and list, describe, and log accept any of the
three roles. apply provisions infrastructure (clusters, secrets,
connections, sources, tables) and is not currently restricted by role.
Your database user must be a member of one of these roles to run commands that connect to the database. Grant the appropriate role to each user:
GRANT materialize_deployer TO deploy_bot;
GRANT materialize_developer TO dev_user;
compile and test do not require an mz-deploy role because they run locally.
Deploy to staging
mz-deploy stage
stage compiles the project, diffs against the last promoted snapshot, and
deploys only changed objects to staging schemas with suffixed names (for example,
public_a1b2c3d).
The deploy ID defaults to the current git SHA prefix. To override it:
mz-deploy stage --deploy-id my-feature
Preview what would be staged without making changes:
mz-deploy stage --dry-run
Allow staging with uncommitted changes:
mz-deploy stage --allow-dirty
Common errors during staging:
- Deploy ID already exists — abort the existing deployment with
mz-deploy abort <id>or choose a different--deploy-id. - Uncommitted changes — commit your changes or pass
--allow-dirty.
Iterate against production data
dev deploys a personal, throwaway copy of your changes to your remote
Materialize so you can validate them against real production data. It
creates a per-developer overlay database (<db>__<profile>) containing
only the views and materialized views you’ve changed, with references
rewritten so unchanged dependencies resolve to production. External
dependencies pass through unchanged.
You pass the cluster every overlay materialized view and index runs on;
the IN CLUSTER clause in your source is rewritten to it. dev refuses
to target a cluster that hosts a promoted deployment, so provision a
dedicated dev cluster and reuse it:
mz-deploy dev <cluster>
Every run drops the overlay and rebuilds it from scratch, so there’s no state to manage.
Show the plan without executing any DDL:
mz-deploy dev <cluster> --dry-run
Tear down the overlay when you’re done:
mz-deploy dev --down
dev requires the materialize_developer role. setup grants the
CREATEDB system privilege to that role, so members inherit it
automatically. Tables, sources, sinks, connections, and secrets are
silently skipped — dev only overlays views and materialized views.
stage |
dev |
|
|---|---|---|
| Required role | materialize_deployer |
materialize_developer |
| Target | Staging schemas alongside production | Per-developer overlay database |
| Git dirty check | Yes | No |
| Object types | All project objects | Views and materialized views only |
| Can be promoted | Yes | No |
Wait for hydration
mz-deploy wait <deploy-id>
This monitors cluster hydration and displays a live dashboard. The possible statuses are:
| Status | Meaning |
|---|---|
| ready | Fully hydrated and caught up |
| hydrating | Objects still being materialized |
| lagging | Hydrated but lag exceeds the --allowed-lag threshold |
| failing | No healthy replicas (possible OOM) |
Flags:
--timeout <seconds>— maximum time to wait before exiting with an error.--allowed-lag <seconds>— lag threshold for the lagging status (default: 300).
Common errors during hydration:
- Timeout — increase
--timeoutor check cluster health. - “failing” status — check cluster sizing; replicas may need more resources.
Promote to production
mz-deploy promote <deploy-id>
This atomically swaps staging schemas into production. promote automatically
runs a readiness check before proceeding.
Preview what would change:
mz-deploy promote <deploy-id> --dry-run
Flags:
--force— skip conflict detection.--no-ready-check— skip the automatic readiness check.--allowed-lag <SECONDS>— maximum wallclock lag a cluster may have and still pass the readiness check. Clusters lagging further block promotion. Defaults to 300 (5 minutes).--dry-run— preview the promotion without applying changes.
promote detects the conflict and
aborts. Re-run mz-deploy stage to pick up the latest production state before
promoting.
Manage deployments
List active staging deployments (similar to git branch):
mz-deploy list
View promotion history (similar to git log):
mz-deploy log
Clean up a staging deployment:
mz-deploy abort <deploy-id>
View deployment details:
mz-deploy describe <deploy-id>
Day-two operations
Making changes
mz-deploy uses a diff-based model. When you change a SQL file and re-stage,
only the modified objects and their dependents are redeployed.
For example, to change stalled_orders from a 30-minute threshold to 1 hour,
update the SQL file:
-- models/materialize/public/stalled_orders.sql
CREATE MATERIALIZED VIEW stalled_orders
IN CLUSTER orders AS
SELECT
id,
customer,
amount,
created_at,
updated_at,
mz_now() - updated_at AS stalled_for
FROM orders
WHERE status = 'pending'
AND updated_at < mz_now() - INTERVAL '1 hour';
When you re-stage, only stalled_orders and its dependents are redeployed.
Deleting objects
Use mz-deploy delete to drop objects. The command drops without CASCADE and
requires confirmation:
mz-deploy delete cluster orders
Pass --yes to skip the confirmation prompt.
Supported types: cluster, connection, network-policy, role, secret,
source, table.
Stable API schemas
If other teams depend on your materialized views, you can mark schemas as stable API boundaries so that deployments never break downstream consumers. See Stable APIs for details.