A reusable GitHub Action to build and deploy Nuxt applications to Cloudflare Workers with versioned deployments.
- Builds Nuxt apps for Cloudflare Workers
- Versioned deployments with tags and messages
- Support for build-time environment variables
- Environment-specific bindings via
wrangler.{env}.jsoncfiles - 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
- 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-namemust match your Cloudflare Worker name exactly. This is the name shown in the Cloudflare dashboard under Workers & Pages.
- 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"
}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.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.
The action automatically detects and applies D1 database migrations during deployment. No additional workflow steps are needed.
How it works:
- The action checks the environment-specific wrangler config (e.g.
wrangler.production.jsonc) ford1_databasesentries - If D1 databases are found, it looks for a migrations directory (typically
server/db/migrations/sqlite) - Migrations are applied to each D1 database via
wrangler d1 migrations applybefore 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.
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 }}"
}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
varsdefined in yourwrangler.{environment}.jsonc. Usevarsfor public configuration (URLs, feature flags) andsecretsfor sensitive values (API keys, credentials).
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 .outputModes:
full(default): Build, upload, and deploy in one stepbuild: Build only - prepares.outputdirectory with merged config, no Cloudflare interactiondeploy: Upload and deploy from.outputdirectory (must run afterbuildin the same job)
| 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 |
| Output | Description |
|---|---|
version-id |
The uploaded Worker Version ID (set in full and deploy modes) |
deploy-tag |
The tag used for the deployment |
- Nuxt must be configured to build for Cloudflare Workers (Nitro generates the wrangler config automatically)
- To use
github-environmentfor environment-specific bindings, createwrangler.{environment}.jsoncfiles - The Cloudflare API token needs
Workers Scripts:Editpermission
MIT