Guides

Cloudflare Workers Deployment

Last updated by
Harlan Wilton
in chore: sync.

Cloudflare Workers Deployment

Deploy Nuxt Ask AI to Cloudflare Pages with Vectorize and Workers AI for a fully serverless, edge-based ASK ai experience.

Overview

Cloudflare Workers deployment uses:

  • Cloudflare Vectorize: Vector storage for embeddings
  • Workers AI: Runtime embeddings and LLM inference
  • Cloudflare Pages: Static site hosting with edge functions

Architecture

Build Time (Local or CI):

1. Generate embeddings locally (transformers.js or OpenAI)
2. Build static site with Nuxt
3. Output embeddings.jsonl for Vectorize upload

Runtime (Cloudflare Workers):

1. User queries via /api/search or /api/chat
2. Workers AI generates query embedding
3. Vectorize searches similar vectors
4. (Chat API) Workers AI generates RAG response
5. Return results to user

Prerequisites

  1. Cloudflare Account: Free or paid account
  2. Wrangler CLI: Cloudflare's deployment tool
  3. Node.js: Version 20 or later
npm install -g wrangler
wrangler login

Configuration

1. Nuxt Config

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-ai-search'],

  nitro: {
    preset: 'cloudflare-pages',
  },

  aiSearch: {
    embeddings: {
      model: 'bge-base-en-v1.5',
      dimensions: 768,

      // Build time: Use local provider
      buildProvider: {
        preset: 'transformers.js'
      },

      // Runtime: Use Workers AI
      runtimeProvider: {
        preset: 'workers-ai'
      },

      // Use Cloudflare Vectorize for vector storage
      vectorDatabase: {
        provider: 'cloudflare-vectorize',
        indexName: 'ai-search',
        metric: 'cosine',
      },
    },

    // LLM for RAG (chat API)
    llm: {
      provider: 'workers-ai',
      model: '@cf/meta/llama-3.1-8b-instruct',
    },

    // Enable all endpoints
    search: { enabled: true },
    chat: { enabled: true },
  },
})

2. Wrangler Config

Create wrangler.toml in your project root:

name = "my-nuxt-site"
compatibility_date = "2025-09-16"

# Required for MCP SDK compatibility
compatibility_flags = ["nodejs_compat"]

# Workers AI binding
[ai]
binding = "AI"

# Vectorize binding
[[vectorize]]
binding = "VECTORIZE"
index_name = "ai-search"

Important: The nodejs_compat flag is required if you enable the MCP protocol.

Deployment Steps

Step 1: Build Your Site

pnpm generate
# or
npm run generate

This creates:

  • .output/public/ - Static site for deployment
  • .data/ai-search/embeddings.jsonl - Vector embeddings
  • .output/public/_ai-search/bulk - Bulk data (static file)

Step 2: Create Vectorize Index

Create the vector index on Cloudflare:

npx wrangler vectorize create ai-search \
  --dimensions=384 \
  --metric=cosine

Dimensions must match your embedding model:

  • Xenova/all-MiniLM-L6-v2: 384
  • text-embedding-3-small: 1536
  • @cf/baai/bge-base-en-v1.5: 768

Step 3: Upload Embeddings

Upload your embeddings to Vectorize:

npx wrangler vectorize insert ai-search \
  --file=.data/ai-search/embeddings.jsonl \
  --remote

For local testing, use --local instead of --remote.

Step 4: Deploy to Cloudflare Pages

npx wrangler pages deploy .output/public \
  --project-name=my-nuxt-site

Your site is now live! Cloudflare provides a *.pages.dev URL.

CI/CD with GitHub Actions

Automate deployments with GitHub Actions:

# .github/workflows/deploy-cloudflare.yaml
name: Deploy to Cloudflare Pages

