NuxtHub applications deploy through standard CI/CD pipelines. Database migrations require special attention, particularly for Cloudflare D1.
The simplest approach is to use Cloudflare Workers Builds, which automatically deploys your application on every commit.
npm run builddistnuxt.config.ts during the build process. No manual wrangler.jsonc file is required.wrangler.jsonc with named environments, set CLOUDFLARE_ENV as a build-time variable for non-production environments in Workers Builds → Build configuration → Environment variables:CLOUDFLARE_ENV=preview for preview deploymentsCLOUDFLARE_ENV=staging for staging deploymentsFor more control over your deployment process, you can use GitHub Actions:
name: Deploy to Cloudflare
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm build
env:
CLOUDFLARE_ENV: ${{ github.ref == 'refs/heads/main' && '' || 'preview' }}
- name: Deploy
run: npx wrangler deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
Unlike PostgreSQL or MySQL, where build-time migrations work because the database is reachable over the network, Cloudflare D1 presents unique challenges:
Run migrations before deployment using Wrangler or the NuxtHub CLI.
export default defineNuxtConfig({
hub: {
db: {
dialect: 'sqlite',
driver: 'd1',
connection: { databaseId: '<database-id>' },
applyMigrationsDuringBuild: false
}
}
})
Option A: Using Wrangler (recommended)
Use Wrangler's built-in D1 migrations command. This requires your migrations to follow Wrangler's migration format.
Configure your wrangler.jsonc to use NuxtHub's migration table and output directory:
{
"d1_databases": [{
"binding": "DB",
"database_id": "<database-id>",
"migrations_table": "_hub_migrations",
"migrations_dir": ".output/server/db/migrations/"
}]
}
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- name: Install dependencies
run: pnpm install
# Run migrations BEFORE the build step
- name: Apply D1 Migrations
run: npx wrangler d1 migrations apply <database-name> --remote
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
- name: Build
run: pnpm build
- name: Deploy
run: npx wrangler deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
Option B: Using nuxt db migrate
If you have the D1 HTTP credentials configured, you can use the Nuxt CLI to apply migrations:
- name: Apply D1 Migrations
run: npx nuxt db migrate
env:
NUXT_HUB_CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
NUXT_HUB_CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
NUXT_HUB_CLOUDFLARE_DATABASE_ID: ${{ secrets.CLOUDFLARE_DATABASE_ID }}
nuxt.config.ts has driver: 'd1' or driver: 'd1-http' configured.PostgreSQL and MySQL migrations can run at build time because the database is reachable via a connection string.
- name: Build with migrations
run: pnpm build
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
nuxt build.For more control over the migration process, you can run migrations in a separate job:
jobs:
migrate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install
- name: Run migrations
run: npx nuxt db migrate
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
deploy:
needs: migrate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# ... build and deploy steps
Vercel handles CI/CD automatically when you connect your repository.
For Vercel deployments with PostgreSQL or MySQL, migrations run during the build process because Vercel's build environment has network access:
export default defineNuxtConfig({
hub: {
db: {
dialect: 'postgresql',
// Migrations run during build since Vercel's build environment has network access
}
}
})
Store your secrets in Repository Settings → Secrets and variables → Actions:
| Secret | Description |
|---|---|
CLOUDFLARE_API_TOKEN | Cloudflare API token with Workers, D1, KV, and R2 permissions |
CLOUDFLARE_ACCOUNT_ID | Your Cloudflare account ID |
CLOUDFLARE_DATABASE_ID | The D1 database ID |
DATABASE_URL | PostgreSQL or MySQL connection string |
Use GitHub Environments to separate production and staging secrets:
jobs:
deploy-production:
environment: production
# This job uses secrets from the production environment
deploy-preview:
environment: preview
# This job uses secrets from the preview environment