Skip to content

thisismess/deploy-nuxt-cloudflare

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Deploy Nuxt to Cloudflare Workers

A reusable GitHub Action to build and deploy Nuxt applications to Cloudflare Workers with versioned deployments.

Features

  • Builds Nuxt apps for Cloudflare Workers
  • Versioned deployments with tags and messages
  • Support for build-time environment variables
  • Environment-specific bindings via wrangler.{env}.jsonc files
  • Automatic D1 database migrations
  • Worker secrets management with automatic var conflict filtering
  • Configurable Node.js version
  • Works with npm, pnpm, or yarn
  • Build/deploy modes for coordinated CI workflows

Usage

Basic Usage

- uses: thisismess/deploy-nuxt-cloudflare@v1
  with:
    cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
    cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
    worker-name: ${{ secrets.CLOUDFLARE_WORKER_NAME }}

Note: The worker-name must match your Cloudflare Worker name exactly. This is the name shown in the Cloudflare dashboard under Workers & Pages.

With Build Environment Variables

- uses: thisismess/deploy-nuxt-cloudflare@v1
  with:
    cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
    cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
    worker-name: ${{ secrets.CLOUDFLARE_WORKER_NAME }}
    working-directory: src/nuxt
    build-env: |
      {
        "NUXT_PUBLIC_API_BASE": "https://api.example.com",
        "NUXT_PUBLIC_SITE_URL": "https://example.com"
      }

With Environment-Specific Bindings

Use GitHub environments to manage different Cloudflare bindings (KV, D1, R2, etc.) for production vs staging.

1. Create environment-specific config files:

Nuxt/Nitro generates the base wrangler config during build (with main, assets, etc.). You only need to create environment-specific files with your bindings:

// wrangler.production.jsonc (production bindings)
{
  "kv_namespaces": [
    { "binding": "CACHE", "id": "abc123-prod-kv-id" }
  ],
  "d1_databases": [
    { "binding": "DB", "database_id": "xyz789-prod-d1-id" }
  ],
  "vars": {
    "ENVIRONMENT": "production"
  }
}
// wrangler.staging.jsonc (staging bindings)
{
  "kv_namespaces": [
    { "binding": "CACHE", "id": "def456-staging-kv-id" }
  ],
  "d1_databases": [
    { "binding": "DB", "database_id": "uvw321-staging-d1-id" }
  ],
  "vars": {
    "ENVIRONMENT": "staging"
  }
}

The vars from your environment file are available as environment variables during the Nuxt build (for NUXT_PUBLIC_* etc.), and the full config is merged into the Nuxt-generated .output/server/wrangler.json after the build step.

2. Configure your workflow with GitHub environments:

name: Deploy

on:
  push:
    branches: [main, staging]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ github.ref_name == 'main' && 'production' || 'staging' }}
    steps:
      - uses: actions/checkout@v4

      - uses: thisismess/deploy-nuxt-cloudflare@v1
        with:
          cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          worker-name: ${{ secrets.CLOUDFLARE_WORKER_NAME }}
          github-environment: ${{ github.ref_name == 'main' && 'production' || 'staging' }}

The action will deep-merge wrangler.{github-environment}.jsonc into the base wrangler.jsonc before deploying. This allows multiple branches to share the same environment config, or unique branches to have unique configs.

D1 Database Migrations

The action automatically detects and applies D1 database migrations during deployment. No additional workflow steps are needed.

How it works:

  1. The action checks the environment-specific wrangler config (e.g. wrangler.production.jsonc) for d1_databases entries
  2. If D1 databases are found, it looks for a migrations directory (typically server/db/migrations/sqlite)
  3. Migrations are applied to each D1 database via wrangler d1 migrations apply before the worker is uploaded

Setup:

Add a d1_databases binding to your environment-specific wrangler config with a migrations_dir pointing to your migrations:

// wrangler.production.jsonc
{
    "d1_databases": [
        {
            "binding": "DB",
            "database_name": "my-app",
            "database_id": "your-database-id",
            "migrations_dir": "server/db/migrations/sqlite"
        }
    ]
}

Migrations are applied automatically on every deploy. If no D1 databases or migrations directory are found, the step is silently skipped.

Full Example (Staging + Production)

