Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check-dist.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
id: diff

# If index.js was different than expected, upload the expected version as an artifact
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
with:
name: dist
Expand Down
5 changes: 4 additions & 1 deletion __tests__/main.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {getDownloadUrl} from '../src/utils'
import {CLI_CONFIG_REGISTRY} from '../src/main'
import * as os from 'os'
import * as process from 'process'
import * as cp from 'child_process'
Expand All @@ -8,6 +7,10 @@ import * as fs from 'fs'
import * as yaml from 'js-yaml'
import {expect, test} from '@jest/globals'

// Importing src/main would run the action in this process, so match its
// exported variable name here instead.
const CLI_CONFIG_REGISTRY = 'SUPABASE_INTERNAL_IMAGE_REGISTRY'

test('gets download url to binary', async () => {
const url = await getDownloadUrl('1.28.0')
expect(
Expand Down
133 changes: 100 additions & 33 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

57 changes: 41 additions & 16 deletions docs/backups.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ Inside your repository, create a new file inside the `.github/workflows` folder

Never backup your data to a public repository.

!!! note

GitHub-hosted runners only support IPv4, and the direct connection string (`db.<ref>.supabase.co`) is IPv6 unless your project has the [IPv4 add-on](https://supabase.com/docs/guides/platform/ipv4-address). Store the **Session pooler** connection string from the [Connect panel](https://supabase.com/dashboard/project/_?showConnect=true&method=session) as the `SUPABASE_DB_URL` secret instead.

## Backup action

```yaml
Expand All @@ -17,29 +21,28 @@ jobs:
build:
runs-on: ubuntu-latest
env:
supabase_db_url: ${{ secrets.SUPABASE_DB_URL }} # For example: postgresql://postgres:[YOUR-PASSWORD]@db.<ref>.supabase.co:5432/postgres
supabase_db_url: ${{ secrets.SUPABASE_DB_URL }} # For example: postgresql://postgres.<ref>:[YOUR-PASSWORD]@aws-0-<region>.pooler.supabase.com:5432/postgres
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v5
- uses: supabase/setup-cli@v1
with:
version: latest
- name: Backup roles
run: supabase db dump --db-url "$supabase_db_url" -f roles.sql --role-only
run: supabase db dump --remote="$supabase_db_url" -f roles.sql --role-only
- name: Backup schema
run: supabase db dump --db-url "$supabase_db_url" -f schema.sql
run: supabase db dump --remote="$supabase_db_url" -f schema.sql
- name: Backup data
run: supabase db dump --db-url "$supabase_db_url" -f data.sql --data-only --use-copy
run: supabase db dump --remote="$supabase_db_url" -f data.sql --data-only --use-copy -x "storage.buckets_vectors" -x "storage.vector_indexes"
```
## Periodic Backups Workflow

You can use the GitHub Action to run periodic backups of your database. In this example, the Action workflow is triggered by `push` and `pull_request` events on the `main` branch, manually via `workflow_dispatch`, and automatically at midnight every day due to the `schedule` event with a `cron` expression.
The workflow runs on the latest Ubuntu runner and requires write permissions to the repository's contents. It uses the Supabase CLI to dump the roles, schema, and data from your Supabase database, utilizing the `SUPABASE_DB_URL` environment variable that is securely stored in the GitHub secrets.
After the backup is complete, it auto-commits the changes to the repository using the `git-auto-commit-action`. This ensures that the latest backup is always available in your repository. The commit message for these automated commits is "Supabase backup".
This workflow provides an automated solution for maintaining regular backups of your Supabase database. It helps keep your data safe and enables easy restoration in case of any accidental data loss or corruption.
After the backup is complete, it auto-commits the changes to the repository using the `git-auto-commit-action`. The commit message for these automated commits is "Supabase backup".

!!! note
!!! warning

Never backup your data to a public repository.
`data.sql` holds the rows from your tables as plain-text SQL, including email addresses and password hashes from `auth.users`. Anyone with read access to the repository can read it, and every committed dump stays in the Git history even after you delete the file. Never backup your data to a public repository. For anything beyond a small private project, consider uploading the files to a private storage bucket instead of committing them.

```yaml
name: Supa-backup
Expand All @@ -58,26 +61,48 @@ jobs:
permissions:
contents: write
env:
supabase_db_url: ${{ secrets.SUPABASE_DB_URL }} # For example: postgresql://postgres:[YOUR-PASSWORD]@db.<ref>.supabase.co:5432/postgres
supabase_db_url: ${{ secrets.SUPABASE_DB_URL }} # For example: postgresql://postgres.<ref>:[YOUR-PASSWORD]@aws-0-<region>.pooler.supabase.com:5432/postgres
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v5
with:
ref: ${{ github.head_ref }}
- uses: supabase/setup-cli@v1
with:
version: latest
- name: Backup roles
run: supabase db dump --db-url "$supabase_db_url" -f roles.sql --role-only
run: supabase db dump --remote="$supabase_db_url" -f roles.sql --role-only
- name: Backup schema
run: supabase db dump --db-url "$supabase_db_url" -f schema.sql
run: supabase db dump --remote="$supabase_db_url" -f schema.sql
- name: Backup data
run: supabase db dump --db-url "$supabase_db_url" -f data.sql --data-only --use-copy
run: supabase db dump --remote="$supabase_db_url" -f data.sql --data-only --use-copy -x "storage.buckets_vectors" -x "storage.vector_indexes"

- uses: stefanzweifel/git-auto-commit-action@v4
- uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: Supabase backup
```

## Check that the backup restores

A dump that finishes without errors can still fail to restore. For example, a data dump that includes `storage.buckets_vectors` fails with `permission denied for table buckets_vectors` when you load it, which is why the commands above exclude that table and `storage.vector_indexes`.

To catch problems like this when the backup runs, add a step at the end of the job that loads the three files into a throwaway local database on the runner. It starts Postgres with `supabase db start` in a temporary directory and runs the restore command from [Backup and Restore using the CLI](https://supabase.com/docs/guides/platform/migrating-within-supabase/backup-restore). With `--single-transaction` and `ON_ERROR_STOP`, any error fails the step.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Move the restore check before auto-commit

When this instruction is followed for the Periodic Backups Workflow, “at the end of the job” places the check after stefanzweifel/git-auto-commit-action at docs/backups.md:79-81. If a dump is produced but cannot be restored—the exact case this section targets—the action has already committed and pushed that unusable dump before psql fails, so the latest published backup is not protected by the check; show or require this validation before the auto-commit step.

Useful? React with 👍 / 👎.


```yaml
- name: Check that the backup restores
run: |
supabase init --workdir "$RUNNER_TEMP/restore-check"
supabase db start --workdir "$RUNNER_TEMP/restore-check"
psql \
--single-transaction \
--variable ON_ERROR_STOP=1 \
--file roles.sql \
--file schema.sql \
--command 'SET session_replication_role = replica' \
--file data.sql \
--dbname 'postgresql://postgres:postgres@127.0.0.1:54322/postgres'
```

## More resources

- Backing up and migrating your project: [Migrating and Upgrading](https://supabase.com/docs/guides/platform/migrating-and-upgrading-projects)
- Backing up and restoring your project: [Backup and Restore using the CLI](https://supabase.com/docs/guides/platform/migrating-within-supabase/backup-restore)
- Daily backups and Point-in-Time Recovery: [Database Backups](https://supabase.com/docs/guides/platform/backups)
Loading