# Migrate from Firebase Storage

> Copy files from Firebase Storage (Cloud Storage for Firebase) into SupaHost Storage buckets, rewrite security rules as policies and update stored download URLs.

Source: https://supahost.app/kb/migrate-from-firebase-storage · Updated 2026-11-11

To move files from Firebase Storage to SupaHost, download them from the underlying Cloud Storage bucket, then upload them to a SupaHost Storage bucket with a short supabase-js script. Firebase security rules become policies on `storage.objects`, and any download URLs saved in your database need rewriting.

## Plan

- **Choose buckets.** Firebase apps usually have one bucket with top-level folders (`avatars/`, `uploads/`). On SupaHost you can keep one bucket or split folders into buckets, which makes public versus private access simpler.
- **Check sizes.** Each file can be up to 50 MB on SupaHost, and all projects on the instance share the plan's file storage (100 GB Builder, 250 GB Pro, 500 GB Business). Find large files first: `gsutil du -h gs://recipe-box-prod.appspot.com/** | sort -h | tail`.
- **Map user folders.** If paths contain Firebase UIDs (`users/{uid}/...`), migrate Auth first ([Migrate from Firebase Auth](/kb/migrate-from-firebase-auth)) so you can swap them for Supabase user IDs.

## Export from Firebase

```bash
gcloud storage cp -r gs://recipe-box-prod.appspot.com ./firebase-files
```

(`gsutil -m cp -r` works too.) This copies every object with its folder structure.

## Create buckets

Dashboard → your project → **Studio** → Storage, or in SQL:

```sql
insert into storage.buckets (id, name, public)
values ('avatars', 'avatars', true), ('uploads', 'uploads', false);
```

## Upload

```ts
import { createClient } from '@supabase/supabase-js'
import { readFile, readdir } from 'node:fs/promises'
import { join, relative, extname } from 'node:path'
import mime from 'mime'

const admin = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE_KEY!)
const root = './firebase-files/recipe-box-prod.appspot.com/uploads'

async function* walk(dir: string): AsyncGenerator<string> {
  for (const e of await readdir(dir, { withFileTypes: true })) {
    const p = join(dir, e.name)
    if (e.isDirectory()) yield* walk(p)
    else yield p
  }
}

for await (const file of walk(root)) {
  let path = relative(root, file).replaceAll('\\', '/')
  path = path.replace(/^users\/([^/]+)/, (_, fbUid) => uidMap.get(fbUid) ?? fbUid)
  const { error } = await admin.storage.from('uploads').upload(path, await readFile(file), {
    contentType: mime.getType(extname(file)) ?? 'application/octet-stream',
    upsert: true,
  })
  if (error) console.error(path, error.message)
}
```

`upsert: true` makes the script safe to rerun. Uploading with the `service_role` key bypasses policies, so the objects have no owner; if your policies check `owner_id`, write them against the path instead (as below).

## Rewrite security rules

A rule like `allow read, write: if request.auth.uid == userId` on `users/{userId}/**` becomes:

```sql
create policy "users manage their own folder" on storage.objects
  for all to authenticated
  using (bucket_id = 'uploads' and (storage.foldername(name))[1] = (select auth.uid())::text)
  with check (bucket_id = 'uploads' and (storage.foldername(name))[1] = (select auth.uid())::text);
```

## Update stored URLs

Firebase download URLs (`https://firebasestorage.googleapis.com/...?alt=media&token=...`) stop working once you shut Firebase down. Store the object path instead and build URLs in the app:

```ts
const { data } = supabase.storage.from('avatars').getPublicUrl(`${userId}/avatar.jpg`)
const { data: signed } = await supabase.storage.from('uploads').createSignedUrl(path, 3600)
```

There are no image transformations on SupaHost, so resize images before upload if you relied on a resize extension.

## Verify

```sql
select bucket_id, count(*), pg_size_pretty(sum((metadata->>'size')::bigint))
from storage.objects group by bucket_id;
```

Compare with the export, then open a few files as a signed-in user.

## Related

- [Buckets](/docs/storage/buckets)
- [Storage access control](/docs/storage/access-control)
- [Serving and downloading files](/docs/storage/serving-files)
- [Images without transformations](/docs/storage/images)
