
http://localhost.
If you just need commands, jump to the Quick Start. For the native desktop app, see the Desktop install guide.
1. Prerequisites
| Tool | Minimum | Notes |
|---|---|---|
| Docker Desktop | 4.30+ (Engine 26+, Compose v2) | Required on macOS, Windows, and Linux desktops. Enable the WSL 2 backend on Windows. |
docker compose | v2.27+ | Ships with Docker Desktop. This guide uses the docker compose (no hyphen) form. |
git | 2.40+ | For cloning the repo. |
| Disk | 15 GB free | Images are about 2.5 GB. Named volumes (Postgres, projects, base cache, Redis) grow with use. |
| RAM | 8 GB minimum, 16 GB recommended | Orchestrator, worker, gateway, and user containers add up. |
| CPU | 4 cores | Vite HMR and agent runs are CPU sensitive. |
OS support
- macOS
- Windows
- Linux
- Colima
macOS 13+ on Intel or Apple Silicon. Docker Desktop with the Virtualization.framework backend is recommended. Apple Silicon pulls
arm64 images transparently.2. Clone and configure
Set required env vars
Only two values are genuinely required for first boot; everything else has sensible defaults.
| Variable | Why | How to get one |
|---|---|---|
SECRET_KEY | Signs JWTs and derives other secrets. | python -c "import secrets; print(secrets.token_hex(32))" |
LITELLM_API_BASE and LITELLM_MASTER_KEY | Backend routes LLM calls through a LiteLLM proxy. Without a real endpoint, agent features stay disabled but the app still boots. | Point at your existing proxy, or stand up your own. |
Review optional env groups
Open
.env.example for the full list. The groups below are the ones you are most likely to touch.| Group | Vars | When to set |
|---|---|---|
| Database | POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_PORT | Keep defaults for dev. Change POSTGRES_PORT only if 5432 is busy. |
| Redis | REDIS_URL, REDIS_PORT | Default redis://redis:6379/0 works in Compose. |
| Secrets | SECRET_KEY, INTERNAL_API_SECRET, CSRF_SECRET_KEY, DEPLOYMENT_ENCRYPTION_KEY, CHANNEL_ENCRYPTION_KEY | Generate real values for any environment you share. CHANNEL_ENCRYPTION_KEY needs a Fernet key: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())". |
| LiteLLM | LITELLM_API_BASE, LITELLM_MASTER_KEY, LITELLM_DEFAULT_MODELS, LITELLM_TEAM_ID, LITELLM_INITIAL_BUDGET | Required for the agent. LITELLM_DEFAULT_MODELS is a comma list, no spaces. |
| OAuth (optional) | GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_OAUTH_REDIRECT_URI, GITHUB_* | Enable social login. Without these only email/password works. Redirect URI for dev: http://localhost/api/auth/{google,github}/callback. |
| Ports | APP_DOMAIN, APP_PROTOCOL, APP_PORT, BACKEND_PORT, FRONTEND_PORT, TRAEFIK_DASHBOARD_PORT | Keep APP_DOMAIN=localhost for dev. Change individual ports only on conflicts. |
| Stripe (optional) | STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_*_PRICE_ID | Needed for billing UI. Use sk_test_* keys plus stripe listen for webhooks. |
| SMTP (optional) | SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, SMTP_SENDER_EMAIL, TWO_FA_ENABLED | Required for 2FA codes or password resets by email. |
3. First boot
Bring up the stack
Build the devserver image
The
devserver service in docker-compose.yml exists purely to produce the tesslate-devserver:latest image that every user project container derives from. Its entrypoint: true keeps it from running, but the build still happens when Compose sees it. If docker compose up skipped the build (for example after a clean image prune), run it explicitly:Services
| Service | Built from / image | Role |
|---|---|---|
traefik | traefik:v3.6 | Reverse proxy. Routes paths to the app or orchestrator and exposes *.localhost for user projects. |
postgres | postgres:15-alpine | Primary database. |
redis | redis:7-alpine | Pub/sub, ARQ task queue, distributed locks. |
orchestrator | orchestrator/Dockerfile | FastAPI backend. Mounts /var/run/docker.sock so it can spawn user containers. |
worker | Same image as orchestrator | Runs agent tasks off the ARQ queue. |
gateway | Same image as orchestrator | Persistent connections for Telegram, Slack, Discord, WhatsApp. Idle unless you enable channels. |
app | app/Dockerfile | Vite dev server with HMR. |
devserver | orchestrator/Dockerfile.devserver | Build-only image. Produces tesslate-devserver:latest that user project containers derive from. |
Verify health
health: starting for up to 30 seconds while Alembic migrations run. Tail it until Uvicorn reports ready:
4. Seed the database
On first backend startup the orchestrator automatically runsrun_all_seeds() from orchestrator/app/seeds/__init__.py. That covers themes, bases, agents, skills, MCP servers, and deployment targets on a clean database. Confirm:
5. Access URLs
| Target | URL | Notes |
|---|---|---|
| Frontend (via Traefik) | http://localhost | Use this for OAuth and cookie-correct testing. |
| Frontend direct | http://localhost:5173 | Vite dev server. Bypasses Traefik. |
| Backend API (via Traefik) | http://localhost/api | Frontend calls here. |
| Backend direct | http://localhost:8000 | Useful for curl. |
| OpenAPI docs | http://localhost:8000/docs | Swagger UI. |
| Traefik dashboard | http://localhost:8080 | Raw dashboard. |
| Traefik via proxy | http://localhost/traefik | Basic-auth gated. Defaults to admin:admin. Change TRAEFIK_BASIC_AUTH in .env. |
| PostgreSQL | localhost:5432 | Connect with pgAdmin or DBeaver. Creds from .env. |
| Redis | localhost:6379 | redis-cli -h localhost works. |
| User project | http://{container}.localhost | Wildcard is handled by Traefik. Some OS need dnsmasq or /etc/hosts entries. |
6. Create your first user
- Sign up in the UI
- Superuser via CLI
- Visit
http://localhost. - Click “Sign up”, enter email and password.
- You are logged in. Billing starts on the FREE tier.
7. Create your first project
New project
From the dashboard, click “New project”. Pick a base such as “Vite + React + FastAPI”, give it a name, and confirm. A slug like
my-app-k3x8n2 is generated.Wait for scaffolding
The orchestrator copies the template and writes a
docker-compose.yml into /projects/{slug}/. Watch for the “Project ready” toast.Start the containers
Click “Start”. Containers spin up on
tesslate-network and register with Traefik. The preview panel loads http://frontend.localhost (or whatever the base’s primary container is called).Talk to the agent
Open the chat panel and ask the agent to make a change. For the complete agent tool reference (read/write, bash, sessions, web search, skills, schedules), see the tesslate-agent reference.
8. Clean slate reset
tesslate-postgres-dev-data, tesslate-redis-data, tesslate-projects-data, tesslate-base-cache, and tesslate-gateway-locks.
Database-only reset:
Quick Start
For someone who has already read this guide once:Platform notes
WSL 2 (Windows)
- Clone into the WSL filesystem (for example
~/code/opensail). Bind mounts from/mnt/c/...are slow and drop file-change events. - Run
docker composefrom inside WSL, not from PowerShell. - When piping scripts that pass container paths through
docker exec, prefix withMSYS_NO_PATHCONV=1on Git Bash.
macOS with Colima
Colima replaces Docker Desktop on macOS. OpenSail works with two tweaks:Linux rootless Docker
The orchestrator bind-mounts the Docker socket so it can manage user project containers. Under rootless Docker the socket path is$XDG_RUNTIME_DIR/docker.sock, and the orchestrator inside the container cannot see it at /var/run/docker.sock. Either run rootful Docker for development, or change the volume mount in docker-compose.yml to the rootless socket and set DOCKER_HOST inside the orchestrator. Kubernetes mode sidesteps this entirely.
Volume permissions
On Linux, the Postgres volume is owned by UID 70 (the alpine postgres user). If you shell in as a different UID you may see permission errors writing to/var/lib/postgresql/data. Do not chown the volume from the host; let the container manage it.
Troubleshooting
Port already in use
Port already in use
Symptom: Re-run
bind: address already in use on 80, 5432, 6379, 8000, 5173, or 8080.Override the port in .env:docker compose up -d. If you changed APP_PORT, Traefik dashboard moves with it, so use http://localhost:8081.Orchestrator stuck unhealthy
Orchestrator stuck unhealthy
- Postgres not ready yet: wait 15 more seconds.
SECRET_KEYis empty or still at the placeholder.LITELLM_API_BASEunreachable: boot continues but the log shows warnings.- Port 8000 busy on the host: change
BACKEND_PORT.
*.localhost does not resolve
*.localhost does not resolve
Modern Linux (systemd-resolved), macOS, and Windows with WSL 2 resolve
*.localhost to 127.0.0.1 automatically. Some distros do not.- Linux: add
address=/localhost/127.0.0.1to dnsmasq, or add per-project entries to/etc/hosts. - Windows native: edit
C:\Windows\System32\drivers\etc\hosts. - Chrome and Firefox honor loopback for
*.localhostwithout/etc/hosts.
Hot reload not firing
Hot reload not firing
The compose file already sets
WATCHFILES_FORCE_POLLING=true, CHOKIDAR_USEPOLLING=true, and WATCHPACK_POLLING=true. If it still stops working:- Inotify limit hit on Linux:
sudo sysctl fs.inotify.max_user_watches=524288. - WSL 2: make sure the repo lives inside the WSL filesystem (
~/code/...), not/mnt/c/.... The/mntmount does not emit file events reliably.
Database connection failed
Database connection failed
docker compose logs postgres. Usually a leftover volume with a mismatched password; run the clean slate reset.User project container not reachable
User project container not reachable
- Traefik dashboard at
http://localhost:8080lists every router. Confirm your container is there. - Verify the container has
com.tesslate.routable=true(the orchestrator sets this automatically from the generated compose). - Check the container is on
tesslate-network:docker network inspect tesslate-network.
Docker socket permission denied (Linux)
Docker socket permission denied (Linux)
The orchestrator mounts
/var/run/docker.sock. If you run rootless Docker, the socket lives in $XDG_RUNTIME_DIR/docker.sock and the mount is wrong. Either run rootful Docker or edit the volume mount in docker-compose.yml.Tailing logs
Tailing logs
Next steps
Desktop install
Install the native Tauri desktop app with local SQLite and per-project runtimes.
Local Kubernetes
Mirror production on Minikube with btrfs CSI and Volume Hub.
AWS production deployment
Deploy OpenSail to AWS EKS with Terraform, ECR, and NGINX Ingress.
Publishing Apps
Turn a project into a Tesslate App and list it in the marketplace.
Getting help
Discord
Chat with the OpenSail team and community.
GitHub Issues
File bugs and feature requests.
Email support
Direct support for teams and self-hosters.