name: Deploy

on:
  push:
    branches: [main, staging]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ github.ref_name == 'main' && 'production' || 'staging' }}
    steps:
      - uses: actions/checkout@v4

      - uses: thisismess/deploy-nuxt-cloudflare@v1
        with:
          cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          worker-name: ${{ secrets.CLOUDFLARE_WORKER_NAME }}
          github-environment: ${{ github.ref_name == 'main' && 'production' || 'staging' }}
          build-env: |
            {
              "NUXT_PUBLIC_SITE_URL": "${{ vars.SITE_URL }}",
              "NUXT_PUBLIC_API_BASE": "${{ vars.API_BASE }}"
            }

With Worker Secrets

Use secrets-json to upload encrypted secrets to your Worker. Secrets are also available as environment variables during the Nuxt build and are uploaded to Cloudflare after deployment. Any secrets that share a name with vars in your wrangler environment config are automatically skipped to avoid conflicts.

- uses: thisismess/deploy-nuxt-cloudflare@v1
  with:
    cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
    cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
    worker-name: ${{ secrets.CLOUDFLARE_WORKER_NAME }}
    github-environment: production
    secrets-json: |
      {
        "API_SECRET_KEY": "${{ secrets.API_SECRET_KEY }}",
        "DATABASE_URL": "${{ secrets.DATABASE_URL }}"
      }

Note: Secret names must not overlap with vars defined in your wrangler.{environment}.jsonc. Use vars for public configuration (URLs, feature flags) and secrets for sensitive values (API keys, credentials).

Separate Build and Deploy Steps

Use mode: build and mode: deploy to separate the build phase from the upload/deploy phase. This is useful when you want to:

  • Run other jobs between build and deploy
  • Control exactly when the upload to Cloudflare happens
  • Coordinate deployments with other systems
name: Deploy

on:
  push:
    branches: [main]

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

      # Build step: prepares .output directory with merged config
      - uses: thisismess/deploy-nuxt-cloudflare@v1
        with:
          cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          worker-name: ${{ secrets.CLOUDFLARE_WORKER_NAME }}
          github-environment: production
          mode: build  # Build only, no upload to Cloudflare

      # ... other steps (e.g., tests, other deployments) ...

      # Deploy step: uploads and deploys the prepared build
      - uses: thisismess/deploy-nuxt-cloudflare@v1
        with:
          cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          worker-name: ${{ secrets.CLOUDFLARE_WORKER_NAME }}
          mode: deploy  # Upload and deploy from .output

Modes:

  • full (default): Build, upload, and deploy in one step
  • build: Build only - prepares .output directory with merged config, no Cloudflare interaction
  • deploy: Upload and deploy from .output directory (must run after build in the same job)

Inputs

Input Description Required Default
cloudflare-api-token Cloudflare API Token with Workers permissions Yes -
cloudflare-account-id Cloudflare Account ID Yes -
worker-name Name of your Cloudflare Worker (must match the name in Cloudflare dashboard) Yes -
working-directory Path to Nuxt project directory No .
node-version Node.js version to use No 20
build-command Command to build the Nuxt app No npm run build
install-command Command to install dependencies (auto-detected from package-manager if not set) No npm ci
package-manager Package manager for caching (npm, pnpm, yarn, or none to disable) No npm
build-env JSON object of environment variables for build No {}
secrets-json JSON object of secrets to upload to Worker (also available during build) No -
deploy-tag Tag for the deployment No Short SHA
deploy-message Message for the deployment No {actor} - {sha}
github-environment GitHub environment name - merges wrangler.{environment}.jsonc into base config No -
mode Action mode: full (build+upload+deploy), build (build only), or deploy (upload+deploy from .output) No full

Outputs

Output Description
version-id The uploaded Worker Version ID (set in full and deploy modes)
deploy-tag The tag used for the deployment

Requirements

  • Nuxt must be configured to build for Cloudflare Workers (Nitro generates the wrangler config automatically)
  • To use github-environment for environment-specific bindings, create wrangler.{environment}.jsonc files
  • The Cloudflare API token needs Workers Scripts:Edit permission

License

MIT

About

A Github Action to deploy a Nuxt application to Cloudflare, with handling for unique values for staging and production.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors