Self-Hosting Guide
Deploy Eryxon Flow on your own infrastructure with full control.
Quick Start (Recommended)
Section titled “Quick Start (Recommended)”The fastest way to get production-ready deployment using our automated script.
Prerequisites
Section titled “Prerequisites”- Node.js 20+
- Git
- A Supabase project (supabase.com - free tier available)
- Your Supabase credentials
One-Command Setup
Section titled “One-Command Setup”# Clone the repositorygit clone https://github.com/SheetMetalConnect/eryxon-flow.gitcd eryxon-flow
# Set your database passwordexport SUPABASE_DB_PASSWORD='your-database-password'
# Run automated setupchmod +x scripts/automate_self_hosting.sh./scripts/automate_self_hosting.shThe script will automatically:
- Install required dependencies (Node.js packages)
- Install Supabase CLI globally (if not present)
- Fix configuration issues
- Link your Supabase project
- Apply database migrations (schema + seed)
- Deploy all Edge Functions
- Run verification checks
Start Development Server
Section titled “Start Development Server”npm run dev# Open http://localhost:5173First User Setup
Section titled “First User Setup”- Navigate to the application
- Click Sign Up
- Enter email and password
- First user automatically becomes admin with a new tenant
Manual Setup (Step by Step)
Section titled “Manual Setup (Step by Step)”Use this for custom configurations or troubleshooting.
1. Create Supabase Project
Section titled “1. Create Supabase Project”- Go to supabase.com → New Project
- Save these credentials from Settings → API:
- Project URL:
https://yourproject.supabase.co - Project ID: The subdomain (e.g.,
yourproject) - Anon key: Public key for frontend
- Service role key: Secret key for backend
- Database password: From project creation
- Project URL:
2. Configure Environment
Section titled “2. Configure Environment”# Copy example filecp .env.example .envEdit .env:
VITE_SUPABASE_URL="https://yourproject.supabase.co"VITE_SUPABASE_PUBLISHABLE_KEY="your-anon-key"VITE_SUPABASE_PROJECT_ID="yourproject"
# Optional: For database scriptsSUPABASE_SERVICE_ROLE_KEY="your-service-role-key"SUPABASE_DB_PASSWORD="your-database-password"3. Link Supabase Project
Section titled “3. Link Supabase Project”# Install Supabase CLInpm install -g supabase
# Login and linksupabase loginsupabase link --project-ref yourproject4. Apply Database Schema
Section titled “4. Apply Database Schema”# Push all migrationssupabase db push
# Verify migrations appliedsupabase migration list5. Run Seed SQL
Section titled “5. Run Seed SQL”Creates storage buckets, RLS policies, and cron jobs:
# Option A: Using Supabase CLIsupabase db execute --file supabase/seed.sql
# Option B: Via Dashboard# Go to SQL Editor, paste seed.sql content, and execute6. Deploy Edge Functions
Section titled “6. Deploy Edge Functions”# Deploy all functionssupabase functions deploy
# Set required secretssupabase secrets set \ SUPABASE_URL="https://yourproject.supabase.co" \ SUPABASE_SERVICE_ROLE_KEY="your-service-role-key"7. Install and Run
Section titled “7. Install and Run”# Install dependenciesnpm ci
# Development modenpm run dev
# Production buildnpm run buildnpm run previewDocker Deployment (Production)
Section titled “Docker Deployment (Production)”Using Pre-built Image
Section titled “Using Pre-built Image”# Pull latest imagedocker pull ghcr.io/sheetmetalconnect/eryxon-flow:latest
# Run containerdocker run -d \ -p 80:80 \ --name eryxon-flow \ --restart unless-stopped \ ghcr.io/sheetmetalconnect/eryxon-flow:latestNote: Pre-built images have demo Supabase credentials. For production, build your own image.
Build Custom Image
Section titled “Build Custom Image”docker build \ --build-arg VITE_SUPABASE_URL="https://yourproject.supabase.co" \ --build-arg VITE_SUPABASE_PUBLISHABLE_KEY="your-anon-key" \ --build-arg VITE_SUPABASE_PROJECT_ID="yourproject" \ -t eryxon-flow .
docker run -d -p 80:80 --name eryxon-flow eryxon-flowDocker Compose (Recommended)
Section titled “Docker Compose (Recommended)”Create docker-compose.yml:
version: '3.8'
services: eryxon-flow: image: ghcr.io/sheetmetalconnect/eryxon-flow:latest # Or use your custom build: # build: # context: . # args: # VITE_SUPABASE_URL: ${VITE_SUPABASE_URL} # VITE_SUPABASE_PUBLISHABLE_KEY: ${VITE_SUPABASE_PUBLISHABLE_KEY} # VITE_SUPABASE_PROJECT_ID: ${VITE_SUPABASE_PROJECT_ID} container_name: eryxon-flow restart: unless-stopped ports: - "80:80" healthcheck: test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"] interval: 30s timeout: 10s retries: 3Start:
docker compose up -dDocker Compose with SSL (Production)
Section titled “Docker Compose with SSL (Production)”Includes Caddy reverse proxy for automatic HTTPS:
version: '3.8'
services: app: image: ghcr.io/sheetmetalconnect/eryxon-flow:latest container_name: eryxon-flow restart: unless-stopped expose: - "80"
caddy: image: caddy:alpine container_name: caddy restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config depends_on: - app
volumes: caddy_data: caddy_config:Create Caddyfile:
your-domain.com { reverse_proxy app:80
header { X-Frame-Options "DENY" X-Content-Type-Options "nosniff" X-XSS-Protection "1; mode=block" Referrer-Policy "strict-origin-when-cross-origin" }}Save the above Docker Compose configuration as docker-compose.prod.yml, then deploy:
docker compose -f docker-compose.prod.yml up -dCloudflare Pages Deployment
Section titled “Cloudflare Pages Deployment”Best for edge deployment with global CDN.
-
Connect Repository
- Go to Cloudflare Pages
- Create a project → Connect your Git repository
-
Configure Build
- Build command:
npm run build - Output directory:
dist
- Build command:
-
Set Environment Variables
Terminal window VITE_SUPABASE_URL=https://yourproject.supabase.coVITE_SUPABASE_PUBLISHABLE_KEY=your-anon-keyVITE_SUPABASE_PROJECT_ID=yourproject -
Deploy
- Cloudflare handles SSL, CDN, and global distribution automatically
Optional Enhancements
Section titled “Optional Enhancements”Email Invitations (Resend)
Section titled “Email Invitations (Resend)”Enable automated email invitations:
supabase secrets set \ RESEND_API_KEY="re_your_api_key" \ APP_URL="https://your-domain.com" \ EMAIL_FROM="Eryxon <noreply@your-domain.com>"Cloudflare Turnstile (CAPTCHA)
Section titled “Cloudflare Turnstile (CAPTCHA)”Add bot protection to auth forms:
- Create widget at Cloudflare Turnstile
- Add to
.env:Terminal window VITE_TURNSTILE_SITE_KEY="your-site-key" - Configure secret key in Supabase Authentication → Captcha Protection
Redis Caching (Upstash)
Section titled “Redis Caching (Upstash)”Improve Edge Function performance:
supabase secrets set \ UPSTASH_REDIS_REST_URL="https://your-redis.upstash.io" \ UPSTASH_REDIS_REST_TOKEN="your-token"CAD Processing Service
Section titled “CAD Processing Service”For server-side CAD file processing (optional):
VITE_CAD_SERVICE_URL="https://your-cad-service.example.com"VITE_CAD_SERVICE_API_KEY="your-api-key"If not configured, browser-based processing is used.
MCP Server (Optional - Local Use Only)
Section titled “MCP Server (Optional - Local Use Only)”The MCP server is NOT part of the deployment stack. It’s an optional local tool for Claude Desktop integration.
What it does:
- Allows Claude Desktop to interact with your database using natural language
- Provides 55 tools for managing jobs, parts, operations via AI
Quick start:
cd mcp-servernpm install && npm run buildexport SUPABASE_URL="https://your-project.supabase.co"export SUPABASE_SERVICE_KEY="your-service-key"npm startComplete setup instructions: See MCP Setup Guide for:
- Local development setup
- Cloud deployment (Railway, Fly.io, Docker)
- Claude Desktop configuration
- All 55 available tools
Note: Your self-hosted application works perfectly without the MCP server. It’s only for developers who want AI assistant integration via Claude Desktop.
Verification
Section titled “Verification”Run the verification script to check your setup:
bash scripts/verify-setup.shChecks:
- ✅ Environment variables
- ✅ Supabase connectivity
- ✅ Database tables
- ✅ Storage buckets (see note below)
- ✅ Dependencies
- ✅ Production build
Note: Storage bucket check may report FAIL (HTTP 400) even when buckets exist. This is expected because the buckets are private (
public: false) and the verification script uses the Anon Key, which cannot list private buckets. Verify manually via SQL:SELECT * FROM storage.buckets;Required buckets:
parts-images,issues,parts-cad,batch-images
Updating Your Deployment
Section titled “Updating Your Deployment”Pull Latest Changes
Section titled “Pull Latest Changes”git pull origin mainnpm ciUpdate Database
Section titled “Update Database”supabase db pushsupabase functions deployRebuild Application
Section titled “Rebuild Application”npm run build
# For Docker:docker compose build --no-cachedocker compose up -d⚠️ Special Attention Points
Section titled “⚠️ Special Attention Points”Critical Configuration Items
Section titled “Critical Configuration Items”-
Environment Variables
- Always use
VITE_SUPABASE_PROJECT_ID(not hardcoded) - Template literals need backticks not quotes:
`https://${var}` - Validate all environment variables before using them
- Always use
-
Database Migrations
- Always run migrations in order
- The
20260127235000_enhance_batch_management.sqlmigration adds:blockedstatus to batch_status enumparent_batch_idcolumn for batch nestingnesting_image_urlandlayout_image_urlcolumns
- Never skip migrations
-
Storage Buckets
- Private buckets require signed URLs (not public URLs)
- We use
createSignedUrl()with 1-year expiry for batch images - Buckets needed:
parts-images,issues,parts-cad,batch-images
-
Edge Functions
- Must be redeployed after code changes
- Check logs if APIs return 502:
supabase functions logs - Verify secrets are set:
supabase secrets list - If experiencing 15s+ timeouts or cold start issues:
- Functions use consolidated handlers to avoid deep module resolution
- Import map (
import_map.json) enables@shared/*path aliases - Circular dependencies in
_shared/folder can cause startup delays
-
SQL Syntax
- ✅ Use
IF EXISTS ... THEN ... END IFblocks - ❌ Don’t use
PERFORM ... WHERE EXISTS(invalid syntax)
- ✅ Use
-
Authentication Trigger
- The
on_auth_user_createdtrigger must exist onauth.users - Without it, new signups won’t get profiles/tenants
- Migration
20260127232000_add_missing_auth_trigger.sqlensures this
- The
Security Checklist
Section titled “Security Checklist”-
.envfile is in.gitignore(never commit) - Service role key is kept secret
- Database password is strong (16+ characters)
- RLS policies are applied (via migrations)
- Storage bucket policies restrict access properly
- HTTPS is enabled in production (use Caddy or Cloudflare)
Performance Tips
Section titled “Performance Tips”- Enable Redis caching for high-traffic deployments
- Use Cloudflare Pages for global edge distribution
- Configure proper database indexes (included in migrations)
- Monitor Edge Function execution times in Supabase dashboard
Common Issues
Section titled “Common Issues”Template Literal Errors
Section titled “Template Literal Errors”If URLs aren’t interpolating correctly, you’re using single quotes instead of backticks:
// Wrongconst url = 'https://${projectId}.supabase.co';
// Correctconst url = `https://${projectId}.supabase.co`;Storage 403 Forbidden Errors
Section titled “Storage 403 Forbidden Errors”Private buckets require signed URLs, not public URLs. Use createSignedUrl() with appropriate expiry.
New Users Can’t Log In
Section titled “New Users Can’t Log In”The on_auth_user_created trigger must exist on auth.users. Without it, new signups won’t get profiles/tenants. Migration 20260127232000_add_missing_auth_trigger.sql ensures this.
Edge Functions Return 502
Section titled “Edge Functions Return 502”Import map might not be deployed. Redeploy all functions:
supabase functions deployMigrations Fail with “Type Already Exists”
Section titled “Migrations Fail with “Type Already Exists””Database has partial state from previous attempts. For fresh setups only (DESTRUCTIVE):
DROP SCHEMA IF EXISTS public CASCADE;CREATE SCHEMA public;GRANT USAGE ON SCHEMA public TO postgres, anon, authenticated, service_role;GRANT ALL ON SCHEMA public TO postgres;GRANT ALL ON SCHEMA public TO service_role;Then re-run: supabase db push
Verify Edge Functions Work
Section titled “Verify Edge Functions Work”Test the health endpoint:
curl https://yourproject.supabase.co/functions/v1/api-jobs \ -H "Authorization: Bearer YOUR_ANON_KEY"Should return JSON (not 404/502).
Cron Jobs Not Running
Section titled “Cron Jobs Not Running”Verify pg_cron extension is enabled:
SELECT * FROM pg_extension WHERE extname = 'pg_cron';If empty, run seed.sql to schedule jobs.