Skip to content
Open
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
138 changes: 138 additions & 0 deletions docs/docs/cloud-ci-cd/migration-safety-checks.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
---
description: Add migration safety checks to your Hasura CI/CD pipeline with pgfence
keywords:
- hasura
- docs
- ci-cd
- migrations
- safety
- postgres
- locks
- pgfence
sidebar_label: Migration safety checks
sidebar_position: 50
---

import ProductBadge from '@site/src/components/ProductBadge';

# Migration Safety Checks

Comment on lines +15 to +19
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

This documentation page is missing the ProductBadge component that is consistently included in all other pages in the cloud-ci-cd section. Add the import statement and ProductBadge component after the main heading to match the pattern used in other files like github-integration.mdx, preview-apps.mdx, and regression-tests.mdx.

Suggested change
# Migration Safety Checks
import { ProductBadge } from '@site/src/components/ProductBadge';
# Migration Safety Checks
<ProductBadge product="cloud-ci-cd" />

Copilot uses AI. Check for mistakes.
<ProductBadge free pro ee />

## Introduction

When deploying Hasura Migrations to production, certain SQL statements can acquire aggressive Postgres locks that block
reads and writes for the duration of the operation. On large tables, this can cause downtime or cascading lock queue
failures.

[pgfence](https://pgfence.com) is an open-source CLI that analyzes your migration SQL files **before deployment** and
reports which lock modes each statement acquires, the associated risk level, and safe rewrite alternatives when a
dangerous pattern is detected.

Adding pgfence to your CI/CD pipeline catches problems like non-concurrent index creation, unsafe column type changes,
and missing `lock_timeout` settings before they reach production.

## What pgfence detects

pgfence checks for common Postgres migration footguns, including:

| Pattern | Risk | Why it's dangerous |
|---------|------|--------------------|
| `CREATE INDEX` (non-concurrent) | Medium | Acquires a `SHARE` lock, blocking all writes |
| `ALTER COLUMN TYPE` | High | Requires `ACCESS EXCLUSIVE` lock and full table rewrite |
| `ADD COLUMN ... NOT NULL` without `DEFAULT` | High | `ACCESS EXCLUSIVE` lock on the entire table |
| `ADD CONSTRAINT ... FOREIGN KEY` (inline) | High | Locks both tables with `ACCESS EXCLUSIVE` |
| `DROP TABLE` / `TRUNCATE` | Critical | `ACCESS EXCLUSIVE` lock, irreversible |
| Missing `SET lock_timeout` | Policy | No timeout means unbounded lock waits |

For each issue found, pgfence provides the safe alternative — for example, `CREATE INDEX CONCURRENTLY` instead of
`CREATE INDEX`, or the `NOT VALID` + `VALIDATE CONSTRAINT` pattern for foreign keys.

## Add pgfence to a GitHub Actions workflow

If you use [GitHub Actions](https://github.com/features/actions) to deploy Hasura Migrations (as recommended for
production in the
[GitHub integration docs](/cloud-ci-cd/github-integration.mdx)), you can add pgfence as a step that runs before
applying migrations.

### Basic workflow

Add the following step to your existing workflow file (e.g., `.github/workflows/hasura-deploy.yml`) **before** the step
that applies migrations:

```yaml
- name: Check migration safety
run: npx --yes @flvmnt/pgfence@0.2.3 analyze --ci hasura/migrations/**/*.sql
```
Comment on lines +64 to +66
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

The recommended npx --yes @flvmnt/pgfence analyze ... step downloads and executes a third-party npm package on every CI run without pinning it to a specific immutable version or verifying integrity, which creates a supply chain risk. If the @flvmnt/pgfence package or its npm distribution is compromised, attackers could execute arbitrary code in your GitHub Actions runner with access to the repository and default secrets (e.g., GITHUB_TOKEN). To reduce this risk, pin @flvmnt/pgfence to a specific trusted version and/or use integrity verification mechanisms rather than invoking an unpinned package via npx in CI.

Copilot uses AI. Check for mistakes.

The `--ci` flag causes pgfence to exit with code 1 if any check exceeds the default risk threshold, which will fail the
workflow and prevent the deployment.

### Full example workflow

```yaml
name: Hasura Deploy

on:
push:
branches: [main]
paths:
- 'hasura/migrations/**'
- 'hasura/metadata/**'

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Check migration safety
run: npx --yes @flvmnt/pgfence@0.2.3 analyze --ci hasura/migrations/**/*.sql

Comment on lines +89 to +91
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

In this full workflow example, the Check migration safety step uses npx --yes @flvmnt/pgfence analyze ... to fetch and run a third-party npm package on each CI execution without version pinning or integrity checks, exposing the pipeline to supply chain compromise. A malicious or hijacked @flvmnt/pgfence release could run arbitrary code in the GitHub Actions environment, potentially exfiltrating repository data or abusing the default GITHUB_TOKEN. Consider pinning @flvmnt/pgfence to a specific immutable version and adding integrity verification instead of running an unpinned package via npx in CI.

Copilot uses AI. Check for mistakes.
- name: Apply migrations
run: hasura migrate apply --endpoint ${{ secrets.HASURA_ENDPOINT }} --admin-secret ${{ secrets.HASURA_ADMIN_SECRET }}

- name: Apply metadata
run: hasura metadata apply --endpoint ${{ secrets.HASURA_ENDPOINT }} --admin-secret ${{ secrets.HASURA_ADMIN_SECRET }}
```

### Controlling the risk threshold

By default, `--ci` fails the build on `HIGH` risk or above. You can adjust this with `--max-risk`:

```yaml
- name: Check migration safety
run: npx --yes @flvmnt/pgfence@0.2.3 analyze --ci --max-risk medium hasura/migrations/**/*.sql
```
Comment on lines +104 to +106
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

The Check migration safety step here again uses npx --yes @flvmnt/pgfence analyze ... without pinning a specific version or verifying package integrity, which is a supply chain risk in CI. If the npm package @flvmnt/pgfence is compromised, an attacker could execute arbitrary code in the workflow environment with access to repository contents and default GitHub Actions secrets. Prefer pinning @flvmnt/pgfence to a known-good version and employing integrity checks instead of invoking an unpinned package directly via npx.

Copilot uses AI. Check for mistakes.

This will fail the build on `MEDIUM` risk or above, catching issues like non-concurrent index creation.

### JSON output for further processing

To integrate pgfence results into other tools or dashboards, use the `--output json` flag:

```yaml
- name: Check migration safety
run: npx --yes @flvmnt/pgfence@0.2.3 analyze --output json hasura/migrations/**/*.sql > pgfence-report.json
```
Comment on lines +115 to +117
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

This example uses npx --yes @flvmnt/pgfence analyze --output json ... which downloads and executes the @flvmnt/pgfence npm package on each CI run without a fixed version or integrity verification, introducing a supply chain attack surface. A compromised package release could run arbitrary code within the GitHub Actions runner and access repository data or GitHub-provided secrets. Mitigate this by pinning @flvmnt/pgfence to a specific immutable version and/or validating the package integrity instead of calling an unpinned package via npx.

Copilot uses AI. Check for mistakes.

## Adjusting migration paths

Hasura stores migrations under `hasura/migrations/<source-name>/<timestamp>_<name>/up.sql` by default. Adjust the glob
pattern to match your project structure:

```yaml
# Single data source named "default"
run: npx --yes @flvmnt/pgfence@0.2.3 analyze --ci hasura/migrations/default/**/up.sql

# All data sources
run: npx --yes @flvmnt/pgfence@0.2.3 analyze --ci hasura/migrations/**/up.sql
```

## Further resources

- [pgfence documentation](https://pgfence.com)
- [pgfence on npm](https://www.npmjs.com/package/@flvmnt/pgfence)
- [pgfence on GitHub](https://github.com/flvmnt/pgfence)
- [PostgreSQL explicit locking documentation](https://www.postgresql.org/docs/current/explicit-locking.html)
- [Hasura Migrations Best Practices](/migrations-metadata-seeds/migration-best-practices.mdx)