How Geass works
Geass is a Kubernetes operator running on a K3s cluster that you own, plus a web dashboard served by that operator. The dashboard writes your intent into Kubernetes objects, and the operator turns those objects into running workloads. The geass command line and its MCP server reach the same dashboard through an API, so they follow the same path. This page describes the parts, how a change moves through them, and where your data lives, so that you know what to look at when something misbehaves.
The parts
Section titled “The parts”- The cluster. The installer installs K3s in server mode with an embedded etcd database (
server --cluster-init). K3s brings the pieces Geass relies on: containerd, the overlay network, CoreDNS, the Traefik ingress controller, and a Helm controller that installs charts fromHelmChartobjects. The first server is the control plane. Workers join it as K3s agents. See Add servers. - The operator. One Deployment,
geass-controller-manager, in thegeass-systemnamespace, with one replica and leader election. It is required to run on the control plane, because the installer loads its image there. It has controllers for clusters, nodes, projects, services, builds, terminal sessions, databases, logical databases, buckets, backup records, connections, high availability readiness and the platform settings. - The dashboard. A React application embedded in the operator binary and served by the operator on port 8085, behind the
ClusterIPServicegeass-geass-dashboard. It has no database and no server of its own: its HTTP API reads and writes Kubernetes objects. It signs users in itself (see Sign in to the dashboard) and accepts API tokens for scripts. The same process serves the versioned JSON API under/api/v1, which is the dashboard’s own operations in a shape a program can call. A stream of change events (/api/events) keeps open browser tabs current. - Add-ons. The operator installs these by creating
HelmChartobjects inkube-system, which K3s’s Helm controller applies: cert-manager, a lite Prometheus stack for metrics, Longhorn for database volumes, and Fluent Bit with VictoriaLogs for log history. Others are added when you first need them: CloudNativePG when you create a PostgreSQL database, MinIO when you turn on in-cluster object storage, and Helm charts for MySQL and Redis. One is opt-in: KEDA and its HTTP add-on, in thekedanamespace, which let a service sleep when idle. The operator installs them only whenspec.addons.serverless.enabledis set on the cluster, and never uninstalls them. See Sleep when idle. With the metrics add-on on, the operator also creates aServiceMonitorand aPrometheusRule, both namedgeass-operator, in themonitoringnamespace, so Prometheus scrapes the operator and has alert rules for failing or stalled reconciles, such asGeassReconcileErrors. The defaultliteprofile installs no Alertmanager, so a firing rule shows in Prometheus and notifies nobody. The Usage page charts the operator’s metrics under Geass health. When the cluster has a Let’s Encrypt contact email (the installer’sGEASS_ACME_EMAIL), the operator also keeps the cert-managerClusterIssuernamedletsencrypt-prod, which every HTTPS certificate is requested from. See HTTPS certificates. - Your workloads. The Deployments, Services, Ingresses, volumes and jobs the operator creates for your services, databases and buckets, in one namespace per project environment.
- Connectors. Optional links to Cloudflare, GitHub, AWS and PlanetScale. The operator calls their APIs from inside the cluster with credentials you saved as Kubernetes Secrets.
- The command line and the MCP server. One program,
geass, that you install on your own machine, not in the cluster. It calls/api/v1on the dashboard with an API token that you got by approving a code in the dashboard (geass login), and it never talks to Kubernetes.geass mcpruns the same program as a Model Context Protocol server, so an AI agent acts as that token and sees only the tools it allows. See Command line and MCP server for AI agents.
you: a browser, the geass command line, an AI agent (geass mcp), or scripts | HTTPS on your domain, or kubectl port-forward (first sign-in) |+---------------- control plane server: K3s server + embedded etcd ------------------+| Traefik ingress (ports 80 and 443) || || namespace geass-system || geass-controller-manager = operator (controllers) + dashboard (UI and API) || Geass objects: projects, services, databases, buckets, nodes ... and Secrets || || add-ons: cert-manager, Prometheus, Longhorn, Fluent Bit + VictoriaLogs || opt-in add-on: KEDA + HTTP add-on (services that sleep when idle) || namespaces <project-id>-<environment>: your workloads |+------------------------------------------------------------------------------------+ | K3s agent, port 6443 and the overlay network |+------------------------+ +------------------------+| worker server | | worker server || your workloads | | your workloads |+------------------------+ +------------------------+
operator --> GitHub, Cloudflare, AWS (S3, IAM), PlanetScale (only what you connect)What runs where
Section titled “What runs where”| Component | Runs on |
|---|---|
| K3s server, etcd, the operator, the dashboard | The control plane server. The operator is pinned there |
| Traefik and the other K3s system pods | Wherever Kubernetes schedules them |
| Services, databases, buckets, add-on pods | Any node they are allowed on: the project’s region and the node tags you set decide. K3s does not keep workloads off the control plane server, so a single-server install runs everything on one machine. See Scaling and placement |
| The K3s agent and your workloads on a worker | The worker |
How a change flows
Section titled “How a change flows”Take the flow of a new service, from Deploy your first app.
- You add a service. The dashboard creates a
GeassAppobject ingeass-systemas a draft:deploy.enabledisfalse, and annotations mark it as using the deploy workflow with one pending change,created. The operator reports that the service is waiting for deployment and creates nothing for it: no Deployment exists yet. - You edit it. Saving settings or variables updates the same object and adds a pending marker (
settingsorvariables) in its annotations. The top bar of the project counts the markers. - Some settings do not wait. A replica count, autoscaling, sleep when idle, the domain and HTTPS, metrics, and a few more are listed as live. The operator applies them from the current object even while the rest waits for a deploy, and the dashboard reports
Saved. Applied now. - You choose Deploy changes. For every pending resource in the environment, the dashboard first checks that the cluster has room for it, then sets
deploy.enabled: true, saves a snapshot of the settings in thegeass.dev/last-deployedannotation, clears the pending markers and, for a service, records a deployment (GeassDeployment) for the history. - The operator reconciles. It works from the snapshot, with live settings overlaid. For a service it creates a Deployment, a Service of the same name and, if the service has a hostname, an Ingress, plus the config and Secret objects for its variables, an autoscaler or a metrics monitor when you asked for them. For a service that sleeps when idle it also creates a wake Service and a KEDA
HTTPScaledObject, and for a function with schedules, oneCronJobper schedule. For a service built from a GitHub repository it first runs a Kaniko build job. For a database it creates the database server and its volume, and for a bucket the bucket and its credentials. - Status comes back. The operator writes the result to the object’s status (the
Readycondition, the URL), and the dashboard shows it as Setting up, then Ready, or Failed.
A deploy that fails does not retry for ever. After repeated failures the resource stops, shows Failed, and waits for you to use Retry.
From the command line or an agent
Section titled “From the command line or an agent”geass deploy web --wait follows the same flow. The command makes one or two calls to /api/v1, which the dashboard turns into the operation behind the matching button, so the same rules apply: new services are drafts, edits wait as pending until a deploy, and the token’s role decides what is allowed. With --wait the command keeps reading the service’s status until the operator reports it Ready or Failed, then exits with a code a script can act on. geass mcp offers an agent the same operations as tools. See Command line.
Deletes go the other way. Deleting a resource marks the object for deletion; the operator removes what it created, in the cluster and outside it (an S3 bucket, an IAM user), and only then lets the object go. See Uninstall Geass.
Where state lives
Section titled “Where state lives”Geass has no database of its own. All of it is in Kubernetes or on your servers’ disks.
| What | Where |
|---|---|
| Projects, services, databases, buckets, nodes, connections, the platform settings and their pending changes | Kubernetes objects of the geass.geass.dev group, stored in K3s’s embedded etcd on the control plane’s disk |
| Dashboard users, session key, login lockouts | The Secret geass-dashboard-auth |
API tokens (a hash of each secret), including the ones geass login and agents hold |
The Secret geass-api-tokens |
| Command-line sign-ins waiting for approval | The operator’s memory only, for 10 minutes. Restarting the operator cancels them |
| The command line’s saved logins | config.json in the geass folder of the user configuration directory on the machine where you ran geass login. It holds a token, so its mode is 0600 |
| Service secrets, shared project secrets, connection credentials, the join token | Secrets in geass-system |
| A function’s schedule token (only for a function that has schedules or is an event handler) | The Secret <service-id>-trigger in the service’s namespace |
| TLS certificates | cert-manager’s Secret <service-id>-tls in the service’s namespace. The ACME account key, letsencrypt-prod-account-key, is in cert-manager |
| Plain service variables | The service’s settings, applied to its workload as a ConfigMap |
| PostgreSQL, MySQL and SQLite data | Volumes on the nodes. With Longhorn, which the installer enables, a volume keeps one copy per node, up to three, or one per instance for highly available Postgres. A database created without Longhorn uses K3s’s local-path storage on one node |
| Redis data | Memory only. Geass runs Redis without persistence |
| In-cluster bucket contents | The MinIO server, on a 10 Gi volume |
| AWS S3 buckets | Your AWS account |
| Database backups | A bucket in your AWS account that you choose in Project settings |
| Metrics | Prometheus: 15 days, at most 4 GB, on a 5 Gi volume |
| Log history | VictoriaLogs: 7 days by default, at most 4 GiB |
| Container images you build | The registry you configure |
Geass does not back up the cluster’s own state. K3s’s embedded etcd takes snapshots on its defaults (twice a day, five kept, in the same data directory), so they do not protect you from losing the disk. Database backups to S3 are the only backups Geass makes. See FAQ.
Namespaces and names
Section titled “Namespaces and names”| Namespace | Holds |
|---|---|
geass-system |
The operator, every Geass object, and Geass’s Secrets |
<project-id>-<environment> |
One project environment’s workloads, such as proj-1a2b3c4d-production |
kube-system |
K3s’s own components, Traefik, and the HelmChart objects for the add-ons |
cert-manager, monitoring, longhorn-system, geass-logging, cnpg-system, keda |
The add-ons. keda exists only when the serverless add-on is on |
A service’s Kubernetes name is its ID, which Geass generates. The dashboard shows a display name that you can change. See Core concepts.
Networking
Section titled “Networking”Traefik, which K3s installs, serves HTTP and HTTPS on ports 80 and 443. For each service with a hostname the operator creates an Ingress that routes that host to the service’s Service. Inside the cluster, a service is reachable at <service-id>.<namespace>.svc.cluster.local:<port>. Nodes talk over K3s’s overlay network, VXLAN by default, or WireGuard in a multi-site cluster.
Each project environment is isolated by network policies that the operator creates: only services of the same project, Geass itself and visitors through your domains reach a project’s services, unless you allow other projects. The platform namespaces also reach them: kube-system, monitoring for Prometheus, cnpg-system and, when the serverless add-on is on, keda. See Network isolation.
What Geass connects to
Section titled “What Geass connects to”Only what the features you use need. The installer downloads from GitHub and get.k3s.io. The Helm controller downloads charts, and containerd pulls images, from public hosts. When you connect them, the operator calls the GitHub, Cloudflare, AWS (S3 and IAM) and PlanetScale APIs. It asks api.ipify.org, then ifconfig.me, for the server’s public IP only when no node reports one and it needs the address for dashboard DNS. cert-manager talks to Let’s Encrypt (acme-v02.api.letsencrypt.org) once the certificate issuer exists, and Geass posts to the webhook URLs you add to a project. With the serverless add-on on, the Helm controller also downloads charts from kedacore.github.io. The geass command line talks only to the Geass address you give it. The repository contains no telemetry or analytics code.
Look at it yourself
Section titled “Look at it yourself”Everything above is visible with kubectl, through k3s kubectl on the control plane:
sudo k3s kubectl get crds | grep geasssudo k3s kubectl -n geass-system get geassprojects,geassapps,geassdatabases,geassobjectstoressudo k3s kubectl -n geass-system get geasscluster default -o yamlsudo k3s kubectl -n kube-system get helmchartssudo k3s kubectl get clusterissuersudo k3s kubectl -n geass-system logs deploy/geass-controller-manager -c managerThe same facts are available without kubectl access, through the API: geass status -p proj-1a2b3c4d -e production lists a project’s services with their state and the reason one is not ready. The reference for each kind is in Resources overview.
Limits worth knowing
Section titled “Limits worth knowing”- The control plane is a single server and a single operator. Losing that server stops the dashboard and all reconciliation. See High availability.
- The operator image and the dashboard ship together. The only supported way to a new release is a new install. See Upgrade Geass.