CI/CD Deployment
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.
- Create a Workers project in the Cloudflare dashboard
- Connect your GitHub or GitLab repository
- Configure the build settings:
- Build command:
npm run build - Build output directory:
dist
- Build command:
nuxt.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=previewfor preview deploymentsCLOUDFLARE_ENV=stagingfor staging deployments- Leave unset for production (uses default environment)
npm run build and wrangler deploy flow does not apply D1 migrations automatically.GitHub Actions
For 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 }}
D1 Migrations in CI/CD
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.
NuxtHub generates migrations_table and migrations_dir in .output/server/wrangler.json for D1 projects. This enables Wrangler-managed migrations, but it does not execute them automatically during wrangler deploy.
_hub_migrations may be missing the .sql suffix. Wrangler requires .sql in order to recognize the migration files.Fix: Update the migration names in the _hub_migrations table to include the .sql suffix.
-- Preview affected rows
SELECT id, name FROM _hub_migrations WHERE name NOT LIKE '%.sql';
-- Append .sql
UPDATE _hub_migrations SET name = name || '.sql' WHERE name NOT LIKE '%.sql';
1. Disable Build-Time Migrations
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.
NuxtHub already generates the required metadata in .output/server/wrangler.json, so you can run migrations directly against the generated config:
npx wrangler d1 migrations apply DB --remote --config .output/server/wrangler.json && npx wrangler deploy
If you maintain a manual wrangler.jsonc, configure it 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/sqlite/"
}]
}
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
- name: Apply D1 Migrations
run: npx wrangler d1 migrations apply DB --remote --config .output/server/wrangler.json
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
- name: Deploy
run: npx wrangler deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
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 / MySQL CI/CD
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.Running Migrations in a Dedicated Job
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 Deployment
Vercel handles CI/CD automatically when you connect your repository.
Setup
- Import your project in the Vercel dashboard
- Configure environment variables under Project Settings → Environment Variables
- 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:
export default defineNuxtConfig({
hub: {
db: {
dialect: 'postgresql',
// Migrations run during build since Vercel's build environment has network access
}
}
})
Environment-Specific Secrets
GitHub Actions
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 |
Per-Environment Secrets
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