Skip to main content
BOW stores application records in its database and file contents on disk. Configure both before using a Kubernetes deployment for production. This guide shows which paths to preserve, how to choose storage for your environment, and how to migrate an existing installation before replacing its pods.
If your existing pods have no persistent file mounts, back up their files before a rollout, upgrade, scale-down, or pod deletion. Adding a PVC does not copy files out of the old containers. Once those containers or pods are gone, missing files require a backup or re-upload.

What must survive a restart

The Helm chart enables persistence for bundled PostgreSQL by default. Application file mounts are separate and must be configured through extraVolumes and extraVolumeMounts. A file can remain listed in BOW after its bytes are lost because its database record still exists. A healthy PostgreSQL PVC therefore does not prove that uploads are safe. Container writable layers are ephemeral. An emptyDir survives a container crash within the same pod but is deleted when that pod is removed. Use a PVC backed by durable storage for files you need to keep. See Kubernetes volumes.

Choose storage for your environment

NFS is one way to provide shared file storage. You can use your cluster’s existing storage services instead. For a single replica, ReadWriteOnce disk storage is sufficient when the pod can be scheduled on a node that can attach that disk. Plan for disk attachment and zone constraints. Use Recreate for this deployment to stop the previous pod before starting its replacement; expect downtime during updates. For replicas across nodes, use ReadWriteMany storage and mount the same claims at the same application paths in every replica. All replicas also need the same PostgreSQL database and encryption key. Shared storage alone does not provide full application high availability; see Day 2 Operations.
ReadWriteOnce means read/write access from one node, not necessarily one pod. It does not provide a shared filesystem across nodes. See Kubernetes access modes.

AWS EKS

For a single replica on EC2 nodes, follow the EBS CSI setup. EBS volumes cannot be mounted by Fargate pods. For shared files, follow the EFS CSI setup. Configure the filesystem, access permissions, and network access before attaching it to BOW. Fargate uses static EFS provisioning rather than dynamic provisioning.

Azure AKS

Use Azure Disk for a single replica or Azure Files for files shared across nodes. Follow the AKS storage overview and Azure Files volume setup.

Other clusters

Ask your cluster administrator for a durable StorageClass and its supported access modes. A PVC backed by a node-local directory can still become unavailable when the workload moves to another node. BOW’s file storage settings expect filesystem paths. A plain S3 bucket URL or Azure Blob URL is not a value you can substitute for a mounted directory.

Mount application file storage

For an existing installation, complete the migration procedure before applying these values. Create two PVCs in the application’s namespace using your provider’s provisioning instructions:
  • bow-uploads for /app/backend/uploads.
  • bow-branding for /app/backend/branding_uploads.
Choose their sizes for your workload and use ReadWriteMany if replicas must mount them across nodes. Confirm the claims provision successfully; some StorageClasses wait for the first consumer before binding. Add the following excerpt to your existing Helm values. This example uses one application replica and two existing PVCs; it does not create the claims or configure the database.
If you already use extraVolumes or extraVolumeMounts, merge these entries into those lists. Helm values files replace lists rather than appending to them. For a new artifact app file store, you can place it inside the persistent uploads directory:
Merge this entry with your existing extraEnv list. If you already use another BOW_ARTIFACT_STORAGE path, mount persistent storage there and migrate its contents as well. Ensure the application user can create, read, and write files on every mount. Check the user ID and group ID in your pinned image and configure filesystem ownership, export permissions, or CSI mount options accordingly. A mount hides directories from the container image; prepare any required directory structure on the volume. Keep your existing config.secretRef pointing at a Secret containing the original BOW_ENCRYPTION_KEY. Do not generate a replacement key during a storage migration. After preparing storage and copying existing files, apply your complete values file:
Replace bowapp and bow if your release and namespace differ, and replace <installed-chart-version> with your current chart version. Keep the image version pinned in your complete values file during the storage migration.

Migrate an existing installation

Use a maintenance window. Do not scale down or restart the old pods to begin this procedure: their local files may be the only copies.
  1. Pause changes without replacing pods. Suspend automated deployments and scaling, block new writes at your ingress or maintenance layer, and let active runs finish. Account for scheduled jobs and integrations that can still create files.
  2. Inventory every application pod. Record the image version, mounted paths, artifact storage path, Helm values, and encryption-key source. With several replicas, each pod may contain different files.
  3. Back up the database, original encryption key, and files. Copy the whole uploads directory from every pod into a separate backup directory. Repeat for branding and the configured artifact storage path. For example, replace <pod-name> with one running application pod:
    Repeat with a different backup directory for each pod. If a directory is absent, record that explicitly. Keep backups outside the pods and verify file counts, sizes, and checksums.
  4. Prepare the target PVCs. Provision storage, prepare directories and permissions, and mount it in a temporary migration pod using your cluster’s storage procedures.
  5. Restore files into the target volumes. Preserve directory structure and filenames. Combine copies from every source pod without silently overwriting conflicting files. Investigate conflicts and retain the original backups. Confirm checksums after copying.
  6. Deploy the persistent mounts. Stop the temporary migration pod so single-node disks can detach before the application mounts them. Apply the values above only after verifying the copies. Keep the same database, encryption key, and configured file paths. Account for any autoscaler or GitOps controller that could override your maintenance settings.
  7. Verify before resuming writes. Open existing uploads, generated files, branding, and artifact app files. Then run the replacement check below. Resume traffic, integrations, scheduling, and automated deployments after these checks pass.
Mounting an empty PVC can make existing files appear missing immediately because it hides the image or container directory beneath the mount. Restore your files before switching the application to the new volume.

Verify persistence after pod replacement

Test first in staging. In production, schedule this check because replacing a pod interrupts in-flight work.
  1. Upload a small test file through BOW and record its contents. If you use branding or artifact app file resources, test those too.
  2. Confirm the application’s pod mounts reference your intended PVCs. Check both the pod specification and the actual mounted directories.
  3. Replace the application pod with a controlled rollout:
  4. Open and download the same file through BOW and compare its contents. Also verify a pre-migration file and a stored connection credential.
  5. If you run multiple replicas, verify file access through each replica and after rescheduling to another eligible node.
A successful health probe or a Bound PVC alone does not verify that BOW is reading the persisted files.

Backups and recovery

Back up PostgreSQL, application file volumes, the original encryption key, and deployment configuration together with a consistent recovery point. Quiesce writes or use a backup procedure that coordinates database and filesystem snapshots. Review your StorageClass reclaim policy and Helm uninstall behavior before deleting claims or a release. Persistent storage is not a backup. If files have already disappeared, restore the matching file backup and verify it against database records. Creating a new PVC cannot recover bytes from deleted ephemeral containers. If there is no recoverable copy, re-upload the original files or regenerate the affected outputs. See Day 2 Operations for the wider backup and recovery checklist.