Skip to main content

Backups And Restore

The public backup surface protects PostgreSQL only. It does not include object storage, RabbitMQ, application/source images, logs, generated builds, extensions, projects/workspaces on the host, TLS material, installer state, or provider-side effects.

The checkout commands below use the selected installation's Docker/Compose context. The host ./flow-steward launcher uses checkout backups/ for both Compact and Scalable. Native flow-steward inside a Compact runtime uses /data/backups/ instead; these are separate stores. The host restore helper requires Compose. Flow Steward has no supported restore procedure on a provider without Compose, such as a standalone Railway container. Keep that provider's own database backups; do not apply the host helper inside its container.

Keep the correct key​

Encrypted backups require FS_BACKUP_ENCRYPTION_KEY. This is not FS_SECRET_KEY, ENCRYPTION_KEY, a database password, or an authentication token. Preserve the exact backup key separately and securely; do not print it in logs or copy it into a change record.

Create, list, and verify​

./flow-steward backup create
./flow-steward backup list
./flow-steward backup verify backups/<file>.dump.enc

backup create writes an owner-only encrypted PostgreSQL custom-format dump under the canonical backups/ directory, decrypts it to an owner-only temporary file, and runs non-mutating pg_restore -l before publishing it. backup verify repeats the envelope/path and catalog checks without modifying the database.

ContextPublic create/list storageVerification path
Checkout ScalableDeployment checkout backups/ on the host, reached through the installed CLI's checkout mount.backups/<file>.dump.enc
Checkout Compact, host ./flow-steward launcherDeployment checkout backups/ on the host, reached through the launcher's checkout mount.backups/<file>.dump.enc through the host launcher.
Native Compact flow-steward inside the runtime/data/backups/ in the Compact durable volume.backups/<file>.dump.enc through the native command.
Standalone provider Compact/data/backups/ inside its mounted durable volume. Use native flow-steward backup create/list/verify in the provider console.Container-native path; exporting the file requires provider volume/file controls.

Use the filename returned by backup create and confirm it appears in backup list. Keep an encrypted off-installation copy; a durable volume alone is not an independent recovery copy. FS_BACKUPS_DIR selects Clone transfer storage, not the public PostgreSQL backup gateway. The separate internal db_backup.sh uses ${FS_ARTIFACT_ROOT:-<application-repository>}/backups; do not apply that script's environment override to the public gateway.

An archive created through the checkout's host launcher is already in host backups/; copy that encrypted file to your protected off-installation storage. If it was created with native flow-steward inside Compact, exporting it is a separate file-copy operation on the Docker host. Resolve the running container through the selected context, not a guessed container name:

# If installer state is external, first set FS_STACK_ENV_FILE to that
# installation's exact <selected-state-root>/compose.env, not another checkout.
source core/entrypoints/cli/scripts/docker_compose_env.sh
compact_container=$(fs_compose ps -q compact-runtime)
test -n "$compact_container" || exit 1
# Substitute the exact filename returned by backup create.
docker cp "$compact_container:/data/backups/<file>.dump.enc" /private/recovery/<file>.dump.enc
chmod 600 /private/recovery/<file>.dump.enc

Prepare /private/recovery as an existing owner-only directory and keep the key separately. The shown destination is a placeholder, not a directory the product creates. For verification through the target host launcher or a host-helper restore, put the encrypted file in the target checkout's backups/ directory with mode 600, verify its identity and use the explicit target helper below. For verification with native flow-steward inside Compact, copy it separately into the target container's /data/backups/, set ownership to that runtime's backup owner, and run the native command inside that runtime. Host copy/import does not restore a database. Do not apply Docker-host commands inside a standalone provider container.

The helper defaults to checkout .flow-steward/compose.env; FS_INSTALLER_STATE_DIR alone does not redirect that read. With a supported external state root, set FS_STACK_ENV_FILE to its exact compose.env before sourcing and before invoking the restore helper, which loads the same context. Inspect the resolved services/project before any copy or recovery operation.

Preserve the other recovery assets​

Alongside the encrypted database artifact, preserve as applicable:

  • object-storage contents and bucket lifecycle/versioning evidence;
  • .env/.env.prod, installer-owned configuration, and generated durable secrets without exposing their values;
  • TLS certificates/keys and public endpoint configuration;
  • immutable role image tags/digests or the exact source tag/commit;
  • mutable extension and project/workspace content;
  • external-provider reconciliation information and idempotency records.

Restore only to an explicit target​

Database-only restore is deliberately absent from the public CLI. The internal operator helper drops and recreates the exact confirmed target database from template0, then imports the dump and repairs the AGE graph catalog. It is not a merge, and it is distinct from Installation Clone below. Rehearse on a disposable installation before a real recovery.

Capture parity evidence while the source is available, and enable manual maintenance before the real restore:

./flow-steward database restore-parity-capture \
--output backups/restore/baseline_<timestamp>.json --confirm
./flow-steward maintenance enable --confirm
./flow-steward maintenance status
./flow-steward backup verify backups/<file>.dump.enc

Use the actual output path returned by parity capture. In native Compact, backups/restore/baseline_<timestamp>.json resolves beneath /data/state/backups/restore/, unlike database dumps beneath /data/backups/. Export the baseline separately and import it into the target's corresponding state path before parity verification; copying a dump alone does not supply it.

Preflight on the target host, in Bash, from the target checkout:

source core/entrypoints/cli/scripts/docker_compose_env.sh
fs_compose config --services
fs_compose ps pg-fs
printf 'Compose project: %s\n' "$FS_COMPOSE_PROJECT_NAME"
printf 'target database: user=%s database=%s\n' "${POSTGRES_USER:-flow_steward}" "${POSTGRES_DB:-flow_steward}"

