
http://localhost.
For the faster inner-loop dev flow without Kubernetes, use the Docker Setup guide instead. The K8s path is for contributors who need to reproduce storage, ingress, or snapshot bugs.
1. What you will run
OpenSail workloads
Backend, frontend, worker, Postgres, and Redis in the
tesslate namespace. Same manifests the cloud runs.NGINX Ingress
Installed via the minikube
ingress addon. Answers on localhost and *.localhost when minikube tunnel is running.btrfs CSI + Volume Hub
Per-node btrfs subvolumes, instant snapshot-clone, and CAS sync to MinIO. Mirrors the AWS storage stack one-for-one.
MinIO
S3-compatible object store in the
minio-system namespace. Backs content-addressable project snapshots.| Component | Namespace | Purpose |
|---|---|---|
| OpenSail backend | tesslate | FastAPI orchestrator |
| OpenSail frontend | tesslate | React UI served by NGINX |
| PostgreSQL | tesslate | Primary database |
| Redis | tesslate | Pub/sub and task queue |
| MinIO | minio-system | S3-compatible object store |
| btrfs CSI driver | kube-system | Per-node subvolumes and snapshots |
| Volume Hub | kube-system | Volume orchestrator, cache placement, S3 sync |
| Snapshot controller | kube-system | VolumeSnapshot CRDs |
| NGINX Ingress | ingress-nginx | HTTP routing |
tesslate. Every --context=tesslate flag below refers to this profile.
2. Prerequisites
minikube 1.33+
brew install minikube, choco install minikube, or download from minikube.sigs.k8s.io.kubectl 1.29+
brew install kubectl or choco install kubernetes-cli.Docker 24.x
Docker Desktop on macOS/Windows, or
docker-ce on Linux. Used as the minikube driver.System resources
- CPU: 4 cores available to Docker
- RAM: 8 GB available to Docker
- Disk: 40 GB free for the minikube VM
btrfs requirement
The btrfs CSI driver needs btrfs inside the minikube VM. The Docker driver’s base image already ships withbtrfs-progs and the driver auto-creates its pool at /mnt/tesslate-pool inside the node. No host filesystem changes are required when using --driver docker. If you switch to kvm2 or hyperkit, make sure the guest image has btrfs-progs and a mountable btrfs partition.
Hosts file
Add these entries:- Linux / macOS:
/etc/hosts - Windows:
C:\Windows\System32\drivers\etc\hosts
http://<slug>-<container>.localhost and are resolved by NGINX Ingress when minikube tunnel --profile tesslate is running. Modern browsers resolve *.localhost to 127.0.0.1 automatically; if yours does not, add the specific project host to hosts too.
3. Start the cluster
ingress addon installs NGINX Ingress in the ingress-nginx namespace. The tunnel exposes the Ingress controller on 127.0.0.1; keep that terminal open while you use the cluster.
4. Install snapshot controller and btrfs CSI driver
OpenSail depends on KubernetesVolumeSnapshot resources for hibernation and project timeline. Install the CRDs and controller first, then build and deploy the btrfs CSI driver.
Configure CSI credentials
The minikube overlay at Edit
services/btrfs-csi/overlays/minikube/ ships an example secrets file. Copy it first:csi-credentials.yaml only if you change the MinIO admin password. The default value matches the example MinIO secret you will edit in the next section. Both secrets must agree.What Volume Hub does
Volume Hub is the storageless orchestrator that sits above the per-node btrfs CSI driver. It exposes a gRPC API attesslate-volume-hub.kube-system.svc:9750 and the backend talks to it through orchestrator/app/services/hub_client.py.
| RPC | What it does |
|---|---|
CreateVolume | Pick a node with capacity, create an empty or template-cloned subvolume |
EnsureCached | Guarantee a volume is present on the node where a pod is about to be scheduled |
TriggerSync | Push CAS content to MinIO when a project hibernates |
DeleteVolume | Remove the subvolume and any S3 objects it owns |
5. Configure secrets
Each cluster secret has a*.example.yaml under k8s/overlays/minikube/secrets/. Copy and edit every file.
Fill in required values
| File | Key | Notes |
|---|---|---|
app-secrets.yaml | SECRET_KEY | python -c "import secrets; print(secrets.token_hex(32))" |
app-secrets.yaml | INTERNAL_API_SECRET | Same generator; must match ORCHESTRATOR_INTERNAL_SECRET in tesslate-btrfs-csi-config |
app-secrets.yaml | DATABASE_URL | postgresql+asyncpg://tesslate_user:<password>@postgres:5432/tesslate_dev |
app-secrets.yaml | LITELLM_API_BASE, LITELLM_MASTER_KEY | Your LiteLLM proxy or OpenAI-compatible endpoint |
postgres-secret.yaml | POSTGRES_PASSWORD | Must match the password embedded in DATABASE_URL |
s3-credentials.yaml | S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY | Must match MINIO_ROOT_USER / MINIO_ROOT_PASSWORD |
minio-credentials.yaml | MINIO_ROOT_USER, MINIO_ROOT_PASSWORD | MinIO admin credentials |
6. Deploy OpenSail
Build and load application images
K8S_DEVSERVER_IMAGE=tesslate-devserver:latest and K8S_IMAGE_PULL_POLICY=Never, so the devserver image must already be inside the node before any user project starts.Deploy MinIO
tesslate-projects (used by the backend) and tesslate-btrfs-snapshots (used by the CSI driver’s CAS sync).Apply the OpenSail overlay
k8s/base/ (namespace, backend, frontend, Postgres, Redis, Ingress, security, Volume Hub references) and applies the minikube-specific patches (local images, imagePullPolicy: Never, HTTP-only Ingress, single replicas).If all four rollouts succeed, OpenSail is live. Keep the
minikube tunnel terminal open.7. Access the app
With the tunnel running, NGINX Ingress answers onlocalhost. No port-forwarding needed for normal use.
| URL | What it serves |
|---|---|
http://localhost/ | Frontend |
http://localhost/api/ | Backend API |
http://<slug>-<container>.localhost/ | User project preview (for example, http://my-app-k3x8n2-frontend.localhost) |
http://minio.localhost/ | MinIO S3 API (after the Ingress rule or via port-forward below) |
OpenSail does not run Traefik in Kubernetes. Traefik is only the Docker Compose dev mode router; NGINX Ingress serves the same role on minikube and in AWS.
8. Seed the database
9. Create a project
Fromhttp://localhost/, sign up and create a project. Behind the scenes the backend:
- Creates a namespace
proj-<uuid>with a NetworkPolicy isolating it from other project namespaces. - Asks Volume Hub to pick a node with capacity and provision a btrfs subvolume, cloning from the template snapshot if one exists.
- Creates a PVC bound to storage class
tesslate-btrfs. PVC size defaults toK8S_PVC_SIZE=5Gi. - Creates one Deployment and Service per container declared in the project’s
.tesslate/config.json. Multiple containers are kept on the same node via pod affinity so they share the volume without cross-node traffic. - Adds an Ingress rule per exposed container on
http://<slug>-<container>.localhost.
10. Snapshots and hibernation
OpenSail keeps up toK8S_MAX_SNAPSHOTS_PER_PROJECT=5 VolumeSnapshot objects per project as a rolling timeline. Idle projects hibernate after K8S_HIBERNATION_IDLE_MINUTES=10 minutes: the backend calls Volume Hub TriggerSync to push the CAS content to MinIO, then tears down the compute pod while keeping the volume cached on its node.
11. Common commands
Always include--context=tesslate.
kubectl and docker exec calls with MSYS_NO_PATHCONV=1 so paths are not mangled.
12. Teardown
13. Troubleshooting
btrfs not supported or CSI node pod CrashLoops
btrfs not supported or CSI node pod CrashLoops
The minikube VM image must have btrfs tools available. Use If you changed driver, the safest fix is to recreate the cluster with the Docker driver.
--driver docker (confirmed working) and check the init container:Snapshot controller missing, no VolumeSnapshot API
Snapshot controller missing, no VolumeSnapshot API
Symptom:
no matches for kind "VolumeSnapshot" in version "snapshot.storage.k8s.io/v1". Re-run section 4.DNS resolution for *.localhost fails
DNS resolution for *.localhost fails
- Confirm
minikube tunnel --profile tesslateis still running in another terminal. kubectl --context=tesslate get ingress -A | grep proj-and verify the rule exists.kubectl --context=tesslate describe ingress -n proj-<uuid>and look for controller errors.
*.localhost automatically, add the specific host to /etc/hosts pointing at 127.0.0.1.Pod stuck in ImagePullBackOff
Pod stuck in ImagePullBackOff
Local images were not loaded into the minikube node, or the tag drifted.
Pod in CrashLoopBackOff
Pod in CrashLoopBackOff
DATABASE_URL in app-secrets.yaml, Postgres not ready yet, INTERNAL_API_SECRET mismatch between tesslate-app-secrets and tesslate-btrfs-csi-config, or missing llama-api-credentials for seeded apps.Database reset
Database reset
Next steps
AWS Production
Deploy OpenSail to EKS with NLB ingress, cert-manager, and Cloudflare DNS.
Docker Setup
Faster inner-loop dev without Kubernetes. Same code paths, different orchestrator.
Publishing Apps
Package and publish an app to the Tesslate marketplace from your local cluster.
Architecture
High-level tour of how the orchestrator, agents, and runtimes fit together.
Getting help
Discord
Real-time help from the Tesslate community.
GitHub
Source, issues, and release notes.
Direct support at
[email protected].