LogoZero

Deployment Guide

Production deployment strategies from single-server to scalable cloud architectures.

This guide covers various deployment strategies for Zero, from simple single-server deployments to scalable cloud architectures. Choose the approach that best fits your requirements and infrastructure.

Deployment Options

Cloudflare Workers

Global edge distribution (Recommended)

Docker

Consistent containerized deployment

Vercel

Serverless frontend deployment

AWS

Scalable cloud architecture

Google Cloud

Serverless container deployment

Zero is optimized for deployment on Cloudflare Workers, providing global edge distribution and excellent performance.

Prerequisites

Required for Cloudflare Workers deployment:

  • Cloudflare account
  • Wrangler CLI installed
  • Domain configured in Cloudflare

Setup Steps

Install Wrangler CLI
Login to Cloudflare
Configure wrangler.jsonc
Set environment variables
Deploy

Install and Setup Wrangler

Terminal
# Install Wrangler CLI
npm install -g wrangler

# Login to Cloudflare
wrangler login

Configure wrangler.jsonc

Zero uses separate Cloudflare Workers for different services. The main configurations are:

Server Worker (apps/server/wrangler.jsonc)

Server Configuration
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "zero-server",
  "compatibility_date": "2025-05-01",
  "compatibility_flags": ["nodejs_compat"],
  "main": "src/main.ts",
  "env": {
    "production": {
      "ai": {
        "binding": "AI"
      },
      "vectorize": [
        {
          "binding": "VECTORIZE",
          "index_name": "threads-vector"
        },
        {
          "binding": "VECTORIZE_MESSAGE", 
          "index_name": "messages-vector"
        }
      ],
      "durable_objects": {
        "bindings": [
          {
            "name": "ZERO_AGENT",
            "class_name": "ZeroAgent"
          },
          {
            "name": "ZERO_MCP",
            "class_name": "ZeroMCP"
          },
          {
            "name": "ZERO_DB",
            "class_name": "ZeroDB"
          },
          {
            "class_name": "DurableMailbox",
            "name": "DURABLE_MAILBOX"
          }
        ]
      }
    }
  }
}

Mail Worker (apps/mail/wrangler.jsonc)

Mail Configuration
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "zero",
  "compatibility_date": "2025-06-27",
  "observability": {
    "enabled": true
  },
  "assets": {
    "directory": "./build/client/",
    "not_found_handling": "single-page-application",
    "html_handling": "force-trailing-slash"
  },
  "env": {
    "local": {
      "name": "zero-local",
      "vars": {
        "VITE_PUBLIC_BACKEND_URL": "http://localhost:8787",
        "VITE_PUBLIC_APP_URL": "http://localhost:3000"
      }
    },
    "staging": {
      "name": "zero-staging", 
      "vars": {
        "VITE_PUBLIC_BACKEND_URL": "https://sapi.0.email",
        "VITE_PUBLIC_APP_URL": "https://staging.0.email"
      }
    },
    "production": {
      "name": "zero-production",
      "vars": {
        "VITE_PUBLIC_BACKEND_URL": "https://api.0.email",
        "VITE_PUBLIC_APP_URL": "https://0.email"
      }
    }
  }
}

Complex Configuration: Zero uses advanced Cloudflare features including Durable Objects, Vectorize, AI, and Queues. Ensure your Cloudflare account has access to these features.

Additional Advanced Features: The production configuration also includes:

  • Workflows: For complex business process automation
  • Hyperdrive: For database connection pooling and acceleration
  • KV Namespaces: For distributed key-value storage
  • Scheduled Events: For background job processing

Required Cloudflare Services

Create Vectorize Indexes
# Create vector indexes for AI features
wrangler vectorize create threads-vector --dimensions=1536 --metric=cosine
wrangler vectorize create messages-vector --dimensions=1536 --metric=cosine
Deploy Durable Objects
# Durable Objects are deployed automatically with the worker
# Ensure these classes are available:
# - ZeroAgent
# - ZeroMCP  
# - ZeroDB
# - DurableMailbox
AI Models
# Zero uses Cloudflare AI models
# Available models:
# - @cf/meta/llama-3.1-70b-instruct
# - @cf/openai/whisper
# - Various embedding models
Create Queues
wrangler queues create thread-queue
wrangler queues create subscribe-queue
wrangler queues create digest-queue
wrangler queues create ai-queue

Environment Variables

Production Environment Variables
# Core Configuration
wrangler secret put DATABASE_URL
wrangler secret put BETTER_AUTH_SECRET
wrangler secret put COOKIE_DOMAIN

