<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Umut Tan</title>
    <description>The latest articles on Forem by Umut Tan (@ogkai).</description>
    <link>https://forem.com/ogkai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2204420%2F901c1b47-5285-46b3-bf5d-146393218c3a.jpg</url>
      <title>Forem: Umut Tan</title>
      <link>https://forem.com/ogkai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ogkai"/>
    <language>en</language>
    <item>
      <title>ComfyDeploy Local Development Setup Guide</title>
      <dc:creator>Umut Tan</dc:creator>
      <pubDate>Sat, 15 Nov 2025 21:36:06 +0000</pubDate>
      <link>https://forem.com/ogkai/comfydeploy-local-development-setup-guide-h6m</link>
      <guid>https://forem.com/ogkai/comfydeploy-local-development-setup-guide-h6m</guid>
      <description>&lt;p&gt;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... &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcjoome7jn1lhcwlg507k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcjoome7jn1lhcwlg507k.png" alt=" " width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What You'll See
&lt;/h2&gt;

&lt;p&gt;By completing this guide, you'll gain hands-on experience with:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full-Stack Development:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚀 &lt;strong&gt;Modern Web Stack&lt;/strong&gt;: React/Vite frontend + Python FastAPI backend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Management&lt;/strong&gt;: PostgreSQL with Drizzle ORM, schema design, and migrations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt;: Clerk integration with JWT validation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Billing &amp;amp; Subscriptions&lt;/strong&gt;: Autumn API for SaaS monetization and feature limits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Containerization&lt;/strong&gt;: Docker Compose for local development infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cloud &amp;amp; Serverless Architecture:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Serverless Computing&lt;/strong&gt;: Deploy GPU-accelerated Python functions with Modal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistent Storage&lt;/strong&gt;: Modal volumes for model storage and file management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Storage&lt;/strong&gt;: AWS S3 integration for file uploads and assets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhooks &amp;amp; Tunneling&lt;/strong&gt;: ngrok for local development with cloud callbacks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;DevOps &amp;amp; Infrastructure:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monorepo Management&lt;/strong&gt;: Turborepo for managing multiple applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment Configuration&lt;/strong&gt;: Managing secrets and API keys across services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD Concepts&lt;/strong&gt;: Understanding deployment pipelines and environments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging&lt;/strong&gt;: Reading logs, troubleshooting errors, and fixing common issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI/ML Deployment:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ComfyUI Integration&lt;/strong&gt;: Running AI image generation workflows in production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GPU Management&lt;/strong&gt;: Serverless GPU allocation and concurrency limits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usage Tracking&lt;/strong&gt;: Monitoring GPU credits and resource consumption&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Free Credits &amp;amp; Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💰 &lt;strong&gt;$5 USD in Modal Credits&lt;/strong&gt;: Sign up at &lt;a href="https://modal.com/" rel="noopener noreferrer"&gt;https://modal.com/&lt;/a&gt; to get free credits for GPU compute&lt;/li&gt;
&lt;li&gt;🎯 &lt;strong&gt;Run Real Workflows&lt;/strong&gt;: Deploy and execute ComfyUI workflows on production-grade infrastructure&lt;/li&gt;
&lt;li&gt;🧪 &lt;strong&gt;Experiment with AI Models&lt;/strong&gt;: Test different models, custom nodes, and workflow configurations&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Production-Ready Codebase&lt;/strong&gt;: Learn from a real SaaS application architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Skills You'll Develop:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up complex development environments with multiple services&lt;/li&gt;
&lt;li&gt;Integrating third-party APIs (Clerk, Autumn, AWS, Modal, GitHub)&lt;/li&gt;
&lt;li&gt;Managing database schemas and foreign key relationships&lt;/li&gt;
&lt;li&gt;Debugging full-stack applications across frontend, backend, and cloud services&lt;/li&gt;
&lt;li&gt;Understanding SaaS billing models and feature gating&lt;/li&gt;
&lt;li&gt;Working with serverless architectures and GPU compute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end of this guide, you'll have a &lt;strong&gt;fully functional local development environment&lt;/strong&gt; for ComfyDeploy, ready to build features, fix bugs, and deploy AI workflows! 🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Required Software &amp;amp; Tools
&lt;/h3&gt;

&lt;p&gt;Install these dependencies before starting:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Core Development Tools:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js&lt;/strong&gt; (v18 or higher) - JavaScript runtime

&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;brew install node&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Or download from &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;https://nodejs.org/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Bun&lt;/strong&gt; (v1.0+) - Fast JavaScript package manager and runtime

&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;brew install oven-sh/bun/bun&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Or: &lt;code&gt;curl -fsSL https://bun.sh/install | bash&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Python&lt;/strong&gt; (3.12.0 exactly) - Required for API server

&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;brew install python@3.12&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Or use pyenv: &lt;code&gt;pyenv install 3.12.0&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;uv&lt;/strong&gt; - Fast Python package manager

&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;brew install uv&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Or: &lt;code&gt;curl -LsSf https://astral.sh/uv/install.sh | sh&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker Desktop&lt;/strong&gt; - For PostgreSQL and Redis

&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;brew install --cask docker&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Or download from &lt;a href="https://www.docker.com/products/docker-desktop/" rel="noopener noreferrer"&gt;https://www.docker.com/products/docker-desktop/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;PostgreSQL Client&lt;/strong&gt; (psql) - For database management

&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;brew install postgresql@16&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Or use the client included with Docker&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cloud &amp;amp; Networking:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS CLI&lt;/strong&gt; - For S3 bucket management

&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;brew install awscli&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Or: &lt;code&gt;curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg" &amp;amp;&amp;amp; sudo installer -pkg AWSCLIV2.pkg -target /&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Modal CLI&lt;/strong&gt; - For serverless deployment

&lt;ul&gt;
&lt;li&gt;Installed via Python: &lt;code&gt;pip install modal&lt;/code&gt; (or included in uv sync)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;ngrok&lt;/strong&gt; - For local tunneling

&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;brew install ngrok/ngrok/ngrok&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Or download from &lt;a href="https://ngrok.com/download" rel="noopener noreferrer"&gt;https://ngrok.com/download&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Optional but Recommended:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Git&lt;/strong&gt; (latest version) - Version control

&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;brew install git&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;jq&lt;/strong&gt; - JSON processor for debugging API responses

&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;brew install jq&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Required Service Accounts &amp;amp; API Keys
&lt;/h3&gt;

&lt;p&gt;You'll need accounts and API keys for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clerk&lt;/strong&gt; (authentication) - get your publishable key, secret key, and JWT public key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autumn&lt;/strong&gt; (billing/subscriptions) - get your secret key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS S3&lt;/strong&gt; (file storage) - create a bucket and get your credentials&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modal&lt;/strong&gt; (serverless compute) - create an account and get your token&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ngrok&lt;/strong&gt; (for local development) - get your authtoken&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt; (API access) - create a personal access token for fetching ComfyUI version info&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Clone and Install Dependencies
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/comfy-deploy/comfydeploy.git
&lt;span class="nb"&gt;cd &lt;/span&gt;comfydeploy

