Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Single-domain public URL (no trailing slash)
FLR_PUBLIC_URL=https://flowrabbit.example.com

# Required secrets
FLR_CLIENT_API_KEY=replace-with-random-secret
FLR_ADMIN_EMAIL=admin@example.com
FLR_ADMIN_PASSWORD=replace-with-strong-password

# Optional legacy hostnames (used only with docker-compose.legacy-hosts.yaml)
FLR_STUDIO_HOST=studio-os.flowrabbit.ai
FLR_API_HOST=api-os.flowrabbit.ai
FLR_PROXY_HOST=proxy-os.flowrabbit.ai
FLR_APPS_HOST=apps-os.flowrabbit.ai
FLR_DOCS_HOST=docs-os.flowrabbit.ai
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
websocket
websocket

# Local runtime artifacts
mongo-data/
app-data/
letsencrypt/
.env
40 changes: 36 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,44 @@ docker compose up

to start Flowrabbit. Make sure you have installed Docker before and have a domain name.

Flowrabbit® Studio will spin up four servers, each needing their own subdomain. The easiest way to install to install
Flowrabbit® Studio is to use the following Docker Compose file.
Flowrabbit® Studio supports two deployment modes:

- Single-domain (recommended for Elestio / one-click hosting): path-based routing on one domain
- Legacy subdomain mode: host-based routing (`api.*`, `studio.*`, `proxy.*`, `apps.*`, `docs.*`) via override file

The docker compose file will also spin up a Traefik reverse proxy server to handle SSL. To make everything work, you need to
update the sub domain URLs in the beginning of the file (`FLR_<app>_HOST`). Do not forget to also update the `"traefik.http.routers.<app>.rule=Host(....)"` sections below.
Start by creating a `.env` file from `.env.example` and setting required secrets.

For single-domain mode, set `FLR_PUBLIC_URL` in `docker-compose.yaml`, for example:

> Note: The long compose snippet below is kept for reference and may lag behind current defaults. Use `docker-compose.yaml` in this repository as source of truth.

```yaml
FLR_PUBLIC_URL: https://flowrabbit.example.com
```

In single-domain mode this will expose:

- `https://flowrabbit.example.com/` -> studio
- `https://flowrabbit.example.com/api` -> backend
- `https://flowrabbit.example.com/proxy` -> proxy
- `https://flowrabbit.example.com/apps` -> apps
- `https://flowrabbit.example.com/docs` -> docs

The docker compose file also spins up Traefik for routing on port `80`.
TLS should be terminated by your hosting platform (for example, Elestio ingress/load balancer).

Use these commands:

```sh
# Single-domain mode (default)
docker compose up -d

# Local mode (forces public URL to localhost)
docker compose -f docker-compose.yaml -f docker-compose.local.yaml up -d

# Legacy host-based mode (optional compatibility mode)
docker compose -f docker-compose.yaml -f docker-compose.legacy-hosts.yaml up -d
```


Also, you need to set the `FLR_CLIENT_API_KEY`, `FLR_JWT_PASSWORD`, `FLR_DB_ENCRYPTION_KEY` and `FLR_ADMIN_PASSWORD`
Expand Down
10 changes: 5 additions & 5 deletions apps/public/config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"api_URL": "https://api-os.flowrabbit.ai",
"proxy_URL": "https://proxy-os.flowrabbit.ai",
"app_URL": "http://localhost:8081",
"node_URL": "https://node-os.flowrabbit.ai"
}
"api_URL": "/api",
"proxy_URL": "",
"app_URL": "/apps",
"node_URL": "/docs"
}
11 changes: 6 additions & 5 deletions apps/src/services/Services.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ class Services {
this.config = {
'default': true,
'auth': 'qux',
'api': "https://api-os.flowrabbit.ai",
'api': "/api",
'user': {
'allowSignUp': true
},
'websocket': '',
'proxy_URL': 'https://proxy-os.flowrabbit.ai',
'api_URL': 'https://api-os.flowrabbit.ai',
'node_URL': 'https://node-os.flowrabbit.ai'
'proxy_URL': '',
'api_URL': '/api',
'app_URL': '/apps',
'node_URL': '/docs'
}
}