Check that the project, database user and database name are the target you intend to replace. Set explicit POSTGRES_USER / POSTGRES_DB in the recovery shell if your target differs from the helper's flow_steward defaults. Do not substitute an independently assembled Compose command: the helper uses the installer-selected project, file set, environment, overrides and profiles.

The current helper decrypts through a one-shot container of the selected fs-app or compact-runtime image, with --pull never; that recorded image must already be available. It no longer needs a host Python/cryptography installation. Docker/Compose, the target files and the correct protected export are prerequisites. Load the exact original FS_BACKUP_ENCRYPTION_KEY privately into the recovery shell from your secret store; the script deliberately does not infer it from an environment file. Do not type the key into shell history.

Run the destructive helper only after identifying the exact target:

core/entrypoints/cli/scripts/db_restore.sh backups/<file>.dump.enc \
--target-compose-project '<exact-project-from-preflight>' --confirm

Both target and confirmation flags are required. The helper starts pg-fs alone and verifies the dump before replacing the database. It stops the currently running writer services (Compact's runtime, or the applicable Scalable services), replaces the database, imports state, and repairs the graph catalog. It leaves writers stopped.

Continue only after [restore] done. If failure occurs after replacement, the database can be incomplete; keep writers stopped and follow the instruction to rerun restore. Do not start the runtime just because decryption or part of the SQL succeeded. Once the helper completes successfully, use the checkout for the intended restored release and start/verify through the installer:

./install.sh
./flow-steward maintenance status
./flow-steward database restore-parity-verify \
--baseline backups/restore/baseline_<timestamp>.json \
--output backups/restore/restored_<timestamp>.json --confirm
./flow-steward status
./flow-steward doctor

The runtime maintenance marker is independent filesystem state, not part of a PostgreSQL-only backup. Preserve and verify the target's enabled marker and traffic controls before restarting writers; do not expect restoring a database to create that marker or carry the source's maintenance setting. A later status check cannot retroactively prevent work that started while the marker was missing. Read-only doctor may report intentional maintenance. Do not run an unqualified doctor --fix, which can disable it; use the diagnosis and repair guide.

Record backup and target identities, operator, timestamps, parity result, RTO, RPO, object-storage reconciliation, and the decision to resume. Disable Maintenance Mode only after all checks pass.

Installation Clone​

For the practical source → transfer → target → cutover procedure, use Diagnose, Repair, and Move a Server.

Clone is an encrypted point-in-time replacement of an already installed target. It is not the PostgreSQL-only backup command, live replication, profile conversion, SSH transport, or a DNS switch.

Both source and target need verified official installed releases before Clone. If either was installed from a checkout in source mode, first complete the release check, backup, manual-maintenance, and confirmed exact official upgrade sequence in Upgrade Reference. Clone does not perform that transition implicitly.

Both source and target must already be in fully enabled manual maintenance. Only same-profile, same-data-placement installations and compatible Linux CPU architectures are supported. Restore replaces allowlisted product state, preserves the target's deployment-local shell, domain, ports, TLS, and installation identity, and activates the exact source release through the common release contract. External S3 objects and provider-side services are not copied.

# Source: secret-command is your own secret-store command, not a product command.
./flow-steward maintenance enable --confirm
secret-command | ./flow-steward clone create --passphrase-stdin --confirm

# Transfer the encrypted archive using your operator-managed transport.
# Preinstalled same-profile/same-placement Scalable target:
./flow-steward maintenance enable --confirm
review_status=0
secret-command | ./flow-steward clone restore backups/source.fsclone \
--passphrase-stdin --non-interactive || review_status=$?
if [ "$review_status" -eq 0 ]; then echo "Unexpected review success" >&2; exit 70; fi
if [ "$review_status" -ne 10 ]; then exit "$review_status"; fi
# Repeat with the exact target-bound confirmation printed by the review:
secret-command | ./flow-steward clone restore backups/source.fsclone \
--passphrase-stdin --non-interactive --confirm '<printed-target-or-plan-value>'
./flow-steward status
./flow-steward doctor
./flow-steward maintenance status

The Scalable review exits 10 without replacement. Compact's host launcher rejects explicit noninteractive restore without a confirmation value with exit 2, before any review is dispatched. On Compact, run an interactive restore, review and decline; use only a confirmation actually printed by that reviewed plan for a later confirmed invocation. See the profile-specific review procedure. Every other nonzero status is a failure, not authorization to continue. Declining confirmation leaves the target untouched. Pass exactly one line via stdin; never put the passphrase in arguments, environment files, or logs. Archive authentication, safe extraction, checksums, profile compatibility, and exact-release availability are checked before replacement. Preserve the printed operation identity and follow its resume instructions after an interruption; do not bypass recovery guards. Maintenance remains enabled after restore. Review external-effects warnings before explicitly reopening work.

Verification boundary​

On 2026-10-05, source commit eb1626b05d06ca893a451560df2b5e95b972c47e passed an actual local Compact PostgreSQL-only rehearsal: encrypted create/list/verify and owner-only export; verification on a separate empty target; wrong-project rejection; maintenance enable; confirmed target replacement; and exact baseline parity for two projects and three jobs. The target's filesystem maintenance marker remained unchanged, and writers were deliberately left stopped.

This does not accept post-restore runtime startup or credential rotation, Scalable or standalone-provider recovery, official-release Installation Clone, production recovery, or a complete system backup. Those require their own target-specific rehearsal.