# OAuth Configuration  
wrangler secret put GOOGLE_CLIENT_ID
wrangler secret put GOOGLE_CLIENT_SECRET

# AI Services
wrangler secret put OPENAI_API_KEY
wrangler secret put PERPLEXITY_API_KEY
wrangler secret put GROQ_API_KEY
wrangler secret put AI_SYSTEM_PROMPT

# Email Services
wrangler secret put RESEND_API_KEY

# Cache/Storage
wrangler secret put REDIS_URL
wrangler secret put REDIS_TOKEN

# Optional Services
wrangler secret put TWILIO_ACCOUNT_SID
wrangler secret put TWILIO_AUTH_TOKEN
wrangler secret put TWILIO_PHONE_NUMBER
wrangler secret put AUTUMN_SECRET_KEY

Environment Variables: These secrets are set per environment (staging/production) and are encrypted by Cloudflare.

Deploy

Deploy Commands
# Deploy server to Cloudflare Workers
cd apps/server
wrangler deploy

# Deploy mail app to Cloudflare Workers  
cd apps/mail
wrangler deploy

Custom Domain

Custom Domain Setup
# Add custom route
wrangler route add "mail.yourdomain.com/*" zero-email-client

2. Docker Deployment

Deploy using Docker containers for consistent environments across different platforms and infrastructure providers.

Docker Configuration

docker/app/Dockerfile
# Base Stage: Node.js with Alpine Linux  
FROM node:22-alpine AS base

# Dependencies Stage: Install Dependencies
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app

# Install package managers
RUN npm install -g pnpm
RUN pnpm install -g turbo

