Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add README #18

Merged
merged 15 commits into from
Aug 7, 2024
154 changes: 154 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Modal ComfyUI Deploy

Run your ComfyUI workflows at blazing fast speed 🏎️ on the cloud GPUs powered by [Modal](https://modal.com/).

Stop breaking your local ComfyUI environment setups when experimenting with new ideas 💡 or stop waiting for 🐌 slow builds to run on your machines. A fully open source and self hosted solution to run your ComfyUI workflows on the powerful cloud GPUs

## App Screenshots

![Create App Screenshot](./screenshots/app/create-app-page.png)

![Apps Screenshot](./screenshots/app/apps-page.png)

![Workflow Screenshot](./screenshots/app/workflow-page.png)

## Requirements

Local Dev

- Node - `>=v20.x.x`
- Python - `>=3.10.x`
- Docker

For running self hosted solution, please sign-up for following services:

- [Modal account](https://modal.com/) - Running workflows
- [Clerk account](https://clerk.com/) - Authentication
- [Fly.io account](https://fly.io/) - Hosting

## Local Setup

### Frontend

Pre-requisites

- Node >v20.x.x - recommended to install via some node version manager like [nvm](https://github.com/nvm-sh/nvm) or [n](https://github.com/tj/n)

Once the Node is installed, please run below commands to install and run the app locally

#### 1. Change directory:

```sh
cd web/
```

#### 2. Install dependencies:

```sh
npm install
```

#### 3. Setup Environment variables:

Create `.env.` file from `.env.sample` and add required keys(more info in the video linked below)

```
cp .env.sample .env
```

#### 4. Run app

```sh
npm run dev
```

### Backend

Pre-requisites

- Install [Docker Desktop](https://www.docker.com/products/docker-desktop/) to avoid messing with Python virtual environment

#### 1. Change directory:

```sh
cd backend/
```

#### 2. Setup Environment variables:

Create `.env.` file from `.env.sample` and add required keys(more info in the video linked below)

```
cp .env.sample .env
```

#### 3. Run app

```sh
docker-compose -f docker-compose.local.yml up --build
```

> Note: Make sure docker desktop is running before running this command

## Hosting

The current implementation hosts both frontend and backend on [Fly.io](https://fly.io/). However, you can use any other service of your choice to host using the Dockerfile for both frontend and backend

Pre-requisites

- Sign up on [Fly.io](https://fly.io/)
- Install [flyctl](https://fly.io/docs/flyctl/install/)

### Frontend

#### 1. Change directory:

```sh
cd web/
```

#### 2. Create Fly app:

[Create App](./docs/flyio/create-flyio-app.md)

#### 3. Setup Environment variables:

You need to add all the environment variables present in your `./web/.env` file to Flyio [Secrets](https://fly.io/docs/apps/secrets/#setting-secrets) for Flyio to pick them during the deploy

You can setup via command line using [flyctl](https://fly.io/docs/apps/secrets/#set-secrets) or use the app dashboard page `https://fly.io/apps/<your-app-name>/secrets`

#### 4. Deploy app:

Make sure you're in `/web` directory before running below command

```sh
fly deploy
```

### Backend

#### 1. Change directory:

```sh
cd backend/
```

#### 2. Create Fly app:

[Create App](./docs/flyio/create-flyio-app.md) (Same steps as frontend)

#### 3. Setup Environment variables:

You need to add all the environment variables present in your `./backend/.env` file to Flyio [Secrets](https://fly.io/docs/apps/secrets/#setting-secrets) for Flyio to pick them during the deploy

You can setup via command line using [flyctl](https://fly.io/docs/apps/secrets/#set-secrets) or use the app dashboard page `https://fly.io/apps/<your-app-name>/secrets`

> Note: Make sure to include base url of the deployed frontend app from the above step in the comma separated list of `CORS_ALLOWED_ORIGINS` variable in your secrets

#### 4. Deploy app:

Make sure you're in `/backend` directory before running below command

```sh
fly deploy
```
13 changes: 13 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Required for creating app, getting list of apps from Modal
MODAL_TOKEN_ID=<ADD_MODAL_TOKEN_ID>
MODAL_TOKEN_SEC

# Required to download models from Civitai. Note: Not all models required api key
CIVITAI_TOKEN=<ADD_CIVITAI_TOKEN>

# Required to authenticate some of the protected API routes. It should be read from the DB but we're keeping it simple
X_API_KEY=<ADD_API_KEY>

# List of allowed origins by BE. Required as FE directly talks to BE for listening to logs using EventSource.
# Rest of the BE API routes are called via Remix actions & loaders so it is not required there
CORS_ALLOWED_ORIGINS="http://localhost:5173"
7 changes: 3 additions & 4 deletions backend/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,9 @@ async def lifespan(app: FastAPI):

app = FastAPI(lifespan=lifespan)

origins = [
"http://localhost:5173",
"https://punitd-modal-comfyui-deploy.fly.dev"
]
origins = [item.strip() for item in
os.environ.get('CORS_ALLOWED_ORIGINS', '').split(',')]

app.add_middleware(
CORSMiddleware,
allow_origins=origins,
Expand Down
50 changes: 50 additions & 0 deletions docs/flyio/create-flyio-app.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## Create Flyio App

Make sure you've installed [flyctl](https://fly.io/docs/flyctl/install/) before running this step

```sh
fly launch
```

You'll see following prompt, please enter "y" to copy the config:

```sh
An existing fly.toml file was found for app punitd-modal-comfyui-deploy
? Would you like to copy its configuration to the new app? (y/N)
```

You'll now see one more prompt confirming to deploy directly or launching a new app, please enter "y" as you'll be creating a new app
in this case

```sh
Scanning source code
Detected a Remix app
App 'punitd-modal-comfyui-deploy' already exists. You can deploy to it with `fly deploy`.
? Continue launching a new app? (y/N)
```

You'll now see following prompt(final one) asking you whether you want to tweak app settings. Please select "y" to change config like name and region based on your choice

```sh
Creating app in /Users/punitdama/Personal/AI/modal-comfyui-deploy-app/web
We're about to launch your Remix app on Fly.io. Here's what you're getting:

Organization: PunitD (fly launch defaults to the personal org)
Name: punitd-modal-comfyui-deploy-falling-shadow-4219 (generated)
Region: Singapore, Singapore (from your fly.toml)
App Machines: shared-cpu-1x, 2GB RAM (from your fly.toml)
Postgres: <none> (not requested)
Redis: <none> (not requested)
Tigris: <none> (not requested)

? Do you want to tweak these settings before proceeding? (y/N)
```

You'll be redirected to web page which would look like below:

![Launch Flyio App](../../screenshots/flyio/launch-flyio-app.png)

Enter app name, region, VM sizes as per you choice and requirements

Click "Confirm Settings" and you'de be redirected back to terminal.
You're app is now ready to deploy! 😎
Binary file added screenshots/app/apps-page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/app/create-app-page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/app/workflow-page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/flyio/launch-flyio-app.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions web/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Base url of backend API
APP_BUILDER_API_BASE_URL=<Add Key here>
# API key to access few protected API routes in backend
APP_BUILDER_API_KEY=<Add Key here>

# Required for setting up Clerk for authentication
CLERK_PUBLISHABLE_KEY=<Add Key here>
CLERK_SECRET_KEY=<Add Key here>
CLERK_SIGN_IN_URL=sign-in
CLERK_SIGN_IN_FORCE_REDIRECT_URL=create-app

# Only emails added to the whitelist can login and use the app.
# It allows us to restrict the app usage to restricted email ids. This is done keeping in mind that app is publicly available and
# we just self host it to deploy our comfyui workflows for individual users or teams
EMAIL_WHITELIST=<Add your email whitelist in CSV format>
40 changes: 0 additions & 40 deletions web/README.md

This file was deleted.

2 changes: 1 addition & 1 deletion web/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export async function loader(args: LoaderFunctionArgs) {
return rootAuthLoader(args, () => {
return json({
ENV: {
MACHINE_BUILDER_API_BASE_URL: process.env.MACHINE_BUILDER_API_BASE_URL,
APP_BUILDER_API_BASE_URL: process.env.APP_BUILDER_API_BASE_URL,
},
});
});
Expand Down
2 changes: 1 addition & 1 deletion web/app/routes/app-logs.$taskId/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default function MachineLogs() {
if (taskId) {
const streamUrl = `${
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any).ENV.MACHINE_BUILDER_API_BASE_URL
(window as any).ENV.APP_BUILDER_API_BASE_URL
}/app-logs/${taskId}`;
eventSource = new EventSource(streamUrl);

Expand Down
8 changes: 4 additions & 4 deletions web/app/routes/apps/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ export async function action({ request }: LoaderFunctionArgs) {
const actionType = formData.get("actionType");
const appId = formData.get("appId");

const url = `${process.env.MACHINE_BUILDER_API_BASE_URL}/apps/${appId}`;
const url = `${process.env.APP_BUILDER_API_BASE_URL}/apps/${appId}`;

if (actionType === "delete") {
try {
const response = await fetch(url, {
method: "DELETE",
headers: {
X_API_KEY: process.env.MACHINE_BUILDER_API_KEY!,
X_API_KEY: process.env.APP_BUILDER_API_KEY!,
},
});

Expand All @@ -77,13 +77,13 @@ export const loader = async (args: LoaderFunctionArgs) => {
return redirect("/sign-in");
}

const url = `${process.env.MACHINE_BUILDER_API_BASE_URL}/apps`;
const url = `${process.env.APP_BUILDER_API_BASE_URL}/apps`;

try {
const response = await fetch(url, {
method: "GET",
headers: {
X_API_KEY: process.env.MACHINE_BUILDER_API_KEY!,
X_API_KEY: process.env.APP_BUILDER_API_KEY!,
},
});

Expand Down
2 changes: 1 addition & 1 deletion web/app/routes/create-app/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {
const requestBody = generateCreateMachineRequestBody(formData);

try {
const url = `${process.env.MACHINE_BUILDER_API_BASE_URL}/app`;
const url = `${process.env.APP_BUILDER_API_BASE_URL}/app`;
const response = await fetch(url, {
method: "POST",
headers: {
Expand Down
2 changes: 1 addition & 1 deletion web/app/routes/upload-workflow-file/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { sendErrorResponse, sendSuccessResponse } from "~/server/utils";

export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const url = `${process.env.MACHINE_BUILDER_API_BASE_URL}/generate-custom-nodes`;
const url = `${process.env.APP_BUILDER_API_BASE_URL}/generate-custom-nodes`;

try {
const response = await fetch(url, {
Expand Down