on:
  push:
    branches: [main]

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

      - uses: pnpm/action-setup@v2

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm

      - name: Install dependencies
        run: pnpm install

      - name: Build site
        run: pnpm generate
        env:
          # For build-time embeddings (if using OpenAI)
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

      - name: Setup Vectorize
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
        run: |
          # Create index if it doesn't exist
          npx wrangler vectorize create ai-search \
            --dimensions=384 \
            --metric=cosine || true

          # Upload embeddings
          npx wrangler vectorize insert ai-search \
            --file=.data/ai-search/embeddings.jsonl \
            --remote

      - name: Deploy to Cloudflare Pages
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
        run: |
          npx wrangler pages deploy .output/public \
            --project-name=my-nuxt-site

GitHub Secrets Required

Add these secrets to your GitHub repository:

Local Development

Option 1: Mock Bindings (Fast Iteration)

Use local providers during development:

// nuxt.config.ts
export default defineNuxtConfig({
  aiSearch: {
    embeddings: {
      provider: 'transformers.js', // Local, no API calls
      vectorDatabase: {
        provider: 'sqlite-vec', // Local SQLite
      },
    },
  },
})

Option 2: Wrangler Dev (Full Testing)

Test with actual Cloudflare bindings:

# Build first
pnpm generate

# Create local Vectorize index
npx wrangler vectorize create ai-search-local \
  --dimensions=384 --metric=cosine

# Upload embeddings locally
npx wrangler vectorize insert ai-search-local \
  --file=.data/ai-search/embeddings.jsonl \
  --local

# Start local dev server
npx wrangler pages dev .output/public \
  --binding AI=ai \
  --vectorize VECTORIZE=ai-search-local

Access at http://localhost:8788

Workers AI Models

Embedding Models

Available via workers-ai provider:

ModelDimensionsUse Case
@cf/baai/bge-base-en-v1.5768General purpose (recommended)
@cf/baai/bge-small-en-v1.5384Faster, smaller
@cf/baai/bge-large-en-v1.51024Higher quality

LLM Models

For the llm configuration:

ModelSizeUse Case
@cf/meta/llama-3.1-8b-instruct8BRecommended for Ask API
@cf/meta/llama-3.2-3b-instruct3BFaster, lighter
@cf/mistral/mistral-7b-instruct-v0.17BAlternative quality

Note: Small models (<7B) may not perform well with query rewriting. Set queryRewriting.enabled: false for models <7B.

Costs & Limits

Cloudflare Vectorize

Free Tier:

  • 5M queries/month
  • 10M vectors stored
  • 1536 max dimensions

Paid:

  • $0.04 per 1M queries
  • $0.04 per 1M stored vector dimensions

Example Cost:

  • 1,000 pages × 5 chunks × 768 dims = 3.84M vector dimensions → Free
  • 100k searches/month → Free

Workers AI

Free Tier:

  • 10k neurons/day (limited models)

Paid Plans:

  • Standard: $0.01 per 1k neurons
  • Embeddings: ~$0.001 per 1k queries (768-dim)
  • LLM: ~$0.01 per 1k tokens generated

Cloudflare Pages

  • Free: Unlimited sites, 500 builds/month, 100k requests/day
  • Pro ($20/month): 5k builds/month, unlimited requests

Best Practices

1. Use OpenAI for Build-Time Embeddings

For production quality and speed:

// nuxt.config.ts
aiSearch: {
  embeddings: {
    model: 'text-embedding-3-small',
    dimensions: 1536,
    buildProvider: {
      preset: 'openai',                    // Fast, reliable build
      apiKey: process.env.OPENAI_API_KEY
    },
    runtimeProvider: {
      preset: 'workers-ai'                 // Free runtime inference
    }
  },
  llm: {
    provider: 'workers-ai',
    model: '@cf/meta/llama-3.1-8b-instruct',
  },
}

2. Enable Caching

Cloudflare automatically caches static assets. Bulk JSONL is served as a static file with no runtime overhead.

3. Monitor Usage

Track your Vectorize and Workers AI usage in the Cloudflare dashboard:

4. Use Environment-Specific Configs

// nuxt.config.ts
const isDev = process.env.NODE_ENV === 'development'