# Copy dependency files
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json ./
RUN mkdir -p apps packages
COPY apps/*/package.json ./apps/
COPY packages/ ./packages/

RUN pnpm install --prod --ignore-scripts

# Builder Stage: Build the Application
FROM base AS builder
WORKDIR /app

# Install global dependencies
RUN npm install -g pnpm
RUN pnpm install -g turbo

# Copy source code and dependencies
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Build the application
RUN turbo build

# Production Stage: Runtime Environment
FROM base AS runner
WORKDIR /app

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nodejs

# Copy built application
COPY --from=builder --chown=nodejs:nodejs /app/apps/mail/build ./apps/mail/build
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/package.json ./package.json

USER nodejs

EXPOSE 3000

ENV NODE_ENV=production
ENV PORT=3000

CMD ["node", "apps/mail/build/server/index.js"]

Docker Compose

Use Docker Compose for easy multi-service deployment with database and cache services.

docker-compose.prod.yaml
services:
  zero:
    build:
      context: .
      dockerfile: docker/app/Dockerfile
    environment:
      VITE_PUBLIC_BACKEND_URL: ${VITE_PUBLIC_BACKEND_URL:-http://cf-worker.example}
      VITE_PUBLIC_APP_URL: ${VITE_PUBLIC_APP_URL:-http://localhost:3000}
      DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-zerodotemail}
      REDIS_URL: ${REDIS_URL}
      REDIS_TOKEN: ${REDIS_TOKEN:-upstash-local-token}
      RESEND_API_KEY: ${RESEND_API_KEY}
      AI_SYSTEM_PROMPT: ${AI_SYSTEM_PROMPT}
      GROQ_API_KEY: ${GROQ_API_KEY}
    depends_on:
      db:
        condition: service_healthy
      migrations:
        condition: service_completed_successfully
      valkey:
        condition: service_healthy
      upstash-proxy:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '--spider', '--quiet', 'http://127.0.0.1:3000']
      interval: 90s
      timeout: 5s
      retries: 3
      start_period: 10s
    ports:
      - 3000:3000

  db:
    container_name: zerodotemail-db
    image: postgres:17
    restart: unless-stopped
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: zerodotemail
      PGDATA: /var/lib/postgresql/data/pgdata
    ports:
      - 5432:5432
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U postgres']
      interval: 10s
      timeout: 5s
      retries: 5

  valkey:
    container_name: zerodotemail-redis
    image: docker.io/bitnami/valkey:8.0
    environment:
      - ALLOW_EMPTY_PASSWORD=yes
      - VALKEY_DISABLE_COMMANDS=FLUSHDB,FLUSHALL
    ports:
      - 6379:6379
    volumes:
      - valkey-data:/bitnami/valkey/data
    healthcheck:
      test: ['CMD', 'valkey-cli', 'ping']
      interval: 10s
      timeout: 5s
      retries: 5

  upstash-proxy:
    container_name: zerodotemail-upstash-proxy
    image: hiett/serverless-redis-http:latest
    environment:
      REDIS_URL: redis://valkey:6379
      TOKEN: upstash-local-token
    ports:
      - 8079:80
    depends_on:
      valkey:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '--spider', '--quiet', 'http://127.0.0.1:80/health']
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  postgres-data:
  valkey-data:

Deployment Commands

Docker Deployment Commands
# Build and start services
docker-compose -f docker-compose.prod.yaml up -d

# View logs
docker-compose logs -f app

# Scale the application
docker-compose up -d --scale app=3

3. Local Development

For local development and testing, Zero includes a comprehensive Docker Compose setup.

Local Development Setup

Local Development Commands
# Start local development environment
docker-compose -f docker-compose.db.yaml up -d

# Install dependencies
pnpm install

# Start development servers
pnpm dev

Local Services

The local development environment includes:

  • PostgreSQL Database - Primary data storage
  • Valkey (Redis) - Caching and session storage
  • Upstash Proxy - Redis HTTP proxy for serverless compatibility
docker-compose.db.yaml
services:
  db:
    container_name: zerodotemail-db
    image: postgres:17
    restart: unless-stopped
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: zerodotemail
    ports:
      - 5432:5432
    volumes:
      - postgres-data:/var/lib/postgresql/data

  valkey:
    container_name: zerodotemail-redis
    image: docker.io/bitnami/valkey:8.0
    environment:
      - ALLOW_EMPTY_PASSWORD=yes
    ports:
      - 6379:6379
    volumes:
      - valkey-data:/bitnami/valkey/data

  upstash-proxy:
    container_name: zerodotemail-upstash-proxy
    image: hiett/serverless-redis-http:latest
    environment:
      REDIS_URL: redis://valkey:6379
      TOKEN: upstash-local-token
    ports:
      - 8079:80

volumes:
  postgres-data:
  valkey-data:

4. AWS Deployment

Deploy on AWS using various services for a scalable, enterprise-grade architecture.

Architecture Overview

ECS Task Definition

ECS Task Definition
{
  "family": "zero-email-client",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "512",
  "memory": "1024",
  "executionRoleArn": "arn:aws:iam::account:role/ecsTaskExecutionRole",
  "taskRoleArn": "arn:aws:iam::account:role/ecsTaskRole",
  "containerDefinitions": [
    {
      "name": "zero-app",
      "image": "your-account.dkr.ecr.region.amazonaws.com/zero-email-client:latest",
      "portMappings": [
        {
          "containerPort": 3000,
          "protocol": "tcp"
        }
      ],
      "environment": [
        {
          "name": "NODE_ENV",
          "value": "production"
        }
      ],
      "secrets": [
        {
          "name": "DATABASE_URL",
          "valueFrom": "arn:aws:ssm:region:account:parameter/zero/database-url"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/zero-email-client",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ]
}

CDK Deployment Script

infrastructure/cdk-stack.ts
import * as cdk from 'aws-cdk-lib'
import * as ecs from 'aws-cdk-lib/aws-ecs'
import * as ec2 from 'aws-cdk-lib/aws-ec2'
import * as rds from 'aws-cdk-lib/aws-rds'
import * as elasticache from 'aws-cdk-lib/aws-elasticache'

export class ZeroEmailStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props)

    // VPC
    const vpc = new ec2.Vpc(this, 'ZeroVPC', {
      maxAzs: 2,
      natGateways: 1,
    })

    // RDS Database
    const database = new rds.DatabaseInstance(this, 'ZeroDatabase', {
      engine: rds.DatabaseInstanceEngine.postgres({
        version: rds.PostgresEngineVersion.VER_15,
      }),
      instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.T3,
        ec2.InstanceSize.MICRO
      ),
      vpc,
      multiAz: true,
      allocatedStorage: 20,
      storageEncrypted: true,
      deletionProtection: true,
    })

    // ElastiCache Redis
    const redis = new elasticache.CacheCluster(this, 'ZeroRedis', {
      engine: 'redis',
      cacheNodeType: 'cache.t3.micro',
      numCacheNodes: 1,
      vpc,
    })

    // ECS Cluster
    const cluster = new ecs.Cluster(this, 'ZeroCluster', {
      vpc,
    })

    // ECS Service
    const taskDefinition = new ecs.FargateTaskDefinition(this, 'ZeroTaskDef', {
      memoryLimitMiB: 1024,
      cpu: 512,
    })

    const container = taskDefinition.addContainer('zero-app', {
      image: ecs.ContainerImage.fromRegistry('your-ecr-repo:latest'),
      memoryLimitMiB: 1024,
      environment: {
        NODE_ENV: 'production',
      },
      secrets: {
        DATABASE_URL: ecs.Secret.fromSecretsManager(databaseSecret),
      },
      logging: ecs.LogDrivers.awsLogs({
        streamPrefix: 'zero-app',
      }),
    })

    container.addPortMappings({
      containerPort: 3000,
      protocol: ecs.Protocol.TCP,
    })

    new ecs.FargateService(this, 'ZeroService', {
      cluster,
      taskDefinition,
      desiredCount: 2,
      assignPublicIp: false,
    })
  }
}

Production Environment Variables

Ensure all production environment variables are properly set before deployment. Missing variables will cause deployment failures.

Required Environment Variables
# Database
DATABASE_URL=postgresql://user:password@prod-db:5432/zerodotemail

# Authentication
BETTER_AUTH_SECRET=your-super-long-production-secret-key
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret

# AI Services  
OPENAI_API_KEY=your-openai-api-key
PERPLEXITY_API_KEY=your-perplexity-api-key
GROQ_API_KEY=your-groq-api-key
AI_SYSTEM_PROMPT=your-custom-system-prompt

# Email Services
RESEND_API_KEY=your-resend-api-key

# Cache/Storage
REDIS_URL=your-redis-connection-string
REDIS_TOKEN=your-redis-token

# Application URLs
VITE_PUBLIC_BACKEND_URL=https://api.yourdomain.com
VITE_PUBLIC_APP_URL=https://yourdomain.com

# Optional Services
TWILIO_ACCOUNT_SID=your-twilio-sid
TWILIO_AUTH_TOKEN=your-twilio-token
TWILIO_PHONE_NUMBER=your-twilio-phone

Security Considerations

Security is critical for email applications. Ensure all security measures are implemented before production deployment.

Essential Security Checklist

  • HTTPS Only - Force all connections to use HTTPS
  • Secure Headers - Implement HSTS, CSP, and security headers
  • Authentication - Secure OAuth configuration and session management
  • Database Security - Encrypted connections and proper access controls
  • Environment Variables - Secure secret management
  • Regular Updates - Keep dependencies and base images updated
  • Access Controls - Proper user permissions and rate limiting
  • Monitoring - Error tracking and security monitoring

Security Headers Configuration

Security Headers Example
// For Cloudflare Workers - configured in wrangler.jsonc
{
  "headers": {
    "Strict-Transport-Security": "max-age=31536000; includeSubDomains",
    "X-Content-Type-Options": "nosniff",
    "X-Frame-Options": "DENY",
    "X-XSS-Protection": "1; mode=block",
    "Referrer-Policy": "strict-origin-when-cross-origin"
  }
}

Monitoring and Health Checks

Proper monitoring ensures your Zero deployment stays healthy and performs optimally.

Health Check Endpoints

Zero includes built-in health check endpoints:

  • /health - Basic application health
  • /health/db - Database connectivity
  • /health/redis - Cache connectivity
  • /health/detailed - Comprehensive system status

Monitoring Setup

Basic Monitoring Commands
# Check application status
curl https://your-domain.com/health

# Monitor Cloudflare Workers logs
wrangler tail --env production

# Check Docker container health
docker ps --filter "name=zero"
docker logs zero-app

Troubleshooting

Common Deployment Issues

Database Connection Failures

Database Troubleshooting
# Test database connectivity
psql $DATABASE_URL -c "SELECT version();"

# Check connection string format
echo $DATABASE_URL

Cloudflare Workers Issues

Workers Troubleshooting
# Check worker status
wrangler whoami
wrangler deployments list

# View real-time logs
wrangler tail --env production

Environment Variable Problems

Environment Troubleshooting
# List all secrets (for Cloudflare Workers)
wrangler secret list

# Test specific environment variables
wrangler secret put TEST_VAR --env production

Build Failures

Build Troubleshooting
# Clear build cache
pnpm store prune
rm -rf node_modules
pnpm install

# Rebuild packages
pnpm build --force

Getting Help

If you encounter issues during deployment:

  1. Check the GitHub Issues for similar problems
  2. Review the application logs for error messages
  3. Verify all environment variables are correctly set
  4. Ensure your deployment platform has the required permissions and resources
  5. Join the community Discord for deployment support