
http://localhost.
1. What you will run
OpenSail workloads
tesslate namespace. Same manifests the cloud runs.NGINX Ingress
ingress addon. Answers on localhost and *.localhost when minikube tunnel is running.btrfs CSI + Volume Hub
MinIO
minio-system namespace. Backs content-addressable project snapshots.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-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.
Install the snapshot controller CRDs
Build and load the btrfs CSI image
Configure CSI credentials
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.Deploy the CSI driver and Volume Hub
tesslate-btrfs-csi-nodeDaemonSet (one pod per node)tesslate-volume-hubDeployment (Hub plus CSI provisioner / snapshotter sidecars)tesslate-image-precacheDaemonSet (pre-pulls the devserver image on every node)
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.
5. Configure secrets
Each cluster secret has a*.example.yaml under k8s/overlays/minikube/secrets/. Copy and edit every file.
Copy the example secrets
Fill in required values
Optional: Llama API secret for seeded apps
crm-demo and nightly-digest apps reference a cluster secret called llama-api-credentials. Without it, those pods fail to start.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).Wait for rollouts
minikube tunnel terminal open.7. Access the app
With the tunnel running, NGINX Ingress answers onlocalhost. No port-forwarding needed for normal use.
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
--driver docker (confirmed working) and check the init container:Snapshot controller missing, no VolumeSnapshot API
Snapshot controller missing, no VolumeSnapshot API
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
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
Docker Setup
Publishing Apps
Architecture
Getting help
Discord
GitHub
[email protected].