Getting ComfyDeploy running locally is a bunch of work; but is a dream, and a good helpful one; in which you gotta spend all your weekend just to get things running...
What You'll See
By completing this guide, you'll gain hands-on experience with:
Full-Stack Development:
- 🚀 Modern Web Stack: React/Vite frontend + Python FastAPI backend
- Database Management: PostgreSQL with Drizzle ORM, schema design, and migrations
- Authentication: Clerk integration with JWT validation
- Billing & Subscriptions: Autumn API for SaaS monetization and feature limits
- Containerization: Docker Compose for local development infrastructure
Cloud & Serverless Architecture:
- Serverless Computing: Deploy GPU-accelerated Python functions with Modal
- Persistent Storage: Modal volumes for model storage and file management
- Cloud Storage: AWS S3 integration for file uploads and assets
- Webhooks & Tunneling: ngrok for local development with cloud callbacks
DevOps & Infrastructure:
- Monorepo Management: Turborepo for managing multiple applications
- Environment Configuration: Managing secrets and API keys across services
- CI/CD Concepts: Understanding deployment pipelines and environments
- Debugging: Reading logs, troubleshooting errors, and fixing common issues
AI/ML Deployment:
- ComfyUI Integration: Running AI image generation workflows in production
- GPU Management: Serverless GPU allocation and concurrency limits
- Usage Tracking: Monitoring GPU credits and resource consumption
Free Credits & Resources:
- 💰 $5 USD in Modal Credits: Sign up at https://modal.com/ to get free credits for GPU compute
- 🎯 Run Real Workflows: Deploy and execute ComfyUI workflows on production-grade infrastructure
- 🧪 Experiment with AI Models: Test different models, custom nodes, and workflow configurations
- 📚 Production-Ready Codebase: Learn from a real SaaS application architecture
Skills You'll Develop:
- Setting up complex development environments with multiple services
- Integrating third-party APIs (Clerk, Autumn, AWS, Modal, GitHub)
- Managing database schemas and foreign key relationships
- Debugging full-stack applications across frontend, backend, and cloud services
- Understanding SaaS billing models and feature gating
- Working with serverless architectures and GPU compute
By the end of this guide, you'll have a fully functional local development environment for ComfyDeploy, ready to build features, fix bugs, and deploy AI workflows! 🚀
Prerequisites
Required Software & Tools
Install these dependencies before starting:
Core Development Tools:
-
Node.js (v18 or higher) - JavaScript runtime
- macOS:
brew install node - Or download from https://nodejs.org/
- macOS:
-
Bun (v1.0+) - Fast JavaScript package manager and runtime
- macOS:
brew install oven-sh/bun/bun - Or:
curl -fsSL https://bun.sh/install | bash
- macOS:
-
Python (3.12.0 exactly) - Required for API server
- macOS:
brew install python@3.12 - Or use pyenv:
pyenv install 3.12.0
- macOS:
-
uv - Fast Python package manager
- macOS:
brew install uv - Or:
curl -LsSf https://astral.sh/uv/install.sh | sh
- macOS:
Infrastructure:
-
Docker Desktop - For PostgreSQL and Redis
- macOS:
brew install --cask docker - Or download from https://www.docker.com/products/docker-desktop/
- macOS:
-
PostgreSQL Client (psql) - For database management
- macOS:
brew install postgresql@16 - Or use the client included with Docker
- macOS:
Cloud & Networking:
-
AWS CLI - For S3 bucket management
- macOS:
brew install awscli - Or:
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg" && sudo installer -pkg AWSCLIV2.pkg -target /
- macOS:
-
Modal CLI - For serverless deployment
- Installed via Python:
pip install modal(or included in uv sync)
- Installed via Python:
-
ngrok - For local tunneling
- macOS:
brew install ngrok/ngrok/ngrok - Or download from https://ngrok.com/download
- macOS:
Optional but Recommended:
-
Git (latest version) - Version control
- macOS:
brew install git
- macOS:
-
jq - JSON processor for debugging API responses
- macOS:
brew install jq
- macOS:
Required Service Accounts & API Keys
You'll need accounts and API keys for:
- Clerk (authentication) - get your publishable key, secret key, and JWT public key
- Autumn (billing/subscriptions) - get your secret key
- AWS S3 (file storage) - create a bucket and get your credentials
- Modal (serverless compute) - create an account and get your token
- ngrok (for local development) - get your authtoken
- GitHub (API access) - create a personal access token for fetching ComfyUI version info
Step 1: Clone and Install Dependencies
git clone https://github.com/comfy-deploy/comfydeploy.git
cd comfydeploy
# IMPORTANT: Initialize git submodules to pull in the actual code
# The apps/api and apps/app directories are git submodules
git submodule init
git submodule update
# Install API dependencies (Node.js packages)
cd apps/api
bun install
# Install Python dependencies using uv (faster than pip)
# This creates a virtual environment at apps/api/.venv
uv sync
# Install frontend dependencies
cd ../app
bun install
Note: The repository uses git submodules for apps/api and apps/app. You must run git submodule init && git submodule update to pull in the actual application code. Without this step, the directories will be empty.
Python Dependencies: The API uses pyproject.toml instead of requirements.txt. Use uv sync to install Python dependencies - it will automatically create a virtual environment at apps/api/.venv with Python 3.12.0.
Step 2: Configure Environment Variables
Required Service Accounts & API Keys
Before configuring, sign up for these services and get your API keys:
-
Clerk (Authentication) - https://clerk.com/
- Dashboard: https://dashboard.clerk.com/
- Get:
CLERK_PUBLISHABLE_KEY,CLERK_SECRET_KEY,CLERK_PUBLIC_JWT_KEY - Go to: Dashboard → Your App → API Keys → Find "JWKS Public Key" section
-
Autumn (Billing) - https://useautumn.com/
- Get:
AUTUMN_SECRET_KEY - Authenticate CLI:
npx atmn auth - Push config:
npx atmn push(after configuringautumn.config.ts)
- Get:
-
AWS S3 (File Storage) - https://aws.amazon.com/s3/
- Create bucket:
aws s3 mb s3://comfydeploy-dev-storage --region us-east-1 - Get credentials from AWS IAM or use existing AWS CLI credentials
- Get: Access Key ID, Secret Access Key
- Create bucket:
Example S3 Bucket Configuration:
Here's one way to configure your S3 bucket for ComfyDeploy. This is a simple approach that works, but you may have better security methods (e.g., using presigned URLs, CloudFront, etc.):
1. Access Control List (ACL):
- Public READ access - Grants READ permission to "AllUsers" for public file access
- Owner has FULL_CONTROL
2. CORS Configuration:
- Allows all origins (
*) - Allows all HTTP methods (GET, PUT, POST, DELETE, HEAD)
- Allows all headers (
*) - Exposes ETag header
- Max age: 3000 seconds
3. Bucket Policy:
- No specific bucket policy required (can be left empty)
4. Public Access Block:
- All public access blocks disabled for public file access
-
BlockPublicAcls: false -
IgnorePublicAcls: false -
BlockPublicPolicy: false -
RestrictPublicBuckets: false
5. Bucket Structure:
-
assets/upload/- For uploaded assets -
outputs/runs/- For workflow output files
⚠️ Security Note: This configuration makes the bucket publicly readable by anyone on the internet. This is a simple approach that works for development, but you may want to implement more secure methods like presigned URLs, CloudFront distributions, or bucket policies for production use.
To configure CORS via AWS CLI:
# Create a cors.json file
cat > cors.json << 'EOF'
{
"CORSRules": [
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
"AllowedOrigins": ["*"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3000
}
]
}
EOF
# Apply CORS configuration
aws s3api put-bucket-cors --bucket YOUR-BUCKET-NAME --cors-configuration file://cors.json --region YOUR-REGION
# Set public read ACL
aws s3api put-bucket-acl --bucket YOUR-BUCKET-NAME --acl public-read --region YOUR-REGION
# Disable public access blocks
aws s3api delete-public-access-block --bucket YOUR-BUCKET-NAME --region YOUR-REGION
To verify your bucket configuration:
# Check CORS
aws s3api get-bucket-cors --bucket YOUR-BUCKET-NAME --region YOUR-REGION
# Check ACL
aws s3api get-bucket-acl --bucket YOUR-BUCKET-NAME --region YOUR-REGION
# Check public access block
aws s3api get-public-access-block --bucket YOUR-BUCKET-NAME --region YOUR-REGION
-
Modal (Serverless Compute) - https://modal.com/
- Dashboard: https://modal.com/home → Settings → API Tokens
- Get:
MODAL_TOKEN_ID,MODAL_TOKEN_SECRET
-
ngrok (Local Tunneling) - https://ngrok.com/
- Dashboard: https://dashboard.ngrok.com/
- Get:
NGROK_AUTHTOKENfrom "Your Authtoken" section
-
GitHub (API Access for ComfyUI Version Info) - https://github.com/
- Settings → Developer settings → Personal access tokens → Tokens (classic)
- Generate new token (classic) with
public_reposcope - Get:
GITHUB_TOKEN(starts withghp_)
Configure Environment Files
Copy the example env files and fill in your credentials:
cp apps/api/.env.example apps/api/.env
cp apps/app/.env.example apps/app/.env
Generate Encryption Key
Generate a secret encryption key for the API:
cd apps/api
source .venv/bin/activate
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode('utf-8'))"
Copy the output and add it to apps/api/.env as SECRET_ENCRYPTION_KEY.
Get Clerk JWT Public Key
- Go to Clerk Dashboard → Your App → API Keys
- Scroll down to the "JWKS Public Key" section
- Copy the entire key (starts with
-----BEGIN PUBLIC KEY-----) - Add it to
apps/api/.envasCLERK_PUBLIC_JWT_KEY
Example Configuration
apps/api/.env (key variables):
ENV=development
DATABASE_URL="postgresql://postgres:postgres@localhost:5480/verceldb"
CLERK_PUBLIC_JWT_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
CLERK_SECRET_KEY="sk_test_..."
AUTUMN_SECRET_KEY="am_sk_test_..."
SPACES_BUCKET_V2="comfydeploy-dev-storage"
SPACES_ENDPOINT_V2="https://s3.amazonaws.com"
SPACES_REGION_V2="us-east-1"
SPACES_KEY_V2="AKIA..."
SPACES_SECRET_V2="..."
MODAL_ENVIRONMENT="dev"
MODAL_TOKEN_ID="ak-..."
MODAL_TOKEN_SECRET="as-..."
SHARED_MODEL_VOLUME_NAME="comfydeploy-dev-public-models" # Check with: modal volume list
PUBLIC_MODEL_VOLUME_NAME="comfydeploy-dev-public-models" # Should match your Modal volume name
NGROK_AUTHTOKEN="..."
GITHUB_TOKEN="ghp_..."
SECRET_ENCRYPTION_KEY="..."
CURRENT_API_URL="https://your-ngrok-url.ngrok-free.dev" # ⚠️ MUST be ngrok URL, not localhost!
⚠️ CRITICAL: CURRENT_API_URL must be set to your ngrok URL (from Step 5.5), NOT http://localhost:3011. Modal functions run in the cloud and cannot reach localhost. See Step 5.5 for how to get your ngrok URL.
Note: To verify your Modal volume name, run modal volume list and use the exact name that appears in the output. See Step 5.6 for details.
apps/app/.env:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_..."
NEXT_PUBLIC_CD_API_URL="http://localhost:3011"
Step 2.5: Set Up Autumn Billing Integration
What is Autumn?
Autumn is a billing and subscription management API for SaaS applications. It handles product definitions, pricing plans, feature limits, and usage tracking. ComfyDeploy uses Autumn to manage different subscription tiers (free, business, enterprise) and enforce feature limits like machine count, workflow limits, and GPU concurrency.
Understanding autumn.config.ts
The file apps/api/autumn.config.ts defines your entire billing structure. Here's what you need to know:
-
Features: Define what users can do (e.g.,
machineLimit,gpuConcurrencyLimit,workflowLimit) -
Products: Combine features into subscription tiers (e.g.,
free,business,enterprise) - Pricing: Set monthly/yearly prices and included usage amounts
Example Configuration
Here's a simplified version of what's in the config:
// Define features
export const machineLimit = feature({
id: "machine_limit",
name: "Machine Limit",
type: "continuous_use",
});
export const gpuConcurrencyLimit = feature({
id: "gpu_concurrency_limit",
name: "GPU Concurrency Limit",
type: "continuous_use",
});
// Define products (subscription tiers)
export const free = product({
id: "free",
name: "Free",
is_default: true,
items: [
featureItem({
feature_id: machineLimit.id,
included_usage: 3, // Free tier gets 3 machines
}),
featureItem({
feature_id: gpuConcurrencyLimit.id,
included_usage: 1, // Can run 1 GPU job at a time
}),
],
});
export const business = product({
id: "business",
name: "Business",
items: [
pricedFeatureItem({
feature_id: gpuCredit.id,
price: 0.01,
included_usage: 0,
usage_model: "pay_per_use",
}),
featureItem({
feature_id: machineLimit.id,
included_usage: 100, // Business tier gets 100 machines
}),
featureItem({
feature_id: gpuConcurrencyLimit.id,
included_usage: 10, // Can run 10 GPU jobs at a time
}),
],
});
Pushing Configuration to Autumn
After updating autumn.config.ts, push your changes to Autumn:
cd apps/api
npx atmn push
This command validates your config and syncs it with the Autumn API. You'll see output confirming which products and features were created/updated.
Verifying Your Setup
Check that plans are active in the Autumn dashboard:
- Go to https://dashboard.autumn.dev (or your Autumn instance)
- Navigate to Products → verify your products are listed
- Check that features have the correct limits
- Test by creating a subscription in the dashboard
Common Gotcha: Missing Feature Definitions
If you see "Max Machines Exceeded" errors even though users have paid plans, it's usually because:
- The
machineLimitfeature isn't defined in the product - The
included_usageis set too low - The config wasn't pushed to Autumn after changes
Fix: Add the missing feature to your product and run npx atmn push again.
Push Configuration to Autumn:
After setting up your .env file with AUTUMN_SECRET_KEY, push the billing configuration:
cd apps/api
npx atmn push
This will push all features and products defined in autumn.config.ts to your Autumn sandbox environment. You should see:
- ✅ 20 features pushed (GPU types, credits, limits, etc.)
- ✅ 10 products pushed (Free, Creator, Deployment, Business plans + add-ons)
View your products at: http://app.useautumn.com/sandbox/products
🔑 GitHub Token Setup
The API uses GitHub's API to fetch the latest ComfyUI and ComfyUI-Deploy version information. Without a GitHub token, the /api/latest-hashes endpoint will fail.
Create a GitHub Classic Token:
- Go to https://github.com/settings/tokens
- Click "Generate new token" → "Generate new token (classic)"
- Give it a descriptive name (e.g., "ComfyDeploy Local Dev")
- Select scopes:
- ✅
public_repo(Access public repositories)
- ✅
- Click "Generate token"
- Copy the token (starts with
ghp_) - Add to
apps/api/.env:
GITHUB_TOKEN="ghp_your_token_here"
Why is this needed?
The API fetches:
- Latest ComfyUI commit hash from
comfyanonymous/ComfyUI - Latest ComfyUI release information
- Latest ComfyUI-Deploy commit hash from
bennykok/comfyui-deploy
Without the token, you'll see errors like:
ERROR:api.routes.comfy_node:Error fetching repo info: Illegal header value b'Bearer '
INFO: 127.0.0.1:xxxxx - "GET /api/latest-hashes HTTP/1.1" 404 Not Found
📦 Modal Volume Configuration
What are Modal Volumes?
Modal volumes are persistent storage for your serverless ComfyUI deployments. They store:
- Public models (shared across all users)
- Private models (user-specific or organization-specific)
- Workflow files and custom nodes
Configure Volume Names:
For local development, you need to set the public model volume name in apps/api/.env:
SHARED_MODEL_VOLUME_NAME="comfydeploy-dev-public-models"
PUBLIC_MODEL_VOLUME_NAME="comfydeploy-dev-public-models"
Why is this needed?
Without these environment variables, Modal will fail to create volumes with the error:
InvalidError: Invalid Volume name: ''.
Names may contain only alphanumeric characters, dashes, periods, and underscores,
must be shorter than 64 characters, and cannot conflict with App ID strings.
Volume Naming Convention:
-
Local Development: Use a simple name like
comfydeploy-dev-public-models -
Production: Use organization-specific names like
models_org_<org_id> -
Private Volumes: Automatically named as
models_<user_id>ormodels_org_<org_id>
How Volumes Work:
- When you deploy a serverless machine, Modal creates volumes with
create_if_missing=True - The public volume is shared across all deployments for common models
- Private volumes are created per-user or per-organization for custom models
- Volumes persist between deployments and are mounted at runtime
Verifying Volume Creation:
After starting the API server, check the Modal dashboard:
- Go to https://modal.com/home
- Navigate to "Volumes" in the sidebar
- You should see
comfydeploy-dev-public-modelslisted - The volume will be created automatically on first use
Step 3: Start Docker Services
cd apps/api
docker compose up -d
This starts PostgreSQL, Redis, pg_proxy, and serverless-redis-http containers.
✓ Verify: Check that all services are running:
docker compose ps
# Expected output should show:
# - api-postgres-1 (port 5480)
# - api-redis-1 (port 6379)
# - api-pg_proxy-1 (port 5481)
# - api-serverless-redis-http-1 (port 8079)
If a service failed to start, check logs:
docker compose logs postgres # or redis, etc.
Step 4: Run Database Migrations
cd apps/api
bun run migrate-local
⚠️ Troubleshooting Connection Timeout:
If migrations fail with CONNECT_TIMEOUT localhost:5480, VS Code's port forwarding may be conflicting:
# Check what's listening on port 5480
lsof -i :5480
# If you see "Code Helper" process, kill it:
kill -9 <PID>
# Then retry migrations
bun run migrate-local
✓ Verify: Check that migrations completed without errors. You should see output like:
Database is live
Migrating... DB 1
Done!
Step 4.5: Understanding the Database Architecture
Schema Structure
All ComfyDeploy tables live in the comfyui_deploy schema (not the default public schema). This keeps your data organized and separate from other PostgreSQL objects.
Key Tables and Relationships
Here's the core data model:
users (root table)
├── machines (user_id → users.id)
│ ├── machine_versions (machine_id → machines.id)
│ └── workflow_runs (machine_id → machines.id)
├── workflows (user_id → users.id)
│ ├── workflow_versions (workflow_id → workflows.id)
│ └── workflow_runs (workflow_id → workflows.id)
├── user_volume (user_id → users.id)
│ └── models (user_volume_id → user_volume.id)
└── deployments (user_id → users.id)
Critical Foreign Key Relationships
-
machines.user_id→users.id(cascade delete) -
user_volume.user_id→users.id(no cascade) -
workflow_runs.machine_id→machines.id(set null on delete) -
models.user_volume_id→user_volume.id(cascade delete)
Verify Your Schema
Check that tables exist and have the right structure:
# Connect to the database
psql postgresql://postgres:postgres@localhost:5480/verceldb
# List all tables in the comfyui_deploy schema
\dt comfyui_deploy.*
# Check the users table structure
\d comfyui_deploy.users
# Count rows in key tables
SELECT COUNT(*) FROM comfyui_deploy.users;
SELECT COUNT(*) FROM comfyui_deploy.machines;
SELECT COUNT(*) FROM comfyui_deploy.workflows;
Why Fresh Docker Restarts Wipe Data
When you run docker-compose down, the PostgreSQL container is removed along with its data volume. This is why you need to:
- Re-run migrations (
bun run migrate-local) - Recreate user records (see Step 6)
In production, you'd use persistent volumes to prevent data loss.
Step 5: Start the Development Servers
Start both servers (they will run in the background):
# Start API server (runs on port 3011)
cd apps/api
bun run dev
# Start Frontend server (runs on port 3001)
cd apps/app
bun run dev
✓ Verify API Server:
curl http://localhost:3011/
# Expected: {"detail":"Not Found"} (this is normal - root path returns 404)
# Check if server is responding
curl -I http://localhost:3011/
# Expected: HTTP/1.1 404 Not Found (server is running)
✓ Verify Frontend: Open http://localhost:3001 in your browser
- You should see the Comfy Deploy login page
- Try logging in with your Clerk credentials
Note: The API server may show some SyntaxWarning messages about invalid escape sequences - these are harmless and don't affect functionality.
Step 5.5: Set Up ngrok Tunnel (CRITICAL - Required for Local Development)
⚠️ CRITICAL STEP: ngrok is required for local development with Modal. Without it, Modal functions running in the cloud cannot send status updates back to your local API server, and workflows will get stuck in "not-started" status.
Install ngrok
If you haven't installed ngrok yet:
# macOS
brew install ngrok
# Or download from https://ngrok.com/download
Configure ngrok Authentication
Get your authtoken from the ngrok dashboard:
- Dashboard: https://dashboard.ngrok.com/get-started/your-authtoken
- Copy your authtoken
Then configure ngrok:
ngrok config add-authtoken YOUR_AUTHTOKEN
Example:
ngrok config add-authtoken YOUR_AUTHTOKEN
Start ngrok Tunnel
Launch ngrok to create a public tunnel to your local API:
ngrok http 3011
You'll see output like this:
Session Status online
Account Your Name (Plan: Free)
Version 3.x.x
Region United States (us)
Latency -
Web Interface http://127.0.0.1:4040
Forwarding https://ngrok_generated_url.dev -> http://localhost:3011
Copy the Forwarding URL (the https one, e.g., https://shelia-nondedicative-kaidence.ngrok-free.dev)
Update Environment Variables
Add the ngrok URL to your apps/api/.env file:
NGROK_DOMAIN="https://your-ngrok-url.ngrok-free.dev"
Example:
NGROK_DOMAIN="https://ngrok_generated_url.dev"
Important: Keep ngrok running in a separate terminal window. If ngrok stops, Modal functions won't be able to communicate with your API.
✓ Verify ngrok is working:
# Test that your ngrok URL reaches your local API
curl https://your-ngrok-url.ngrok-free.dev/
# Expected: {"detail":"Not Found"} (this is normal - root path returns 404)
Step 5.6: Set Up Modal for Serverless Compute
What is Modal?
Modal is a serverless platform for running Python code with GPU access. ComfyDeploy uses Modal to run ComfyUI workflows in the cloud without needing to manage servers. When you create a "serverless machine" in ComfyDeploy, it's actually a Modal app that gets deployed and runs your workflows.
Creating a Modal Environment
Modal uses "environments" to organize deployments (dev, staging, production). You've already configured this in your .env:
MODAL_ENVIRONMENT="dev"
MODAL_TOKEN_ID="your-modal-token-id"
MODAL_TOKEN_SECRET="your-modal-token-secret"
⚠️ CRITICAL: Deploying the volume-operations App
This step is REQUIRED! Without deploying the
volume-operationsapp, model downloads will fail and workflows won't be able to access models from Modal volumes.
The volume-operations Modal app handles downloading models to Modal volumes. Deploy it:
cd apps/api
modal deploy src/modal_apps/modal_downloader.py::modal_downloader_app
What this does:
- Creates a Modal app called "volume-operations" in your Modal workspace
- Enables downloading models from HuggingFace, CivitAI, and arbitrary URLs
- Uploads models to Modal volumes for use in workflows
- Required for model management functionality to work
✓ Verify Modal Deployment:
# List all Modal apps in your workspace
modal app list
# Expected output should include "volume-operations"
Check the Modal dashboard at https://modal.com/apps to see your deployed apps.
Common Issues:
- If you see "Function not found" errors when downloading models, you likely forgot this step
- The app must be deployed to the same Modal environment specified in
MODAL_ENVIRONMENT - Re-run the deploy command if you change Modal environments
Configuring Modal Volumes
Modal volumes are persistent storage that Modal functions can access. ComfyDeploy creates volumes for:
-
comfydeploy-dev-public-models: Shared models available to all workflows -
models_org_<org_id>: Organization-specific models -
models_<user_id>: User-specific models
Check your existing Modal volumes:
modal volume list
You should see output like:
Volumes in environment 'dev'
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┓
┃ Name ┃ Created at ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━┩
│ comfydeploy-dev-public-models │ 2025-11-15 22:23 +03 │
│ models_org_35Wmd12wjmWmYICCWpxZykn4YVq │ 2025-11-15 22:18 +03 │
│ public-models │ 2025-10-18 19:05 +03 │
└─────────────────────────────────────────┴──────────────────────┘
Update your .env with the correct volume name:
Look for the volume that matches your environment (e.g., comfydeploy-dev-public-models for dev environment), then update apps/api/.env:
# Modal Volume Configuration
SHARED_MODEL_VOLUME_NAME=comfydeploy-dev-public-models
PUBLIC_MODEL_VOLUME_NAME=comfydeploy-dev-public-models
Important Notes:
- The volume name must match exactly what appears in
modal volume list - If the volume doesn't exist, Modal will create it automatically on first use
- Use environment-specific names (e.g.,
comfydeploy-dev-*for dev,comfydeploy-prod-*for production) - Both
SHARED_MODEL_VOLUME_NAMEandPUBLIC_MODEL_VOLUME_NAMEcan point to the same volume for local development
When you download a model through the UI, it gets uploaded to the appropriate volume, and then your workflows can access it.
The ngrok Part (Important!)
Here's the thing: Modal functions run in the cloud and need to send status updates back to your local API. They can't reach http://localhost:3011 directly. That's where ngrok comes in.
ngrok creates a public tunnel to your local API. When you start it, you'll get a URL like https://abc123.ngrok-free.app. Update your .env:
CURRENT_API_URL="https://ngrok_generated_url.dev"
Then restart your API server. Now Modal functions can reach your API and send status updates properly.
Step 6: Create a User Record in the Database (CRITICAL - Must Do After First Login)
⚠️ CRITICAL STEP: After logging in with Clerk for the first time, you must create a user record in your local database. Without this, you'll get ForeignKeyViolationError errors when trying to use the application.
Why is this needed?
Clerk handles authentication and creates a user in their system, but your local PostgreSQL database doesn't know about this user yet. Most API endpoints require a user record to exist in the comfyui_deploy.users table due to foreign key constraints.
When to do this:
- After your first login via the frontend (http://localhost:3001)
- After restarting Docker (which wipes the database)
- Whenever you see errors like:
Key (user_id)=(user_xxx) is not present in table "users"
Get Your Clerk User ID
First, log in to the frontend at http://localhost:3001. Then get your Clerk user ID:
Option 1: From Browser DevTools
- Open DevTools (F12)
- Go to Console tab
- Type:
window.Clerk.user.id - Copy the user ID (starts with
user_)
Option 2: From API Logs
Check your API server logs - you'll see the user ID in authentication errors:
ERROR: Key (user_id)=(user_123456789) is not present in table "users"
Create the User Record
Replace your-clerk-user-id, your-username, and Your Name with your actual values:
cd apps/api
source .venv/bin/activate
python3 << 'EOF'
import asyncio
from sqlalchemy import text
from sqlalchemy.ext.asyncio import create_async_engine
DATABASE_URL = "postgresql+asyncpg://postgres:postgres@localhost:5480/verceldb"
engine = create_async_engine(DATABASE_URL)
async def main():
async with engine.begin() as conn:
await conn.execute(text("""
INSERT INTO comfyui_deploy.users (id, username, name, created_at, updated_at)
VALUES (:id, :username, :name, now(), now())
ON CONFLICT (id) DO NOTHING
"""), {
"id": "your-clerk-user-id", # e.g., "user_35WjS9hWy1iZFJ6WbeThk92qcgf"
"username": "your-username", # e.g., "umut"
"name": "Your Name" # e.g., "Umut"
})
await engine.dispose()
print("✅ User created successfully!")
asyncio.run(main())
EOF
Example with actual values:
python3 << 'EOF'
import asyncio
from sqlalchemy import text
from sqlalchemy.ext.asyncio import create_async_engine
DATABASE_URL = "postgresql+asyncpg://postgres:postgres@localhost:5480/verceldb"
engine = create_async_engine(DATABASE_URL)
async def main():
async with engine.begin() as conn:
await conn.execute(text("""
INSERT INTO comfyui_deploy.users (id, username, name, created_at, updated_at)
VALUES (:id, :username, :name, now(), now())
ON CONFLICT (id) DO NOTHING
"""), {
"id": "user_12345679",
"username": "umut",
"name": "Umut"
})
await engine.dispose()
print("✅ User created successfully!")
asyncio.run(main())
EOF
✓ Verify User Creation:
psql postgresql://postgres:postgres@localhost:5480/verceldb
# In psql:
SELECT id, username, name FROM comfyui_deploy.users;
# You should see your user record
# Exit psql with: \q
After creating the user, refresh your browser at http://localhost:3001. The foreign key errors should be gone and you should be able to use the application normally.
Step 6.5: Attach an Autumn Plan to Your User or Organization (CRITICAL)
⚠️ CRITICAL STEP: After creating your user record, you must attach a subscription plan in the Autumn dashboard. Without a plan, you'll hit feature limits and won't be able to create machines or run workflows.
Why is this needed?
ComfyDeploy uses Autumn for billing and feature limits. Even in local development, the API checks Autumn to determine:
- How many machines you can create (
machine_limit) - How many concurrent GPU jobs you can run (
gpu_concurrency_limit) - How many workflows you can have (
workflow_limit) - How many GPU credits you have available (
gpu-credit)
Without a plan attached, you'll be on the default "Free" tier with very limited features.
Access the Autumn Dashboard
- Go to https://dashboard.autumn.dev/ (or https://app.useautumn.com/)
- Log in with your Autumn account
- Navigate to Customers in the sidebar
Attach a Plan to Your Organization
For Organization-based Development (Recommended):
- In the Autumn dashboard, go to Customers
- Find or create a customer for your Clerk organization ID (e.g.,
org_123456789)- If it doesn't exist, click "Create Customer"
- Enter your Clerk organization ID as the customer ID
- Click on the customer to view details
- Click "Attach Plan" or "Add Subscription"
- Select "Business (Monthly)" plan
- Verify the plan includes:
- GPU Concurrency Limit: 10
- Machine Limit: 25
- Workflow Limit: 300
- Seats: 10
- GPU Credit: $10,000 per month (100,000 credits)
- Price: $998 per month
- Click "Create" or "Attach"
For User-based Development:
- In the Autumn dashboard, go to Customers
- Find or create a customer for your Clerk user ID (e.g.,
user_123456789) - Follow the same steps as above to attach the Business plan
Example Plan Configuration
Here's what the Business (Monthly) plan should look like in Autumn:
Business (Monthly)
├── GPU Concurrency Limit: 10
├── Machine Limit: 25
├── Workflow Limit: 300
├── Seats: 10
├── GPU Credit: $10,000 per month (100,000 credits)
└── Price: $998 per month
Feature Items Breakdown:
| Feature | Included Usage | Price |
|---|---|---|
gpu_concurrency_limit |
10 | Feature |
machine_limit |
25 | Feature |
workflow_limit |
300 | Feature |
seats |
10 | Feature |
gpu-credit |
$10,000 per GPU Credit (cents) per month | Feature |
| Total | $998 per month |
Verify the Plan is Active
- Refresh your browser at http://localhost:3001
- Navigate to Settings → Billing (or Usage)
- You should see:
- Current plan: Business
- Machine limit: 25
- GPU concurrency: 10
- Available credits: 100,000 credits ($10,000)
Check via API:
curl -H "Authorization: Bearer YOUR_CLERK_TOKEN" \
http://localhost:3011/api/platform/plan
You should see a response with:
{
"plan": "business",
"features": {
"machine_limit": 25,
"gpu_concurrency_limit": 10,
"workflow_limit": 300,
"gpu_credit": 100000
}
}
Common Issues
Error: "Max Machines Exceeded" despite attaching a plan
This usually means:
- The plan wasn't attached to the correct customer ID (check if you're using user ID vs organization ID)
- The Autumn cache needs to be refreshed - wait 30 seconds and try again
- The
autumn.config.tswasn't pushed - runnpx atmn pushagain
Error: "Insufficient GPU credits"
The Business plan includes $10,000 in GPU credits (100,000 credits). If you see this error:
- Check the Autumn dashboard to verify credits were added
- Make sure the
gpu-creditfeature is included in the plan - Try refreshing the page or logging out and back in
Plan not showing in the frontend
- Clear browser cache and hard refresh (Cmd+Shift+R or Ctrl+Shift+R)
- Check that
AUTUMN_SECRET_KEYis correct inapps/api/.env - Restart the API server to pick up the latest Autumn data
Step 7: Access the App
Open http://localhost:3001 in your browser. You should be able to log in with Clerk and start creating workflows.
✓ Verify Full Stack:
- Log in with your Clerk credentials
- Navigate to "Machines" → you should see an empty list
- Try creating a new workflow
- Check that the API is receiving requests (watch the API server logs)
Troubleshooting Guide
Database Issues
Error: relation "comfyui_deploy.machines" does not exist
This means migrations didn't run or failed. Fix it:
cd apps/api
bun run migrate-local
Check the output for any errors. If migrations are stuck, you may need to reset:
# WARNING: This deletes all data!
docker-compose down -v
docker-compose up -d
bun run migrate-local
Error: ForeignKeyViolationError: Key (user_id)=(...) is not present in table "users"
This is the most common error in local development. The user record doesn't exist in the database. This happens when:
- You restarted Docker (which wipes the database)
- You're using a new Clerk user ID that hasn't been added to the database
- You skipped Step 6 after first login
Fix: Run Step 6 to create the user record. This is a required step after every Docker restart or first login.
Error: 500 Internal Server Error on /api/volume/private-models
Usually caused by missing user record. Check:
# Get your Clerk user ID from the browser console:
# Open DevTools → Application → Cookies → find __clerk_db_jwt
# Decode it to see your user_id
# Then verify the user exists:
psql postgresql://postgres:postgres@localhost:5480/verceldb
SELECT * FROM comfyui_deploy.users WHERE id = 'your-user-id';
If the user doesn't exist, create it (Step 6).
Port and Process Issues
Error: Address already in use on port 3011
Something is already running on that port. Kill it:
lsof -ti:3011 | xargs kill -9
Or find what's using it:
lsof -i :3011
Error: Address already in use on port 3001 (frontend)
lsof -ti:3001 | xargs kill -9
Error: ngrok tunnel keeps disconnecting
ngrok free tier has connection limits. If you're testing heavily:
- Upgrade to ngrok pro
- Or restart ngrok:
ngrok http 3011
Modal and Workflow Issues
Runs stuck in "not-started" status
This means Modal functions can't send status updates back to your API. Check:
- Is ngrok running?
ps aux | grep ngrok
- Is the ngrok URL in your
.env?
grep CURRENT_API_URL apps/api/.env
- Did you restart the API server after updating
.env?
# Kill the old server and restart it
lsof -ti:3011 | xargs kill -9
PORT=3011 uv run src/api/server.py
- Test the ngrok tunnel:
curl https://your-ngrok-url.ngrok-free.app/api/health
Error: "Max Machines Exceeded" despite having a paid plan
This is an Autumn billing configuration issue. The most common cause is not attaching a plan to your user or organization in the Autumn dashboard.
Fix:
-
🔴 MOST COMMON: Did you attach a plan in the Autumn dashboard? (See Step 6.5)
- Go to https://dashboard.autumn.dev/
- Navigate to Customers
- Find your user ID or organization ID
- Click "Attach Plan" → Select "Business (Monthly)"
- Verify the plan includes
machine_limit: 25
Did you push your config to Autumn?
cd apps/api
npx atmn push
- Is the
machineLimitfeature defined in your product?
grep -A 5 "machineLimit" autumn.config.ts
-
Are you using the correct customer ID?
- Check if you're logged in as a user or organization
- The plan must be attached to the correct customer ID (user ID vs org ID)
- Check browser console:
window.Clerk.user.idorwindow.Clerk.organization.id
Wait 30 seconds for Autumn cache to refresh, then try again
Error: "GET /api/latest-hashes 404 Not Found" or "Illegal header value b'Bearer '"
This means the GITHUB_TOKEN is missing or empty in your .env file. The API needs this token to fetch ComfyUI version information from GitHub.
Fix:
- Create a GitHub classic token at https://github.com/settings/tokens
- Select
public_reposcope - Add to
apps/api/.env:
GITHUB_TOKEN="ghp_your_token_here"
- Restart the API server
Error: "Invalid Volume name: ''" or "Can't find Volume: Invalid Volume name: ''"
This means the Modal volume environment variables are missing or empty. Modal requires valid volume names to create persistent storage for models.
Symptoms:
ERROR:api.middleware.authMiddleware:Can't find Volume: Invalid Volume name: ''.
Names may contain only alphanumeric characters, dashes, periods, and underscores,
must be shorter than 64 characters, and cannot conflict with App ID strings.
Fix:
- Add volume names to
apps/api/.env:
SHARED_MODEL_VOLUME_NAME="comfydeploy-dev-public-models"
PUBLIC_MODEL_VOLUME_NAME="comfydeploy-dev-public-models"
- Restart the API server
- The volume will be automatically created in Modal on first use
Why this happens:
- The code looks for
PUBLIC_MODEL_VOLUME_NAMEorSHARED_MODEL_VOLUME_NAMEenvironment variables - If both are empty, it returns an empty string
"" - Modal rejects empty volume names with a validation error
- This affects both the
/api/volume/public-modelsendpoint and serverless deployments
Error: Model downloads fail or "Function not found" errors
This usually means you forgot to deploy the volume-operations Modal app, which is required for model management.
Symptoms:
- Model downloads fail silently or with errors
- "Function not found" errors when trying to download models
- Models don't appear in Modal volumes
Fix:
- Deploy the volume-operations app:
cd apps/api
modal deploy src/modal_apps/modal_downloader.py::modal_downloader_app
- Verify deployment:
modal app list
# Should show "volume-operations" in the list
- Check the Modal dashboard at https://modal.com/apps
Why this is required:
- The
volume-operationsapp contains Modal functions for downloading models - Without it, the API can't download models to Modal volumes
- This is a one-time deployment per Modal environment
- See Step 5.4 in the setup guide for details
Service Health Checks
Check if PostgreSQL is running:
psql postgresql://postgres:postgres@localhost:5480/verceldb -c "SELECT 1"
# Expected: 1
Check if Redis is running:
redis-cli -p 6379 ping
# Expected: PONG
Check if API server is responding:
curl http://localhost:3011/api/health
# Expected: 200 OK
Check if frontend is running:
curl http://localhost:3001
# Expected: HTML response
Check API server logs for errors:
# The API server logs to stdout, so check your terminal
# Look for ERROR or EXCEPTION messages
Check Docker container logs:
docker-compose logs postgres # PostgreSQL logs
docker-compose logs redis # Redis logs
docker-compose logs # All services
React Query Caching Issues
Settings page shows "disabled for free tier" despite having a paid plan
This is a React Query cache issue. The frontend cached stale plan data. Fix:
- Clear browser cache: DevTools → Application → Clear Storage
- Or hard refresh: Ctrl+Shift+R (Windows/Linux) or Cmd+Shift+R (Mac)
- Or log out and log back in
If this keeps happening, check that your .env has the correct AUTUMN_SECRET_KEY.
That's It!
You're now ready to develop. The key insights:
🔴 CRITICAL: Create user record after first login (Step 6) - Without this, you'll get foreign key errors on every API call. This must be done after every Docker restart.
🔴 CRITICAL: Attach Autumn plan (Step 6.5) - Without a subscription plan attached in the Autumn dashboard, you'll hit feature limits and won't be able to create machines or run workflows. Attach the "Business (Monthly)" plan to your user or organization.
🔴 CRITICAL: ngrok must be running (Step 5.5) - Modal functions need to communicate back to your local API. Without ngrok, workflows will get stuck in "not-started" status.
-
Fresh Docker restarts wipe data - You'll need to:
- Re-run migrations (
bun run migrate-local) - Recreate user records (Step 6)
- Verify Autumn plan is still attached (Step 6.5)
- Re-run migrations (
Autumn config must be pushed - Changes to
autumn.config.tsdon't take effect until you runnpx atmn pushCheck logs first - Most issues are visible in the API server logs or Docker logs
Quick Start Checklist
After cloning the repo, here's the minimal path to get running:
Prerequisites:
- [ ] Install all required software (Node.js, Bun, Python 3.12, uv, Docker, AWS CLI, ngrok, psql)
- [ ] Create accounts and get API keys (Clerk, Autumn, AWS S3, Modal, ngrok, GitHub)
Setup:
- [ ] Initialize git submodules:
git submodule init && git submodule update - [ ] Install dependencies (API:
bun install+uv sync, Frontend:bun install) - [ ] Configure
.envfiles with all API keys (Clerk, Autumn, AWS S3, Modal, ngrok, GitHub, Modal Volumes) - [ ] Push Autumn config:
npx atmn push - [ ] Start Docker:
docker compose up -d - [ ] Run migrations:
bun run migrate-local - [ ] 🔴 Install and configure ngrok:
brew install ngrok/ngrok/ngrok+ngrok config add-authtoken YOUR_TOKEN - [ ] 🔴 Start ngrok:
ngrok http 3011(keep running) - [ ] 🔴 Update
.envwith ngrok URL and Modal volume names - [ ] Start API server:
bun run dev - [ ] Start frontend:
bun run dev - [ ] 🔴 Log in via frontend, then create user record in database (Step 6)
- [ ] 🔴 Attach Business plan to your user/org in Autumn dashboard (Step 6.5)
- [ ] Verify everything works by creating a test workflow
Happy coding! 🚀

Top comments (0)