Skip to the article

Storage and attachments

PostgreSQL stores Storage object metadata and application file references.

Separate object records from file bytes

PostgreSQL stores Storage object metadata and application file references. The Storage backend holds the actual bytes. Both belong to the same instance and backup point. The full filesystem profile persists bytes in the pinned upstream docker/volumes/storage directory; a configured S3-compatible backend needs a separate raw backend snapshot. Ephemeral container files are not durable Storage. Watch capacity for database, attachments and backup copies and keep backups off the active Storage disk.

Authorized application file access
PostgreSQL object metadata
Raw filesystem or S3 backend bytes
Matching configuration and recovery keys
Restoring a file requires its database record, stored bytes and matching keys together.

Verify authorized access

Private page and issue files pass application authorization before the service generates an allowed download. Publishing a page exposes files only within its published set through signed URLs; it does not make the bucket public or open the private application route. A stored object existing on disk does not prove its metadata, policies, encryption key or application permissions are correct. With a demo account, upload and download a file and compare its SHA-256. Repeat after restoration for each bucket used by the instance.

Recover a missing or inaccessible file

Run the Supabase verifier, check the correct stack and service-role configuration, then compare object records with the raw backend bytes and preserved keys. Correct the Storage service, policy or configuration failure before retrying. Never delete a non-empty avatars bucket to clear a readiness warning. Restoring database records alone cannot restore bytes. For S3 restoration, restore the raw backend snapshot rather than reimporting through /storage/v1/s3, which can create conflicting metadata.

Bash
pnpm verify:supabase --db-url "$SUPABASE_DB_URL" \
  --supabase-url "$MINDDY_PUBLIC_SUPABASE_URL" \
  --service-role-key "$SUPABASE_SERVICE_ROLE_KEY"

Docker Desktop filesystem Storage

On the tested macOS Docker Desktop profile, a host bind mount returned ENOTSUP when Storage wrote extended attributes. A new Linux named volume avoided that failure. For a new installation with no object bytes, the following persistent override replaces only the Storage mount. Keep RESTORE_OVERRIDE in the installed Compose context. Do not switch an existing populated mount to an empty volume or overwrite an existing restore override: stop writes and preserve its bytes using the backup and restoration procedures first.

Bash
: "${RESTORE_OVERRIDE:=/etc/minddy/storage-volume.yml}"
export RESTORE_OVERRIDE
test ! -e "$RESTORE_OVERRIDE"
export STORAGE_VOLUME=minddy-filesystem-storage
if docker volume inspect "$STORAGE_VOLUME" >/dev/null 2>&1; then
  echo "Refusing to replace an existing Storage volume." >&2
  exit 1
fi
cat > "$RESTORE_OVERRIDE" <<EOF
services:
  storage:
    volumes:
      - $STORAGE_VOLUME:/var/lib/storage
volumes:
  $STORAGE_VOLUME:
EOF
# Apply this overlay with the installed Compose context before the first start.

Related articles