&lt;span class="c"&gt;# IMPORTANT: Initialize git submodules to pull in the actual code&lt;/span&gt;
&lt;span class="c"&gt;# The apps/api and apps/app directories are git submodules&lt;/span&gt;
git submodule init
git submodule update

&lt;span class="c"&gt;# Install API dependencies (Node.js packages)&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;apps/api
bun &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Install Python dependencies using uv (faster than pip)&lt;/span&gt;
&lt;span class="c"&gt;# This creates a virtual environment at apps/api/.venv&lt;/span&gt;
uv &lt;span class="nb"&gt;sync&lt;/span&gt;

&lt;span class="c"&gt;# Install frontend dependencies&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ../app
bun &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The repository uses git submodules for &lt;code&gt;apps/api&lt;/code&gt; and &lt;code&gt;apps/app&lt;/code&gt;. You must run &lt;code&gt;git submodule init &amp;amp;&amp;amp; git submodule update&lt;/code&gt; to pull in the actual application code. Without this step, the directories will be empty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python Dependencies:&lt;/strong&gt; The API uses &lt;code&gt;pyproject.toml&lt;/code&gt; instead of &lt;code&gt;requirements.txt&lt;/code&gt;. Use &lt;code&gt;uv sync&lt;/code&gt; to install Python dependencies - it will automatically create a virtual environment at &lt;code&gt;apps/api/.venv&lt;/code&gt; with Python 3.12.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Configure Environment Variables
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Required Service Accounts &amp;amp; API Keys
&lt;/h3&gt;

