What runs where
Deployment tooling owns maintenance, workload shutdown, infrastructure, and restart. The release image owns database migrations and authorization preparation. Neither the application nor the chart maintains an activation receipt, a migration authority switch, or a second authorization engine for this process.formbricks-authzed-prepare is a preparation command, not an unattended installer. It does not
discover or stop writers, suspend controllers, take backups, or restart workloads. Its confirmations are
operator acknowledgements, not proof that maintenance is active. Complete and verify those steps first.
The command reuses the image’s existing migration runner and formbricks-authzed upgrade prepare and
upgrade check implementations. It does not automatically prune unexplained relationships.
Before the maintenance window
- Rehearse with a restored copy of the installation. Measure migrations, backfill, verification, and restoration time; use those measurements to communicate the maintenance window.
- Pin the target Formbricks image by digest and the matching chart/Compose files. Preserve the current images, chart, values, secrets, and volume names. Do not combine this change with a PostgreSQL major upgrade, a storage migration, or unrelated Hub/Cube changes.
- Prepare private SpiceDB connectivity, dedicated datastore credentials, its reviewed version, and completed datastore migrations. Retain existing operator ownership; never install a second operator over an existing cluster-wide operator. See SpiceDB operations.
Perform the upgrade
1
Enter and verify maintenance
Serve maintenance at the ingress/reverse proxy and stop accepting new work. Suspend the identified
controllers and schedules, stop all writers, and wait for in-flight work and database transactions
to finish. Suspending a CronJob does not stop an already-running Job. Scaling a Deployment to zero
is insufficient if an autoscaler or GitOps controller can restore it.Verify there are no remaining writer pods, containers, Jobs, external scripts, or active writer
transactions. Keep only required infrastructure and the dedicated preparation process running.
Keep maintenance active through preparation, restart, and access verification.
2
Take a recoverable backup
Take consistent backups after writers stop, including the application database, existing SpiceDB
datastore, and any other datastore changed by the selected release. Retain secrets, configuration,
storage state, and exact source images. Confirm the restore procedure has been tested.Online backups taken earlier are useful but are not a substitute for a known recovery point that
includes the final writes accepted before maintenance.
3
Prepare using the target image
Run the command below using the exact target image and the same application database and AuthZed
configuration that v6 will use. It first validates configuration and authenticated SpiceDB health,
then runs application migrations, schema preparation, outbox drain, relationship repair, and a
separate full verification. A non-empty, different SpiceDB schema is not overwritten automatically. Review
AUTHZED_CONSISTENCY must be fully_consistent.schema check and
the release’s schema changes, then repeat with --expected-current-digest sha256:<reviewed-digest>.
Only one preparation/schema-writing process may run at a time.4
Deploy and test v6 while maintenance remains active
Only after preparation exits
0, deploy the same image digest. Retain fully_consistent and the
same datastore bindings. With Helm, set authzed.migrationAcknowledged=true; this is an explicit
operator acknowledgement, not a database activation record. The upgrade hook only verifies the
prepared graph; it does not apply schemas or backfill while old workloads are running.Verify the actual pod/container image IDs. Through restricted access, test an allowed user, a
denied user, a scoped API key, organization/workspace lists, and a grant revocation. Verify outbox
delivery, rerun formbricks-authzed upgrade check, and check operational-error metrics. /health
alone does not establish authorization readiness.5
Resume service
Restore controller and worker settings only after their desired manifests point to the tested v6
release. Remove maintenance after the checks pass. Monitor authorization errors, outbox age,
revocation delays, and scheduled relationship audits. Keep backups until upgrade acceptance ends.
Docker and one-click
Keep the same Compose project name, database images, and volumes. Prepare a target Compose file that retains customized SMTP, storage, networking, and Traefik settings. Pinformbricks,
formbricks-migrate, authzed-ops, and authzed-initialize to the same target image digest. Do not copy a
fresh-install Compose file over an existing installation without reviewing those differences.
After stopping the old project’s writers and verifying external writers/controllers are stopped,
start only the required infrastructure from the reviewed target configuration. Never use down -v.
With infrastructure already healthy, run the existing operations service without starting dependencies:
--project-name, --env-file, and -f options when customized. The operator service
must receive the application database URL, migration credentials when different, and the six AuthZed
client variables. The distributed default uses the bundled PostgreSQL database; customize it when your
installation uses a managed database. No database or SpiceDB port needs public exposure.
After successful preparation, a one-click installation also sets
FORMBRICKS_AUTHZED_V6_MIGRATION_ACKNOWLEDGED=true before its explicit v6 update. Do not invoke an
unattended update or move a floating image tag until the release-specific installation tests pass.
Helm and GitOps
Prepare SpiceDB before the application upgrade. Keep the current database/storage settings and existing Secrets. During maintenance, use an explicit, temporary Kubernetes Job with the target Formbricks image, the same database/AuthZed environment bindings, and this command:DATABASE_URL or AuthZed values that it normally injects separately. Prefer
explicit valueFrom.secretKeyRef bindings to existing Secrets when possible. Never put secrets on a
command line or commit their values.
Wait for Job completion and inspect its sanitized result before running helm upgrade or resuming an
Argo CD/Flux reconciliation. Preparation is not a Helm migration hook or a permanent chart component.
Delete the Job and any temporary environment Secret after acceptance; retain only sanitized evidence.
There is no activation table to clean up.
Results and recovery
Success prints one JSON result and exits0:
2, without a restartRequired field. It never authorizes restarting workloads,
including when arguments were rejected before any work ran. The code identifies the failed step:
Each subprocess has a 30-minute limit. If the rehearsal exceeds it, budget a longer maintenance window
and run the existing migration and operational commands individually under a reviewed Job deadline.
Interrupted containers can exit with a signal code rather than JSON; any nonzero exit remains blocked.
The command never restarts a workload on success, failure, or interruption. Retry preparation with the
same image after correcting the cause, provided the interrupted migration is safe to rerun. Never mark
a failed Prisma migration applied without investigating it.
Diagnose migration failures privately
The wrapper deliberately does not offer raw output in shared Job logs: migration errors can contain credentials, SQL, or customer identifiers. In a restricted PostgreSQL operator session, inspect failed Prisma migration records in_prisma_migrations (migration_name, started_at, finished_at,
rolled_back_at, and logs). Treat logs as sensitive; do not paste it into CI, tickets, or Slack.
For failures before Prisma records the attempt, check connectivity and permissions in that restricted
session. If a migration must be retried, first establish whether partial changes need repair, then use
the target image’s node /home/nextjs/packages/database/dist/scripts/apply-migrations.js under maintenance.
Capture any diagnostic output only in operator-controlled storage; never enable it on normal startup.
After database changes, helm rollback or changing the image is not necessarily a safe rollback.
Remain in maintenance and either complete the v6 preparation or restore the coordinated backup and
matching v5 images/configuration. Resuming writes before restoration risks losing those new writes.
Later v6 upgrades
The same read-onlyupgrade check gate also applies to v6.x updates. With an unchanged canonical
SpiceDB schema and a clean graph/outbox, no additional authorization preparation is needed. One-click
stops the stack before application migrations and starts it only after they succeed. Helm keeps its
existing application-migration lifecycle; follow the target release’s compatibility and maintenance notes.
If a release changes the canonical SpiceDB schema, this flow deliberately blocks the upgrade until
explicit maintenance preparation has completed with that target image and a reviewed current digest.
Use the procedure above again; the persistent acknowledgement is not permission to skip preparation.
An online, backward-compatible schema rollout would need its own reviewed and tested release procedure;
neither the Helm hook nor the one-click updater infers that a schema change is safe while writers run.