Expand Down Expand Up @@ -70,4 +71,4 @@ class Services {
}

}
export default new Services()
export default new Services()
2 changes: 1 addition & 1 deletion apps/src/views/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export default {
},
domains: {
},
appURL: Services.getConfig().app_URL || "apps-os.flowrabbit.ai",
appURL: Services.getConfig().app_URL || "/apps",
isAppStore: false
}
},
Expand Down
30 changes: 30 additions & 0 deletions docker-compose.legacy-hosts.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
services:
backend:
labels:
- "traefik.http.routers.api.rule=Host(`${FLR_API_HOST:-api-os.flowrabbit.ai}`)"
- "traefik.http.routers.api.entrypoints=web"
- "traefik.http.routers.api.service=api"

studio:
labels:
- "traefik.http.routers.studio.rule=Host(`${FLR_STUDIO_HOST:-studio-os.flowrabbit.ai}`)"
- "traefik.http.routers.studio.entrypoints=web"
- "traefik.http.routers.studio.service=studio"

proxy:
labels:
- "traefik.http.routers.proxy.rule=Host(`${FLR_PROXY_HOST:-proxy-os.flowrabbit.ai}`)"
- "traefik.http.routers.proxy.entrypoints=web"
- "traefik.http.routers.proxy.service=proxy"

apps:
labels:
- "traefik.http.routers.apps.rule=Host(`${FLR_APPS_HOST:-apps-os.flowrabbit.ai}`)"
- "traefik.http.routers.apps.entrypoints=web"
- "traefik.http.routers.apps.service=apps"

docs:
labels:
- "traefik.http.routers.docs.rule=Host(`${FLR_DOCS_HOST:-docs-os.flowrabbit.ai}`)"
- "traefik.http.routers.docs.entrypoints=web"
- "traefik.http.routers.docs.service=docs"
20 changes: 20 additions & 0 deletions docker-compose.local.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
services:
backend:
environment:
FLR_PUBLIC_URL: http://localhost

studio:
environment:
FLR_PUBLIC_URL: http://localhost

proxy:
environment:
FLR_PUBLIC_URL: http://localhost

apps:
environment:
FLR_PUBLIC_URL: http://localhost