&lt;p&gt;Before configuring, sign up for these services and get your API keys:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Clerk&lt;/strong&gt; (Authentication) - &lt;a href="https://clerk.com/" rel="noopener noreferrer"&gt;https://clerk.com/&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dashboard: &lt;a href="https://dashboard.clerk.com/" rel="noopener noreferrer"&gt;https://dashboard.clerk.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Get: &lt;code&gt;CLERK_PUBLISHABLE_KEY&lt;/code&gt;, &lt;code&gt;CLERK_SECRET_KEY&lt;/code&gt;, &lt;code&gt;CLERK_PUBLIC_JWT_KEY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Go to: Dashboard → Your App → API Keys → Find "JWKS Public Key" section&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Autumn&lt;/strong&gt; (Billing) - &lt;a href="https://useautumn.com/" rel="noopener noreferrer"&gt;https://useautumn.com/&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get: &lt;code&gt;AUTUMN_SECRET_KEY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Authenticate CLI: &lt;code&gt;npx atmn auth&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Push config: &lt;code&gt;npx atmn push&lt;/code&gt; (after configuring &lt;code&gt;autumn.config.ts&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AWS S3&lt;/strong&gt; (File Storage) - &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;https://aws.amazon.com/s3/&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create bucket: &lt;code&gt;aws s3 mb s3://comfydeploy-dev-storage --region us-east-1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Get credentials from AWS IAM or use existing AWS CLI credentials&lt;/li&gt;
&lt;li&gt;Get: Access Key ID, Secret Access Key&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Example S3 Bucket Configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Access Control List (ACL):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public READ access - Grants READ permission to "AllUsers" for public file access&lt;/li&gt;
&lt;li&gt;Owner has FULL_CONTROL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. CORS Configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allows all origins (&lt;code&gt;*&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Allows all HTTP methods (GET, PUT, POST, DELETE, HEAD)&lt;/li&gt;
&lt;li&gt;Allows all headers (&lt;code&gt;*&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Exposes ETag header&lt;/li&gt;
&lt;li&gt;Max age: 3000 seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Bucket Policy:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No specific bucket policy required (can be left empty)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Public Access Block:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All public access blocks disabled for public file access&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BlockPublicAcls&lt;/code&gt;: false&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IgnorePublicAcls&lt;/code&gt;: false&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BlockPublicPolicy&lt;/code&gt;: false&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RestrictPublicBuckets&lt;/code&gt;: false&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Bucket Structure:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;assets/upload/&lt;/code&gt; - For uploaded assets&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;outputs/runs/&lt;/code&gt; - For workflow output files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Security Note:&lt;/strong&gt; This configuration makes the bucket &lt;strong&gt;publicly readable&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To configure CORS via AWS CLI:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Create a cors.json file&lt;/span&gt;
   &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; cors.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
   {
     "CORSRules": [
       {
         "AllowedHeaders": ["*"],
         "AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
         "AllowedOrigins": ["*"],
         "ExposeHeaders": ["ETag"],
         "MaxAgeSeconds": 3000
       }
     ]
   }
&lt;/span&gt;&lt;span class="no"&gt;   EOF

&lt;/span&gt;   &lt;span class="c"&gt;# Apply CORS configuration&lt;/span&gt;
   aws s3api put-bucket-cors &lt;span class="nt"&gt;--bucket&lt;/span&gt; YOUR-BUCKET-NAME &lt;span class="nt"&gt;--cors-configuration&lt;/span&gt; file://cors.json &lt;span class="nt"&gt;--region&lt;/span&gt; YOUR-REGION

   &lt;span class="c"&gt;# Set public read ACL&lt;/span&gt;
   aws s3api put-bucket-acl &lt;span class="nt"&gt;--bucket&lt;/span&gt; YOUR-BUCKET-NAME &lt;span class="nt"&gt;--acl&lt;/span&gt; public-read &lt;span class="nt"&gt;--region&lt;/span&gt; YOUR-REGION

   &lt;span class="c"&gt;# Disable public access blocks&lt;/span&gt;
   aws s3api delete-public-access-block &lt;span class="nt"&gt;--bucket&lt;/span&gt; YOUR-BUCKET-NAME &lt;span class="nt"&gt;--region&lt;/span&gt; YOUR-REGION
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;To verify your bucket configuration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Check CORS&lt;/span&gt;
   aws s3api get-bucket-cors &lt;span class="nt"&gt;--bucket&lt;/span&gt; YOUR-BUCKET-NAME &lt;span class="nt"&gt;--region&lt;/span&gt; YOUR-REGION

   &lt;span class="c"&gt;# Check ACL&lt;/span&gt;
   aws s3api get-bucket-acl &lt;span class="nt"&gt;--bucket&lt;/span&gt; YOUR-BUCKET-NAME &lt;span class="nt"&gt;--region&lt;/span&gt; YOUR-REGION

   &lt;span class="c"&gt;# Check public access block&lt;/span&gt;
   aws s3api get-public-access-block &lt;span class="nt"&gt;--bucket&lt;/span&gt; YOUR-BUCKET-NAME &lt;span class="nt"&gt;--region&lt;/span&gt; YOUR-REGION
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Modal&lt;/strong&gt; (Serverless Compute) - &lt;a href="https://modal.com/" rel="noopener noreferrer"&gt;https://modal.com/&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dashboard: &lt;a href="https://modal.com/home" rel="noopener noreferrer"&gt;https://modal.com/home&lt;/a&gt; → Settings → API Tokens&lt;/li&gt;
&lt;li&gt;Get: &lt;code&gt;MODAL_TOKEN_ID&lt;/code&gt;, &lt;code&gt;MODAL_TOKEN_SECRET&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ngrok&lt;/strong&gt; (Local Tunneling) - &lt;a href="https://ngrok.com/" rel="noopener noreferrer"&gt;https://ngrok.com/&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dashboard: &lt;a href="https://dashboard.ngrok.com/" rel="noopener noreferrer"&gt;https://dashboard.ngrok.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Get: &lt;code&gt;NGROK_AUTHTOKEN&lt;/code&gt; from "Your Authtoken" section&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt; (API Access for ComfyUI Version Info) - &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;https://github.com/&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Settings → Developer settings → Personal access tokens → Tokens (classic)&lt;/li&gt;
&lt;li&gt;Generate new token (classic) with &lt;code&gt;public_repo&lt;/code&gt; scope&lt;/li&gt;
&lt;li&gt;Get: &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; (starts with &lt;code&gt;ghp_&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Configure Environment Files
&lt;/h3&gt;

&lt;p&gt;Copy the example env files and fill in your credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp &lt;/span&gt;apps/api/.env.example apps/api/.env
&lt;span class="nb"&gt;cp &lt;/span&gt;apps/app/.env.example apps/app/.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Generate Encryption Key
&lt;/h3&gt;

&lt;p&gt;Generate a secret encryption key for the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;apps/api
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode('utf-8'))"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the output and add it to &lt;code&gt;apps/api/.env&lt;/code&gt; as &lt;code&gt;SECRET_ENCRYPTION_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get Clerk JWT Public Key
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to Clerk Dashboard → Your App → API Keys&lt;/li&gt;
&lt;li&gt;Scroll down to the "JWKS Public Key" section&lt;/li&gt;
&lt;li&gt;Copy the entire key (starts with &lt;code&gt;-----BEGIN PUBLIC KEY-----&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Add it to &lt;code&gt;apps/api/.env&lt;/code&gt; as &lt;code&gt;CLERK_PUBLIC_JWT_KEY&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example Configuration
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;apps/api/.env&lt;/strong&gt; (key variables):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;⚠️ CRITICAL:&lt;/strong&gt; &lt;code&gt;CURRENT_API_URL&lt;/code&gt; must be set to your ngrok URL (from Step 5.5), NOT &lt;code&gt;http://localhost:3011&lt;/code&gt;. Modal functions run in the cloud and cannot reach localhost. See Step 5.5 for how to get your ngrok URL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; To verify your Modal volume name, run &lt;code&gt;modal volume list&lt;/code&gt; and use the exact name that appears in the output. See Step 5.6 for details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;apps/app/.env&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_..."
NEXT_PUBLIC_CD_API_URL="http://localhost:3011"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2.5: Set Up Autumn Billing Integration
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is Autumn?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding autumn.config.ts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The file &lt;code&gt;apps/api/autumn.config.ts&lt;/code&gt; defines your entire billing structure. Here's what you need to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Features&lt;/strong&gt;: Define what users can do (e.g., &lt;code&gt;machineLimit&lt;/code&gt;, &lt;code&gt;gpuConcurrencyLimit&lt;/code&gt;, &lt;code&gt;workflowLimit&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Products&lt;/strong&gt;: Combine features into subscription tiers (e.g., &lt;code&gt;free&lt;/code&gt;, &lt;code&gt;business&lt;/code&gt;, &lt;code&gt;enterprise&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pricing&lt;/strong&gt;: Set monthly/yearly prices and included usage amounts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's a simplified version of what's in the config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Define features&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;machineLimit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;machine_limit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Machine Limit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;continuous_use&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gpuConcurrencyLimit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpu_concurrency_limit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GPU Concurrency Limit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;continuous_use&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Define products (subscription tiers)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;free&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;free&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Free&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;is_default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;featureItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;feature_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;machineLimit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;included_usage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Free tier gets 3 machines&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nf"&gt;featureItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;feature_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gpuConcurrencyLimit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;included_usage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Can run 1 GPU job at a time&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;business&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;business&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Business&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;pricedFeatureItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;feature_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gpuCredit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;included_usage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;usage_model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pay_per_use&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nf"&gt;featureItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;feature_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;machineLimit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;included_usage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Business tier gets 100 machines&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nf"&gt;featureItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;feature_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gpuConcurrencyLimit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;included_usage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Can run 10 GPU jobs at a time&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pushing Configuration to Autumn&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After updating &lt;code&gt;autumn.config.ts&lt;/code&gt;, push your changes to Autumn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;apps/api
npx atmn push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command validates your config and syncs it with the Autumn API. You'll see output confirming which products and features were created/updated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verifying Your Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check that plans are active in the Autumn dashboard:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://dashboard.autumn.dev" rel="noopener noreferrer"&gt;https://dashboard.autumn.dev&lt;/a&gt; (or your Autumn instance)&lt;/li&gt;
&lt;li&gt;Navigate to Products → verify your products are listed&lt;/li&gt;
&lt;li&gt;Check that features have the correct limits&lt;/li&gt;
&lt;li&gt;Test by creating a subscription in the dashboard&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Common Gotcha: Missing Feature Definitions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you see "Max Machines Exceeded" errors even though users have paid plans, it's usually because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;machineLimit&lt;/code&gt; feature isn't defined in the product&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;included_usage&lt;/code&gt; is set too low&lt;/li&gt;
&lt;li&gt;The config wasn't pushed to Autumn after changes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Add the missing feature to your product and run &lt;code&gt;npx atmn push&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Push Configuration to Autumn:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After setting up your &lt;code&gt;.env&lt;/code&gt; file with &lt;code&gt;AUTUMN_SECRET_KEY&lt;/code&gt;, push the billing configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;apps/api
npx atmn push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will push all features and products defined in &lt;code&gt;autumn.config.ts&lt;/code&gt; to your Autumn sandbox environment. You should see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ 20 features pushed (GPU types, credits, limits, etc.)&lt;/li&gt;
&lt;li&gt;✅ 10 products pushed (Free, Creator, Deployment, Business plans + add-ons)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;View your products at: &lt;a href="http://app.useautumn.com/sandbox/products" rel="noopener noreferrer"&gt;http://app.useautumn.com/sandbox/products&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🔑 GitHub Token Setup
&lt;/h3&gt;

&lt;p&gt;The API uses GitHub's API to fetch the latest ComfyUI and ComfyUI-Deploy version information. Without a GitHub token, the &lt;code&gt;/api/latest-hashes&lt;/code&gt; endpoint will fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a GitHub Classic Token:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://github.com/settings/tokens" rel="noopener noreferrer"&gt;https://github.com/settings/tokens&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click "Generate new token" → "Generate new token (classic)"&lt;/li&gt;
&lt;li&gt;Give it a descriptive name (e.g., "ComfyDeploy Local Dev")&lt;/li&gt;
&lt;li&gt;Select scopes:

&lt;ul&gt;
&lt;li&gt;✅ &lt;code&gt;public_repo&lt;/code&gt; (Access public repositories)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click "Generate token"&lt;/li&gt;
&lt;li&gt;Copy the token (starts with &lt;code&gt;ghp_&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Add to &lt;code&gt;apps/api/.env&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ghp_your_token_here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why is this needed?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The API fetches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Latest ComfyUI commit hash from &lt;code&gt;comfyanonymous/ComfyUI&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Latest ComfyUI release information&lt;/li&gt;
&lt;li&gt;Latest ComfyUI-Deploy commit hash from &lt;code&gt;bennykok/comfyui-deploy&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without the token, you'll see errors like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📦 Modal Volume Configuration
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What are Modal Volumes?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Modal volumes are persistent storage for your serverless ComfyUI deployments. They store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public models (shared across all users)&lt;/li&gt;
&lt;li&gt;Private models (user-specific or organization-specific)&lt;/li&gt;
&lt;li&gt;Workflow files and custom nodes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Configure Volume Names:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For local development, you need to set the public model volume name in &lt;code&gt;apps/api/.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SHARED_MODEL_VOLUME_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"comfydeploy-dev-public-models"&lt;/span&gt;
&lt;span class="nv"&gt;PUBLIC_MODEL_VOLUME_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"comfydeploy-dev-public-models"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why is this needed?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without these environment variables, Modal will fail to create volumes with the error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Volume Naming Convention:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local Development&lt;/strong&gt;: Use a simple name like &lt;code&gt;comfydeploy-dev-public-models&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production&lt;/strong&gt;: Use organization-specific names like &lt;code&gt;models_org_&amp;lt;org_id&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private Volumes&lt;/strong&gt;: Automatically named as &lt;code&gt;models_&amp;lt;user_id&amp;gt;&lt;/code&gt; or &lt;code&gt;models_org_&amp;lt;org_id&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How Volumes Work:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When you deploy a serverless machine, Modal creates volumes with &lt;code&gt;create_if_missing=True&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The public volume is shared across all deployments for common models&lt;/li&gt;
&lt;li&gt;Private volumes are created per-user or per-organization for custom models&lt;/li&gt;
&lt;li&gt;Volumes persist between deployments and are mounted at runtime&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Verifying Volume Creation:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After starting the API server, check the Modal dashboard:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://modal.com/home" rel="noopener noreferrer"&gt;https://modal.com/home&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to "Volumes" in the sidebar&lt;/li&gt;
&lt;li&gt;You should see &lt;code&gt;comfydeploy-dev-public-models&lt;/code&gt; listed&lt;/li&gt;
&lt;li&gt;The volume will be created automatically on first use&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 3: Start Docker Services
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;apps/api
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This starts PostgreSQL, Redis, pg_proxy, and serverless-redis-http containers.&lt;/p&gt;

&lt;p&gt;✓ &lt;strong&gt;Verify&lt;/strong&gt;: Check that all services are running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose ps

&lt;span class="c"&gt;# Expected output should show:&lt;/span&gt;
&lt;span class="c"&gt;# - api-postgres-1 (port 5480)&lt;/span&gt;
&lt;span class="c"&gt;# - api-redis-1 (port 6379)&lt;/span&gt;
&lt;span class="c"&gt;# - api-pg_proxy-1 (port 5481)&lt;/span&gt;
&lt;span class="c"&gt;# - api-serverless-redis-http-1 (port 8079)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a service failed to start, check logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose logs postgres  &lt;span class="c"&gt;# or redis, etc.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Run Database Migrations
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;apps/api
bun run migrate-local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;⚠️ Troubleshooting Connection Timeout:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If migrations fail with &lt;code&gt;CONNECT_TIMEOUT localhost:5480&lt;/code&gt;, VS Code's port forwarding may be conflicting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check what's listening on port 5480&lt;/span&gt;
lsof &lt;span class="nt"&gt;-i&lt;/span&gt; :5480

&lt;span class="c"&gt;# If you see "Code Helper" process, kill it:&lt;/span&gt;
&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-9&lt;/span&gt; &amp;lt;PID&amp;gt;

&lt;span class="c"&gt;# Then retry migrations&lt;/span&gt;
bun run migrate-local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✓ &lt;strong&gt;Verify&lt;/strong&gt;: Check that migrations completed without errors. You should see output like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Database is live
Migrating... DB 1
Done!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4.5: Understanding the Database Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Schema Structure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All ComfyDeploy tables live in the &lt;code&gt;comfyui_deploy&lt;/code&gt; schema (not the default &lt;code&gt;public&lt;/code&gt; schema). This keeps your data organized and separate from other PostgreSQL objects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Tables and Relationships&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's the core data model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Critical Foreign Key Relationships&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;machines.user_id&lt;/code&gt; → &lt;code&gt;users.id&lt;/code&gt; (cascade delete)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user_volume.user_id&lt;/code&gt; → &lt;code&gt;users.id&lt;/code&gt; (no cascade)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;workflow_runs.machine_id&lt;/code&gt; → &lt;code&gt;machines.id&lt;/code&gt; (set null on delete)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;models.user_volume_id&lt;/code&gt; → &lt;code&gt;user_volume.id&lt;/code&gt; (cascade delete)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Verify Your Schema&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check that tables exist and have the right structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Connect to the database&lt;/span&gt;
psql postgresql://postgres:postgres@localhost:5480/verceldb

&lt;span class="c"&gt;# List all tables in the comfyui_deploy schema&lt;/span&gt;
&lt;span class="se"&gt;\d&lt;/span&gt;t comfyui_deploy.&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Check the users table structure&lt;/span&gt;
&lt;span class="se"&gt;\d&lt;/span&gt; comfyui_deploy.users

&lt;span class="c"&gt;# Count rows in key tables&lt;/span&gt;
SELECT COUNT&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; FROM comfyui_deploy.users&lt;span class="p"&gt;;&lt;/span&gt;
SELECT COUNT&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; FROM comfyui_deploy.machines&lt;span class="p"&gt;;&lt;/span&gt;
SELECT COUNT&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; FROM comfyui_deploy.workflows&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why Fresh Docker Restarts Wipe Data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you run &lt;code&gt;docker-compose down&lt;/code&gt;, the PostgreSQL container is removed along with its data volume. This is why you need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Re-run migrations (&lt;code&gt;bun run migrate-local&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Recreate user records (see Step 6)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In production, you'd use persistent volumes to prevent data loss.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Start the Development Servers
&lt;/h2&gt;

&lt;p&gt;Start both servers (they will run in the background):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start API server (runs on port 3011)&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;apps/api
bun run dev

&lt;span class="c"&gt;# Start Frontend server (runs on port 3001)&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;apps/app
bun run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✓ &lt;strong&gt;Verify API Server&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3011/
&lt;span class="c"&gt;# Expected: {"detail":"Not Found"} (this is normal - root path returns 404)&lt;/span&gt;

&lt;span class="c"&gt;# Check if server is responding&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; http://localhost:3011/
&lt;span class="c"&gt;# Expected: HTTP/1.1 404 Not Found (server is running)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✓ &lt;strong&gt;Verify Frontend&lt;/strong&gt;: Open &lt;a href="http://localhost:3001" rel="noopener noreferrer"&gt;http://localhost:3001&lt;/a&gt; in your browser&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You should see the Comfy Deploy login page&lt;/li&gt;
&lt;li&gt;Try logging in with your Clerk credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The API server may show some &lt;code&gt;SyntaxWarning&lt;/code&gt; messages about invalid escape sequences - these are harmless and don't affect functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5.5: Set Up ngrok Tunnel (CRITICAL - Required for Local Development)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;⚠️ CRITICAL STEP:&lt;/strong&gt; ngrok is &lt;strong&gt;required&lt;/strong&gt; 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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install ngrok
&lt;/h3&gt;

&lt;p&gt;If you haven't installed ngrok yet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;ngrok

&lt;span class="c"&gt;# Or download from https://ngrok.com/download&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure ngrok Authentication
&lt;/h3&gt;

&lt;p&gt;Get your authtoken from the ngrok dashboard:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dashboard: &lt;a href="https://dashboard.ngrok.com/get-started/your-authtoken" rel="noopener noreferrer"&gt;https://dashboard.ngrok.com/get-started/your-authtoken&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Copy your authtoken&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then configure ngrok:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok config add-authtoken YOUR_AUTHTOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok config add-authtoken YOUR_AUTHTOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Start ngrok Tunnel
&lt;/h3&gt;

&lt;p&gt;Launch ngrok to create a public tunnel to your local API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http 3011
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see output like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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 -&amp;gt; http://localhost:3011
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Copy the &lt;code&gt;Forwarding&lt;/code&gt; URL&lt;/strong&gt; (the https one, e.g., &lt;code&gt;https://shelia-nondedicative-kaidence.ngrok-free.dev&lt;/code&gt;)&lt;/p&gt;

&lt;h3&gt;
  
  
  Update Environment Variables
&lt;/h3&gt;

&lt;p&gt;Add the ngrok URL to your &lt;code&gt;apps/api/.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;NGROK_DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://your-ngrok-url.ngrok-free.dev"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;NGROK_DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://ngrok_generated_url.dev"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Keep ngrok running in a separate terminal window. If ngrok stops, Modal functions won't be able to communicate with your API.&lt;/p&gt;

&lt;p&gt;✓ &lt;strong&gt;Verify ngrok is working:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test that your ngrok URL reaches your local API&lt;/span&gt;
curl https://your-ngrok-url.ngrok-free.dev/
&lt;span class="c"&gt;# Expected: {"detail":"Not Found"} (this is normal - root path returns 404)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5.6: Set Up Modal for Serverless Compute
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is Modal?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating a Modal Environment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Modal uses "environments" to organize deployments (dev, staging, production). You've already configured this in your &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MODAL_ENVIRONMENT="dev"
MODAL_TOKEN_ID="your-modal-token-id"
MODAL_TOKEN_SECRET="your-modal-token-secret"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;⚠️ CRITICAL: Deploying the volume-operations App&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This step is REQUIRED!&lt;/strong&gt; Without deploying the &lt;code&gt;volume-operations&lt;/code&gt; app, model downloads will fail and workflows won't be able to access models from Modal volumes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;volume-operations&lt;/code&gt; Modal app handles downloading models to Modal volumes. Deploy it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;apps/api
modal deploy src/modal_apps/modal_downloader.py::modal_downloader_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a Modal app called "volume-operations" in your Modal workspace&lt;/li&gt;
&lt;li&gt;Enables downloading models from HuggingFace, CivitAI, and arbitrary URLs&lt;/li&gt;
&lt;li&gt;Uploads models to Modal volumes for use in workflows&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Required for model management functionality to work&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✓ &lt;strong&gt;Verify Modal Deployment&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all Modal apps in your workspace&lt;/span&gt;
modal app list

&lt;span class="c"&gt;# Expected output should include "volume-operations"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the Modal dashboard at &lt;a href="https://modal.com/apps" rel="noopener noreferrer"&gt;https://modal.com/apps&lt;/a&gt; to see your deployed apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common Issues:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you see "Function not found" errors when downloading models, you likely forgot this step&lt;/li&gt;
&lt;li&gt;The app must be deployed to the same Modal environment specified in &lt;code&gt;MODAL_ENVIRONMENT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Re-run the deploy command if you change Modal environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Configuring Modal Volumes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Modal volumes are persistent storage that Modal functions can access. ComfyDeploy creates volumes for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;comfydeploy-dev-public-models&lt;/code&gt;: Shared models available to all workflows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;models_org_&amp;lt;org_id&amp;gt;&lt;/code&gt;: Organization-specific models&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;models_&amp;lt;user_id&amp;gt;&lt;/code&gt;: User-specific models&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Check your existing Modal volumes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;modal volume list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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 │
└─────────────────────────────────────────┴──────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Update your &lt;code&gt;.env&lt;/code&gt; with the correct volume name:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Look for the volume that matches your environment (e.g., &lt;code&gt;comfydeploy-dev-public-models&lt;/code&gt; for dev environment), then update &lt;code&gt;apps/api/.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Modal Volume Configuration&lt;/span&gt;
&lt;span class="nv"&gt;SHARED_MODEL_VOLUME_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;comfydeploy-dev-public-models
&lt;span class="nv"&gt;PUBLIC_MODEL_VOLUME_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;comfydeploy-dev-public-models
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important Notes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The volume name must match exactly what appears in &lt;code&gt;modal volume list&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If the volume doesn't exist, Modal will create it automatically on first use&lt;/li&gt;
&lt;li&gt;Use environment-specific names (e.g., &lt;code&gt;comfydeploy-dev-*&lt;/code&gt; for dev, &lt;code&gt;comfydeploy-prod-*&lt;/code&gt; for production)&lt;/li&gt;
&lt;li&gt;Both &lt;code&gt;SHARED_MODEL_VOLUME_NAME&lt;/code&gt; and &lt;code&gt;PUBLIC_MODEL_VOLUME_NAME&lt;/code&gt; can point to the same volume for local development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you download a model through the UI, it gets uploaded to the appropriate volume, and then your workflows can access it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ngrok Part (Important!)
&lt;/h2&gt;

&lt;p&gt;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 &lt;code&gt;http://localhost:3011&lt;/code&gt; directly. That's where ngrok comes in.&lt;/p&gt;

&lt;p&gt;ngrok creates a public tunnel to your local API. When you start it, you'll get a URL like &lt;code&gt;https://abc123.ngrok-free.app&lt;/code&gt;. Update your &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CURRENT_API_URL="https://ngrok_generated_url.dev"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then restart your API server. Now Modal functions can reach your API and send status updates properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Create a User Record in the Database (CRITICAL - Must Do After First Login)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;⚠️ CRITICAL STEP:&lt;/strong&gt; After logging in with Clerk for the first time, you &lt;strong&gt;must&lt;/strong&gt; create a user record in your local database. Without this, you'll get &lt;code&gt;ForeignKeyViolationError&lt;/code&gt; errors when trying to use the application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is this needed?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;comfyui_deploy.users&lt;/code&gt; table due to foreign key constraints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to do this:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;After your first login via the frontend (&lt;a href="http://localhost:3001" rel="noopener noreferrer"&gt;http://localhost:3001&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;After restarting Docker (which wipes the database)&lt;/li&gt;
&lt;li&gt;Whenever you see errors like: &lt;code&gt;Key (user_id)=(user_xxx) is not present in table "users"&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Get Your Clerk User ID
&lt;/h3&gt;

&lt;p&gt;First, log in to the frontend at &lt;a href="http://localhost:3001" rel="noopener noreferrer"&gt;http://localhost:3001&lt;/a&gt;. Then get your Clerk user ID:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1: From Browser DevTools&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open DevTools (F12)&lt;/li&gt;
&lt;li&gt;Go to Console tab&lt;/li&gt;
&lt;li&gt;Type: &lt;code&gt;window.Clerk.user.id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copy the user ID (starts with &lt;code&gt;user_&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Option 2: From API Logs&lt;/strong&gt;&lt;br&gt;
Check your API server logs - you'll see the user ID in authentication errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: Key (user_id)=(user_123456789) is not present in table "users"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the User Record
&lt;/h3&gt;

&lt;p&gt;Replace &lt;code&gt;your-clerk-user-id&lt;/code&gt;, &lt;code&gt;your-username&lt;/code&gt;, and &lt;code&gt;Your Name&lt;/code&gt; with your actual values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;apps/api
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
python3 &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
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())
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example with actual values:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
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())
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✓ &lt;strong&gt;Verify User Creation&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql postgresql://postgres:postgres@localhost:5480/verceldb

&lt;span class="c"&gt;# In psql:&lt;/span&gt;
SELECT &lt;span class="nb"&gt;id&lt;/span&gt;, username, name FROM comfyui_deploy.users&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c"&gt;# You should see your user record&lt;/span&gt;
&lt;span class="c"&gt;# Exit psql with: \q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After creating the user&lt;/strong&gt;, refresh your browser at &lt;a href="http://localhost:3001" rel="noopener noreferrer"&gt;http://localhost:3001&lt;/a&gt;. The foreign key errors should be gone and you should be able to use the application normally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6.5: Attach an Autumn Plan to Your User or Organization (CRITICAL)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;⚠️ CRITICAL STEP:&lt;/strong&gt; After creating your user record, you &lt;strong&gt;must&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is this needed?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ComfyDeploy uses Autumn for billing and feature limits. Even in local development, the API checks Autumn to determine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How many machines you can create (&lt;code&gt;machine_limit&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;How many concurrent GPU jobs you can run (&lt;code&gt;gpu_concurrency_limit&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;How many workflows you can have (&lt;code&gt;workflow_limit&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;How many GPU credits you have available (&lt;code&gt;gpu-credit&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without a plan attached, you'll be on the default "Free" tier with very limited features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access the Autumn Dashboard
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://dashboard.autumn.dev/" rel="noopener noreferrer"&gt;https://dashboard.autumn.dev/&lt;/a&gt; (or &lt;a href="https://app.useautumn.com/" rel="noopener noreferrer"&gt;https://app.useautumn.com/&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Log in with your Autumn account&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Customers&lt;/strong&gt; in the sidebar&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Attach a Plan to Your Organization
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;For Organization-based Development&lt;/strong&gt; (Recommended):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Autumn dashboard, go to &lt;strong&gt;Customers&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Find or create a customer for your Clerk organization ID (e.g., &lt;code&gt;org_123456789&lt;/code&gt;)

&lt;ul&gt;
&lt;li&gt;If it doesn't exist, click &lt;strong&gt;"Create Customer"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter your Clerk organization ID as the customer ID&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click on the customer to view details&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;"Attach Plan"&lt;/strong&gt; or &lt;strong&gt;"Add Subscription"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;"Business (Monthly)"&lt;/strong&gt; plan&lt;/li&gt;
&lt;li&gt;Verify the plan includes:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GPU Concurrency Limit&lt;/strong&gt;: 10&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Machine Limit&lt;/strong&gt;: 25&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workflow Limit&lt;/strong&gt;: 300&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seats&lt;/strong&gt;: 10&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GPU Credit&lt;/strong&gt;: $10,000 per month (100,000 credits)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Price&lt;/strong&gt;: $998 per month&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;"Create"&lt;/strong&gt; or &lt;strong&gt;"Attach"&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;For User-based Development&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Autumn dashboard, go to &lt;strong&gt;Customers&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Find or create a customer for your Clerk user ID (e.g., &lt;code&gt;user_123456789&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Follow the same steps as above to attach the Business plan&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example Plan Configuration
&lt;/h3&gt;

&lt;p&gt;Here's what the Business (Monthly) plan should look like in Autumn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Feature Items Breakdown:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Included Usage&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gpu_concurrency_limit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;machine_limit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;Feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;workflow_limit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;td&gt;Feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;seats&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gpu-credit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$10,000 per GPU Credit (cents) per month&lt;/td&gt;
&lt;td&gt;Feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$998 per month&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Verify the Plan is Active
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Refresh your browser at &lt;a href="http://localhost:3001" rel="noopener noreferrer"&gt;http://localhost:3001&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Billing&lt;/strong&gt; (or &lt;strong&gt;Usage&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;You should see:

&lt;ul&gt;
&lt;li&gt;Current plan: &lt;strong&gt;Business&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Machine limit: &lt;strong&gt;25&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;GPU concurrency: &lt;strong&gt;10&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Available credits: &lt;strong&gt;100,000 credits&lt;/strong&gt; ($10,000)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Check via API:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_CLERK_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://localhost:3011/api/platform/plan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a response with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"plan"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"business"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"machine_limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"gpu_concurrency_limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"workflow_limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"gpu_credit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Common Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error: "Max Machines Exceeded" despite attaching a plan&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This usually means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The plan wasn't attached to the correct customer ID (check if you're using user ID vs organization ID)&lt;/li&gt;
&lt;li&gt;The Autumn cache needs to be refreshed - wait 30 seconds and try again&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;autumn.config.ts&lt;/code&gt; wasn't pushed - run &lt;code&gt;npx atmn push&lt;/code&gt; again&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Error: "Insufficient GPU credits"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Business plan includes $10,000 in GPU credits (100,000 credits). If you see this error:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check the Autumn dashboard to verify credits were added&lt;/li&gt;
&lt;li&gt;Make sure the &lt;code&gt;gpu-credit&lt;/code&gt; feature is included in the plan&lt;/li&gt;
&lt;li&gt;Try refreshing the page or logging out and back in&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Plan not showing in the frontend&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clear browser cache and hard refresh (Cmd+Shift+R or Ctrl+Shift+R)&lt;/li&gt;
&lt;li&gt;Check that &lt;code&gt;AUTUMN_SECRET_KEY&lt;/code&gt; is correct in &lt;code&gt;apps/api/.env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Restart the API server to pick up the latest Autumn data&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 7: Access the App
&lt;/h2&gt;

&lt;p&gt;Open &lt;a href="http://localhost:3001" rel="noopener noreferrer"&gt;http://localhost:3001&lt;/a&gt; in your browser. You should be able to log in with Clerk and start creating workflows.&lt;/p&gt;

&lt;p&gt;✓ &lt;strong&gt;Verify Full Stack&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in with your Clerk credentials&lt;/li&gt;
&lt;li&gt;Navigate to "Machines" → you should see an empty list&lt;/li&gt;
&lt;li&gt;Try creating a new workflow&lt;/li&gt;
&lt;li&gt;Check that the API is receiving requests (watch the API server logs)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Troubleshooting Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Database Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error: &lt;code&gt;relation "comfyui_deploy.machines" does not exist&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This means migrations didn't run or failed. Fix it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;apps/api
bun run migrate-local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the output for any errors. If migrations are stuck, you may need to reset:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# WARNING: This deletes all data!&lt;/span&gt;
docker-compose down &lt;span class="nt"&gt;-v&lt;/span&gt;
docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
bun run migrate-local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Error: &lt;code&gt;ForeignKeyViolationError: Key (user_id)=(...) is not present in table "users"&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is the most common error in local development.&lt;/strong&gt; The user record doesn't exist in the database. This happens when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You restarted Docker (which wipes the database)&lt;/li&gt;
&lt;li&gt;You're using a new Clerk user ID that hasn't been added to the database&lt;/li&gt;
&lt;li&gt;You skipped Step 6 after first login&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Run Step 6 to create the user record. This is a &lt;strong&gt;required step&lt;/strong&gt; after every Docker restart or first login.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error: &lt;code&gt;500 Internal Server Error on /api/volume/private-models&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Usually caused by missing user record. Check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get your Clerk user ID from the browser console:&lt;/span&gt;
&lt;span class="c"&gt;# Open DevTools → Application → Cookies → find __clerk_db_jwt&lt;/span&gt;
&lt;span class="c"&gt;# Decode it to see your user_id&lt;/span&gt;

&lt;span class="c"&gt;# Then verify the user exists:&lt;/span&gt;
psql postgresql://postgres:postgres@localhost:5480/verceldb
SELECT &lt;span class="k"&gt;*&lt;/span&gt; FROM comfyui_deploy.users WHERE &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'your-user-id'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user doesn't exist, create it (Step 6).&lt;/p&gt;

&lt;h3&gt;
  
  
  Port and Process Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error: &lt;code&gt;Address already in use&lt;/code&gt; on port 3011&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Something is already running on that port. Kill it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lsof &lt;span class="nt"&gt;-ti&lt;/span&gt;:3011 | xargs &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-9&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or find what's using it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lsof &lt;span class="nt"&gt;-i&lt;/span&gt; :3011
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Error: &lt;code&gt;Address already in use&lt;/code&gt; on port 3001 (frontend)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lsof &lt;span class="nt"&gt;-ti&lt;/span&gt;:3001 | xargs &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-9&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Error: ngrok tunnel keeps disconnecting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ngrok free tier has connection limits. If you're testing heavily:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upgrade to ngrok pro&lt;/li&gt;
&lt;li&gt;Or restart ngrok: &lt;code&gt;ngrok http 3011&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Modal and Workflow Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Runs stuck in "not-started" status&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This means Modal functions can't send status updates back to your API. Check:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is ngrok running?
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ps aux | &lt;span class="nb"&gt;grep &lt;/span&gt;ngrok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Is the ngrok URL in your &lt;code&gt;.env&lt;/code&gt;?
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;grep &lt;/span&gt;CURRENT_API_URL apps/api/.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Did you restart the API server after updating &lt;code&gt;.env&lt;/code&gt;?
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Kill the old server and restart it&lt;/span&gt;
   lsof &lt;span class="nt"&gt;-ti&lt;/span&gt;:3011 | xargs &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-9&lt;/span&gt;
   &lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3011 uv run src/api/server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Test the ngrok tunnel:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   curl https://your-ngrok-url.ngrok-free.app/api/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Error: "Max Machines Exceeded" despite having a paid plan&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is an Autumn billing configuration issue. The most common cause is &lt;strong&gt;not attaching a plan to your user or organization in the Autumn dashboard&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;🔴 MOST COMMON:&lt;/strong&gt; Did you attach a plan in the Autumn dashboard? (See Step 6.5)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="https://dashboard.autumn.dev/" rel="noopener noreferrer"&gt;https://dashboard.autumn.dev/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Customers&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Find your user ID or organization ID&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;"Attach Plan"&lt;/strong&gt; → Select &lt;strong&gt;"Business (Monthly)"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Verify the plan includes &lt;code&gt;machine_limit: 25&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Did you push your config to Autumn?&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;cd &lt;/span&gt;apps/api
   npx atmn push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Is the &lt;code&gt;machineLimit&lt;/code&gt; feature defined in your product?
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 5 &lt;span class="s2"&gt;"machineLimit"&lt;/span&gt; autumn.config.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Are you using the correct customer ID?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check if you're logged in as a user or organization&lt;/li&gt;
&lt;li&gt;The plan must be attached to the correct customer ID (user ID vs org ID)&lt;/li&gt;
&lt;li&gt;Check browser console: &lt;code&gt;window.Clerk.user.id&lt;/code&gt; or &lt;code&gt;window.Clerk.organization.id&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wait 30 seconds for Autumn cache to refresh, then try again&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Error: "GET /api/latest-hashes 404 Not Found" or "Illegal header value b'Bearer '"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This means the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; is missing or empty in your &lt;code&gt;.env&lt;/code&gt; file. The API needs this token to fetch ComfyUI version information from GitHub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a GitHub classic token at &lt;a href="https://github.com/settings/tokens" rel="noopener noreferrer"&gt;https://github.com/settings/tokens&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;public_repo&lt;/code&gt; scope&lt;/li&gt;
&lt;li&gt;Add to &lt;code&gt;apps/api/.env&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ghp_your_token_here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Restart the API server&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Error: "Invalid Volume name: ''" or "Can't find Volume: Invalid Volume name: ''"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This means the Modal volume environment variables are missing or empty. Modal requires valid volume names to create persistent storage for models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Symptoms:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add volume names to &lt;code&gt;apps/api/.env&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;SHARED_MODEL_VOLUME_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"comfydeploy-dev-public-models"&lt;/span&gt;
   &lt;span class="nv"&gt;PUBLIC_MODEL_VOLUME_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"comfydeploy-dev-public-models"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Restart the API server&lt;/li&gt;
&lt;li&gt;The volume will be automatically created in Modal on first use&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why this happens:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The code looks for &lt;code&gt;PUBLIC_MODEL_VOLUME_NAME&lt;/code&gt; or &lt;code&gt;SHARED_MODEL_VOLUME_NAME&lt;/code&gt; environment variables&lt;/li&gt;
&lt;li&gt;If both are empty, it returns an empty string &lt;code&gt;""&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Modal rejects empty volume names with a validation error&lt;/li&gt;
&lt;li&gt;This affects both the &lt;code&gt;/api/volume/public-models&lt;/code&gt; endpoint and serverless deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Error: Model downloads fail or "Function not found" errors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This usually means you forgot to deploy the &lt;code&gt;volume-operations&lt;/code&gt; Modal app, which is &lt;strong&gt;required&lt;/strong&gt; for model management.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Symptoms:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model downloads fail silently or with errors&lt;/li&gt;
&lt;li&gt;"Function not found" errors when trying to download models&lt;/li&gt;
&lt;li&gt;Models don't appear in Modal volumes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deploy the volume-operations app:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;cd &lt;/span&gt;apps/api
   modal deploy src/modal_apps/modal_downloader.py::modal_downloader_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Verify deployment:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   modal app list
   &lt;span class="c"&gt;# Should show "volume-operations" in the list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check the Modal dashboard at &lt;a href="https://modal.com/apps" rel="noopener noreferrer"&gt;https://modal.com/apps&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why this is required:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;volume-operations&lt;/code&gt; app contains Modal functions for downloading models&lt;/li&gt;
&lt;li&gt;Without it, the API can't download models to Modal volumes&lt;/li&gt;
&lt;li&gt;This is a one-time deployment per Modal environment&lt;/li&gt;
&lt;li&gt;See Step 5.4 in the setup guide for details&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Service Health Checks
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Check if PostgreSQL is running:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql postgresql://postgres:postgres@localhost:5480/verceldb &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"SELECT 1"&lt;/span&gt;
&lt;span class="c"&gt;# Expected: 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Check if Redis is running:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;redis-cli &lt;span class="nt"&gt;-p&lt;/span&gt; 6379 ping
&lt;span class="c"&gt;# Expected: PONG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Check if API server is responding:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3011/api/health
&lt;span class="c"&gt;# Expected: 200 OK&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Check if frontend is running:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3001
&lt;span class="c"&gt;# Expected: HTML response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Check API server logs for errors:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# The API server logs to stdout, so check your terminal&lt;/span&gt;
&lt;span class="c"&gt;# Look for ERROR or EXCEPTION messages&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Check Docker container logs:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose logs postgres    &lt;span class="c"&gt;# PostgreSQL logs&lt;/span&gt;
docker-compose logs redis       &lt;span class="c"&gt;# Redis logs&lt;/span&gt;
docker-compose logs             &lt;span class="c"&gt;# All services&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  React Query Caching Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Settings page shows "disabled for free tier" despite having a paid plan&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a React Query cache issue. The frontend cached stale plan data. Fix:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clear browser cache: DevTools → Application → Clear Storage&lt;/li&gt;
&lt;li&gt;Or hard refresh: Ctrl+Shift+R (Windows/Linux) or Cmd+Shift+R (Mac)&lt;/li&gt;
&lt;li&gt;Or log out and log back in&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If this keeps happening, check that your &lt;code&gt;.env&lt;/code&gt; has the correct &lt;code&gt;AUTUMN_SECRET_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's It!
&lt;/h2&gt;

&lt;p&gt;You're now ready to develop. The key insights:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;🔴 CRITICAL: Create user record after first login (Step 6)&lt;/strong&gt; - Without this, you'll get foreign key errors on every API call. This must be done after every Docker restart.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;🔴 CRITICAL: Attach Autumn plan (Step 6.5)&lt;/strong&gt; - 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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;🔴 CRITICAL: ngrok must be running (Step 5.5)&lt;/strong&gt; - Modal functions need to communicate back to your local API. Without ngrok, workflows will get stuck in "not-started" status.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fresh Docker restarts wipe data&lt;/strong&gt; - You'll need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Re-run migrations (&lt;code&gt;bun run migrate-local&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Recreate user records (Step 6)&lt;/li&gt;
&lt;li&gt;Verify Autumn plan is still attached (Step 6.5)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Autumn config must be pushed&lt;/strong&gt; - Changes to &lt;code&gt;autumn.config.ts&lt;/code&gt; don't take effect until you run &lt;code&gt;npx atmn push&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check logs first&lt;/strong&gt; - Most issues are visible in the API server logs or Docker logs&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Quick Start Checklist
&lt;/h2&gt;

&lt;p&gt;After cloning the repo, here's the minimal path to get running:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Install all required software (Node.js, Bun, Python 3.12, uv, Docker, AWS CLI, ngrok, psql)&lt;/li&gt;
&lt;li&gt;[ ] Create accounts and get API keys (Clerk, Autumn, AWS S3, Modal, ngrok, GitHub)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Setup:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Initialize git submodules: &lt;code&gt;git submodule init &amp;amp;&amp;amp; git submodule update&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Install dependencies (API: &lt;code&gt;bun install&lt;/code&gt; + &lt;code&gt;uv sync&lt;/code&gt;, Frontend: &lt;code&gt;bun install&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;[ ] Configure &lt;code&gt;.env&lt;/code&gt; files with all API keys (Clerk, Autumn, AWS S3, Modal, ngrok, &lt;strong&gt;GitHub&lt;/strong&gt;, &lt;strong&gt;Modal Volumes&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;[ ] Push Autumn config: &lt;code&gt;npx atmn push&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Start Docker: &lt;code&gt;docker compose up -d&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Run migrations: &lt;code&gt;bun run migrate-local&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;🔴 Install and configure ngrok: &lt;code&gt;brew install ngrok/ngrok/ngrok&lt;/code&gt; + &lt;code&gt;ngrok config add-authtoken YOUR_TOKEN&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;🔴 Start ngrok: &lt;code&gt;ngrok http 3011&lt;/code&gt;&lt;/strong&gt; (keep running)&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;🔴 Update &lt;code&gt;.env&lt;/code&gt; with ngrok URL and Modal volume names&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Start API server: &lt;code&gt;bun run dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Start frontend: &lt;code&gt;bun run dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;🔴 Log in via frontend, then create user record in database (Step 6)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;🔴 Attach Business plan to your user/org in Autumn dashboard (Step 6.5)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Verify everything works by creating a test workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding! 🚀&lt;/p&gt;

</description>
      <category>comfyui</category>
      <category>comfydeploy</category>
      <category>webdev</category>
      <category>fullstack</category>
    </item>
  </channel>
</rss>