export default defineNuxtConfig({
  aiSearch: {
    embeddings: {
      model: 'bge-base-en-v1.5',
      buildProvider: {
        preset: 'transformers.js'
      },
      runtimeProvider: {
        preset: isDev ? 'transformers.js' : 'workers-ai'
      },
      vectorDatabase: {
        provider: isDev ? 'sqlite-vec' : 'cloudflare-vectorize',
        indexName: isDev ? undefined : 'ai-search'
      },
    },
  },
})

Troubleshooting

Build Errors

"Local DB provider configured with Cloudflare preset"

  • Cause: Using sqlite-vec with cloudflare-pages preset
  • Fix: Change embeddings.vectorDatabase.provider to 'cloudflare-vectorize'

"Could not load workers-ai-provider"

  • Cause: Missing dependency
  • Fix: pnpm add workers-ai-provider

Runtime Errors

"Cannot read property 'VECTORIZE' of undefined"

  • Cause: Vectorize binding not configured in wrangler.toml
  • Fix: Add Vectorize binding configuration (see above)

"AI binding not found"

  • Cause: Workers AI binding missing
  • Fix: Add [ai] section to wrangler.toml

"nodejs_compat flag required"

  • Cause: MCP SDK requires Node.js compatibility
  • Fix: Add compatibility_flags = ["nodejs_compat"] to wrangler.toml

Vector Dimension Mismatch

"Vector dimension mismatch"

  • Cause: Build embeddings don't match Vectorize index dimensions
  • Fix: Ensure embeddings.dimensions matches Vectorize index:
    # Delete old index
    npx wrangler vectorize delete ai-search
    

Create new index with correct dimensions

npx wrangler vectorize create ai-search --dimensions=1536 --metric=cosine

Re-upload embeddings

npx wrangler vectorize insert ai-search --file=.data/ai-search/embeddings.jsonl --remote


## Migration from Other Platforms

### From Vercel/Netlify

1. Change Nitro preset to `'cloudflare-pages'`
2. Update vector DB provider to `'cloudflare-vectorize'`
3. Add `wrangler.toml` configuration
4. Follow deployment steps above

### From sqlite-vec (Local)

```bash
# 1. Update config
# Change embeddings.vectorDatabase.provider to 'cloudflare-vectorize'

# 2. Rebuild
pnpm generate

# 3. Create Vectorize index
npx wrangler vectorize create ai-search --dimensions=384 --metric=cosine

# 4. Upload embeddings
npx wrangler vectorize insert ai-search \
--file=.data/ai-search/embeddings.jsonl --remote

# 5. Deploy
npx wrangler pages deploy .output/public

Advanced Configuration

Custom Domains

Configure custom domains in Cloudflare Pages dashboard:

  1. Go to Workers & Pages → Your Project → Custom domains
  2. Add your domain
  3. Follow DNS configuration instructions

Environment Variables

Set runtime environment variables:

npx wrangler pages deployment create \
  --project-name=my-nuxt-site \
  --branch=production \
  --commit-hash=$(git rev-parse HEAD) \
  --commit-message="Deploy with env vars" \
  --env-var KEY=VALUE

Multiple Environments

Use different Vectorize indexes per environment:

// nuxt.config.ts
const indexName = process.env.NODE_ENV === 'production'
  ? 'ai-search-prod'
  : 'ai-search-dev'

export default defineNuxtConfig({
  aiSearch: {
    embeddings: {
      model: 'bge-base-en-v1.5',
      buildProvider: { preset: 'transformers.js' },
      runtimeProvider: { preset: 'workers-ai' },
      vectorDatabase: {
        provider: 'cloudflare-vectorize',
        indexName,
      },
    },
  },
})

Testing Deployment

Use the automated test script to validate your deployment:

# From repo root
pnpm test:cloudflare

# With cleanup (deletes test resources)
pnpm test:cloudflare --cleanup

This script:

  1. Builds the site
  2. Creates Vectorize index
  3. Uploads embeddings
  4. Deploys to Cloudflare Pages
  5. Tests all API endpoints
  6. Optionally cleans up resources

Next Steps

Resources

Did this page help you?