docs:
environment:
FLR_PUBLIC_URL: http://localhost
88 changes: 43 additions & 45 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,67 +1,50 @@
x-common-env: &common-env
FLR_PUBLIC_URL: ${FLR_PUBLIC_URL:-http://localhost}
TZ: America/Chicago
FLR_AUTH_SERVICE: qux
FLR_USER_ALLOW_SIGNUP: "true"
FLR_USER_ALLOWED_DOMAINS: "*"
FLR_INTERNAL_API_URL: http://backend:8080
FLR_INTERNAL_PROXY_URL: http://proxy:8084
FLR_API_URL: https://api-os.flowrabbit.ai
FLR_APPS_URL: https://apps-os.flowrabbit.ai
FLR_PROXY_URL: https://proxy-os.flowrabbit.ai
FLR_NODE_URL: https://node-os.flowrabbit.ai
FLR_HTTP_HOST: https://studio-os.flowrabbit.ai
FLR_CLIENT_API_KEY: Put_Some_Secret_In_Here
FLR_ADMIN_EMAIL: email@example.com
FLR_ADMIN_PASSWORD: Put_Some_Secret_In_Here
FLR_API_URL: ${FLR_PUBLIC_URL:-http://localhost}/api
FLR_APPS_URL: ${FLR_PUBLIC_URL:-http://localhost}/apps
FLR_PROXY_URL: ${FLR_PUBLIC_URL:-http://localhost}
FLR_NODE_URL: ${FLR_PUBLIC_URL:-http://localhost}/docs
FLR_HTTP_HOST: ${FLR_PUBLIC_URL:-http://localhost}
FLR_CLIENT_API_KEY: ${FLR_CLIENT_API_KEY:?Set FLR_CLIENT_API_KEY}
FLR_ADMIN_EMAIL: ${FLR_ADMIN_EMAIL:?Set FLR_ADMIN_EMAIL}
FLR_ADMIN_PASSWORD: ${FLR_ADMIN_PASSWORD:?Set FLR_ADMIN_PASSWORD}

services:
traefik:
image: traefik:v3.0
command:
# Providers
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--api.dashboard=true"

# Entrypoints
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"

# Redirect all HTTP -> HTTPS
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"

# ACME / Let's Encrypt
- "--certificatesresolvers.myresolver.acme.email=support@flowrabbit.ai"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.myresolver.acme.caserver=https://acme-v02.api.letsencrypt.org/directory"

# Request certs at startup (not just on first request)
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
- "--entrypoints.websecure.http.tls.certresolver=myresolver"
- "--entrypoints.websecure.http.tls.domains[0].main=docs-os.flowrabbit.ai"


ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./letsencrypt:/letsencrypt"

mongo:
restart: always
container_name: mongo
image: mongo
volumes:
- ./mongo-data:/data/db # pth for the data to be stored and kept on your host machine is on the left side of the ":"
- mongo-data:/data/db
backend:
restart: always
container_name: backend
image: flowrabbit/backend:latest
volumes:
- ./app-data:/app-data
- app-data:/app-data
environment:
<<: *common-env
FLR_HTTP_PORT: 8080
Expand All @@ -78,9 +61,12 @@ services:
- mongo
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api-os.flowrabbit.ai`)" # update here
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=myresolver"
# Single-domain path-based routing
- "traefik.http.routers.api-path.rule=PathPrefix(`/api`)"
- "traefik.http.routers.api-path.entrypoints=web"
- "traefik.http.routers.api-path.priority=100"
- "traefik.http.routers.api-path.middlewares=api-strip"
- "traefik.http.middlewares.api-strip.stripprefix.prefixes=/api"
- "traefik.http.services.api.loadbalancer.server.port=8080"
studio:
restart: always
Expand All @@ -90,9 +76,10 @@ services:
<<: *common-env
labels:
- "traefik.enable=true"
- "traefik.http.routers.studio.rule=Host(`studio-os.flowrabbit.ai`)" # update here
- "traefik.http.routers.studio.entrypoints=websecure"
- "traefik.http.routers.studio.tls.certresolver=myresolver"
# Single-domain path-based routing (default catch-all)
- "traefik.http.routers.studio-path.rule=PathPrefix(`/`)"
- "traefik.http.routers.studio-path.entrypoints=web"
- "traefik.http.routers.studio-path.priority=1"
- "traefik.http.services.studio.loadbalancer.server.port=8082"
proxy:
restart: always
Expand All @@ -102,9 +89,10 @@ services:
<<: *common-env
labels:
- "traefik.enable=true"
- "traefik.http.routers.proxy.rule=Host(`proxy-os.flowrabbit.ai`)" #update here
- "traefik.http.routers.proxy.entrypoints=websecure"
- "traefik.http.routers.proxy.tls.certresolver=myresolver"
# Single-domain path-based routing
- "traefik.http.routers.proxy-path.rule=PathPrefix(`/proxy`) || PathPrefix(`/stream`) || PathPrefix(`/mcp`)"
- "traefik.http.routers.proxy-path.entrypoints=web"
- "traefik.http.routers.proxy-path.priority=100"
- "traefik.http.services.proxy.loadbalancer.server.port=8084"
apps:
restart: always
Expand All @@ -114,9 +102,12 @@ services:
<<: *common-env
labels:
- "traefik.enable=true"
- "traefik.http.routers.apps.rule=Host(`apps-os.flowrabbit.ai`)" #update here
- "traefik.http.routers.apps.entrypoints=websecure"
- "traefik.http.routers.apps.tls.certresolver=myresolver"
# Single-domain path-based routing
- "traefik.http.routers.apps-path.rule=PathPrefix(`/apps`)"
- "traefik.http.routers.apps-path.entrypoints=web"
- "traefik.http.routers.apps-path.priority=100"
- "traefik.http.routers.apps-path.middlewares=apps-strip"
- "traefik.http.middlewares.apps-strip.stripprefix.prefixes=/apps"
- "traefik.http.services.apps.loadbalancer.server.port=8083"
docs:
restart: always
Expand All @@ -126,7 +117,14 @@ services:
<<: *common-env
labels:
- "traefik.enable=true"
- "traefik.http.routers.docs.rule=Host(`docs-os.flowrabbit.ai`)" #update here
- "traefik.http.routers.docs.entrypoints=websecure"
- "traefik.http.routers.docs.tls.certresolver=myresolver"
- "traefik.http.services.docs.loadbalancer.server.port=8085"
# Single-domain path-based routing
- "traefik.http.routers.docs-path.rule=PathPrefix(`/docs`)"
- "traefik.http.routers.docs-path.entrypoints=web"
- "traefik.http.routers.docs-path.priority=100"
- "traefik.http.routers.docs-path.middlewares=docs-strip"
- "traefik.http.middlewares.docs-strip.stripprefix.prefixes=/docs"
- "traefik.http.services.docs.loadbalancer.server.port=8085"

volumes:
mongo-data:
app-data: