CI/CD Deployment

Set up continuous integration and deployment for your NuxtHub application with proper database migration handling.

NuxtHub applications deploy through standard CI/CD pipelines. Database migrations require special attention, particularly for Cloudflare D1.

Cloudflare Deployment

Workers Builds

The simplest approach is to use Cloudflare Workers Builds, which automatically deploys your application on every commit.

  1. Create a Workers project in the Cloudflare dashboard
  2. Connect your GitHub or GitLab repository
  3. Configure the build settings:
    • Build command: npm run build
    • Build output directory: dist
NuxtHub automatically generates wrangler bindings from your nuxt.config.ts during the build process. No manual wrangler.jsonc file is required.
If you use a custom 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 deployments
  • CLOUDFLARE_ENV=staging for staging deployments
  • Leave unset for production (uses default environment)

GitHub Actions

For more control over your deployment process, you can use GitHub Actions:

.github/workflows/deploy.yml
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 }}

D1 Migrations in CI/CD

Cloudflare D1 databases require special handling because they are not accessible during build time in CI environments.

The Problem

Unlike PostgreSQL or MySQL, where build-time migrations work because the database is reachable over the network, Cloudflare D1 presents unique challenges:

  • It requires Worker bindings to access the database
  • These bindings are only available at runtime
  • CI build environments have no database connection

Solution: Pre-Deployment Migration Step

Run migrations before deployment using Wrangler or the NuxtHub CLI.

1. Disable Build-Time Migrations

nuxt.config.ts
export default defineNuxtConfig({
  hub: {
    db: {
      dialect: 'sqlite',
      driver: 'd1',
      connection: { databaseId: '<database-id>' },
      applyMigrationsDuringBuild: false
    }
  }
})

2. Add the Migration Step to Your CI/CD Pipeline

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:

wrangler.jsonc
{
  "d1_databases": [{
    "binding": "DB",
    "database_id": "<database-id>",
    "migrations_table": "_hub_migrations",
    "migrations_dir": ".output/server/db/migrations/"
  }]
}
.github/workflows/deploy.yml
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:

.github/workflows/deploy.yml
- 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 }}
This uses the D1 HTTP API to apply migrations remotely. Make sure your nuxt.config.ts has driver: 'd1' or driver: 'd1-http' configured.

PostgreSQL / MySQL CI/CD

PostgreSQL and MySQL migrations can run at build time because the database is reachable via a connection string.

.github/workflows/deploy.yml
- name: Build with migrations
  run: pnpm build
  env:
    DATABASE_URL: ${{ secrets.DATABASE_URL }}
Build-time migrations are enabled by default. NuxtHub automatically applies them during nuxt build.

Running Migrations in a Dedicated Job

For more control over the migration process, you can run migrations in a separate job:

.github/workflows/deploy.yml
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 Deployment

Vercel handles CI/CD automatically when you connect your repository.

Setup

  1. Import your project in the Vercel dashboard
  2. Configure environment variables under Project Settings → Environment Variables
  3. Vercel will deploy automatically on every push

Database Migrations

For Vercel deployments with PostgreSQL or MySQL, migrations run during the build process because Vercel's build environment has network access:

nuxt.config.ts
export default defineNuxtConfig({
  hub: {
    db: {
      dialect: 'postgresql',
      // Migrations run during build since Vercel's build environment has network access
    }
  }
})
Learn more about Vercel Git integrations in the Vercel documentation.

Environment-Specific Secrets

GitHub Actions

Store your secrets in Repository Settings → Secrets and variables → Actions:

SecretDescription
CLOUDFLARE_API_TOKENCloudflare API token with Workers, D1, KV, and R2 permissions
CLOUDFLARE_ACCOUNT_IDYour Cloudflare account ID
CLOUDFLARE_DATABASE_IDThe D1 database ID
DATABASE_URLPostgreSQL or MySQL connection string

Per-Environment Secrets

Use GitHub Environments to separate production and staging secrets:

.github/workflows/deploy.yml
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
Learn more about GitHub Environments in the GitHub documentation.