Deployment Overview
Lakehousecat is deployed on Kubernetes using a Kubernetes Operator that manages the complete lifecycle of your data platform instances. This operator-based approach provides automated deployment, configuration management, scaling, and upgrades across any Kubernetes environment.
How Lakehousecat Deployment Works
The Lakehousecat deployment consists of two main components:
1. Lakehousecat Operator (Cluster-Wide)
The Operator is a Kubernetes controller that:
- Manages Lakehousecat instances across multiple namespaces
- Validates licenses via the Lakehousecat license service (
license.lakehousecat.com) - Pulls Lakehousecat's own images (Custom Images + the Operator itself) from the public GitHub
Container Registry (
ghcr.io/lakehousecat) — no registry credentials required — and pulls third-party images (PostgreSQL, ClickHouse, Airflow, etc.) directly from their official upstream registries - Orchestrates 20+ microservices (Airflow, PostgreSQL, Valkey, ClickHouse, Superset, SeaweedFS, etc.)
- Handles upgrades, scaling, and configuration changes
Install once per cluster - The operator can manage multiple Lakehousecat instances.
Install exactly one Operator instance per Kubernetes cluster, in its own dedicated namespace. This single Operator can manage any number of Lakehousecat instances (Custom Resources) across multiple namespaces — you do not need a separate Operator per instance.
- Operator and CRD versions must match. The Operator's Custom Resource Definition (CRD) is registered cluster-wide, not per namespace. When upgrading, always upgrade the Operator together with its CRD as a matched pair (see Upgrade).
- Running multiple Operator instances in the same cluster is not an officially supported or recommended configuration. It would require every instance to run the exact same Operator and CRD version, and provides no known operational benefit over a single Operator managing multiple instances.
Lakehousecat's release process publishes Custom Images and the Operator itself to the public
GitHub Container Registry (ghcr.io/lakehousecat). No AWS account, ECR access, or image pull
secret is required on the customer side — the operator pulls these images anonymously, and pulls
all third-party images directly from their respective official upstream registries.
2. Lakehousecat Instance (Namespace-Specific)
Each instance is defined as a Custom Resource (CR) that specifies:
- License credentials (handshake key)
- Version and architecture (amd64/arm64)
- Storage configuration (S3, SeaweedFS, etc.)
- Domain and TLS settings
- Resource allocations and scaling parameters
Deploy multiple instances - Each instance runs in its own namespace with isolated resources.
Operator Permissions
The Lakehousecat Operator follows the principle of least privilege. It does not require
cluster-admin rights, has no rights in kube-system and does not create namespaces. Its write
access is confined to the instance namespaces you list in the chart value targetNamespaces:
the chart binds one RoleBinding per entry, applied by helm install/helm upgrade with your
rights, not the operator's. Create those namespaces before installing the chart.
Inside each instance namespace (RoleBinding to <release>-target-namespace-permissions):
| Resource Group | Resources | Access |
|---|---|---|
Core ("") | secrets, configmaps, services, persistentvolumeclaims, serviceaccounts, pods | Manage instance resources |
Core ("") | pods/log, endpoints, events | Read; events also written |
apps | deployments, statefulsets, replicasets | Deploy and manage application workloads |
batch | jobs, cronjobs | Orchestrate data ingestion workflows |
networking.k8s.io | ingresses, networkpolicies | Configure ingress and network policies |
rbac.authorization.k8s.io | roles, rolebindings | Namespace-scoped RBAC for instance service accounts |
autoscaling | horizontalpodautoscalers | Enable HPA-based autoscaling |
policy | poddisruptionbudgets | Ensure availability during cluster maintenance |
coordination.k8s.io | leases | Database leader election |
discovery.k8s.io | endpointslices | Database readiness |
In its own namespace (Role): its secrets (handshake key, license token cache) and the
leader election lease. Jobs and pods there are only read, never created: the operator runs no
workload of its own in that namespace.
The operator never connects to an instance's database. License renewal sends the handshake key, the cluster ID and a signature — no user counts. The number of users is known to the Portal from accepted invitations, which is also what your subscription is billed by.
Cluster-wide (ClusterRole <release>-cluster-scope), only what Kubernetes cannot scope to a
namespace, and no write access to any namespaced resource:
| Resource Group | Resources | Access |
|---|---|---|
lhc.lakehousecat.com | lakehousecats, lakehousecats/status, lakehousecats/finalizers | The operator's own cluster-scoped CRD |
Core ("") | namespaces | get on kube-system (cluster ID for licensing) and the target namespaces; patch on the target namespaces (labels). By name only. |
Core ("") | nodes | list — detects whether the CNI enforces NetworkPolicies (k3s) |
apps | daemonsets | list — same detection, by CNI DaemonSet name |
networking.k8s.io | ingressclasses | get — verifies the configured ingress class exists |
In addition, a Role in default allows reading the kubernetes Service and its Endpoints by
name: the instance NetworkPolicies need the API server's addresses for their egress rule.
Create its namespace, add it to targetNamespaces and run helm upgrade on the operator. A
Lakehousecat resource pointing at a namespace that is not prepared stays in phase
CreatingNamespace with the condition NamespaceReady=False and a message saying what is missing.
Prerequisites
Before deploying Lakehousecat, ensure you have:
Required Software
- Kubernetes: Version 1.29 or higher — this is a hard minimum, not a recommendation. Semantic jobs run their backend as a native sidecar container, which became stable in 1.29 and is what keeps job payloads isolated from service credentials.
- Helm: Version 3.8 or higher
- kubectl: Configured and connected to your cluster
- Handshake Key: Obtained from portal.lakehousecat.com after registration (free)
Cluster Resources (Minimum)
For Single-User Evaluation:
- 10 CPU cores (minimum)
- 32 GB RAM (minimum)
- 50 GB free disk space (SSD recommended)
- Supported OS: macOS, Linux, Windows
For Production Multi-User Deployment:
- 16+ CPU cores
- 64+ GB RAM
- 500+ GB persistent storage (scales with data volume)
- Persistent volume provisioner (for database storage)
Network Requirements
- Outbound HTTPS access to:
license.lakehousecat.com(license validation)ghcr.io(Lakehousecat Custom Images + Operator — public, no credentials required) and the official upstream registries of the third-party services the operator deploys (PostgreSQL, ClickHouse, Airflow, etc.) — all container image pulls are handled automatically by the operator
- DNS resolution for internal Kubernetes service discovery
- Ingress controller (required for external access via domain)
Domain and TLS
A domain you control is required, not optional — spec.domain.baseDomain is a mandatory
field on the Lakehousecat Custom Resource. Without it, the Kubernetes API rejects the CR outright
and the Operator never starts reconciling. There is no IP-only mode: the UI is a browser
application with absolute URLs, CORS, cookies, and OAuth callbacks that all depend on a real
domain.
You need:
- A domain (or subdomain) you control
- Access to its DNS — either directly, or someone (e.g. your IT department) who can create a single record for you
That record points at the address of your ingress LoadBalancer, which only becomes available once the cluster and ingress controller are up. Whether that address is a hostname (typical for cloud load balancers) or an IP (typical on-premise / bare metal) depends on your platform — see the EKS or On-Premise guide.
If you're pointing your bare domain (e.g. company.com, not a subdomain) at a hostname-based
load balancer, standard DNS forbids a CNAME record on the zone apex. Use your DNS provider's
ALIAS/ANAME equivalent (e.g. Route 53 Alias records), or use a subdomain (e.g. lakehousecat.company.com) instead, which allows a normal CNAME.
Recommended order: create the DNS record before applying the CR. cert-manager then requests the certificate as soon as the Ingress is reconciled, with no extra steps.
If you set the record after the CR is already applied, the certificate can be delayed for up to
30 minutes even though the domain resolves publicly right away. This is caused by the
cluster-internal DNS resolver (CoreDNS) caching the earlier "domain doesn't exist" answer
(NXDOMAIN) — a standard DNS behavior (RFC 2308) driven
by your DNS zone's SOA minimum TTL (commonly up to 24h upstream; CoreDNS caps this at 30 minutes).
To force an immediate recheck instead of waiting:
kubectl -n kube-system rollout restart deployment coredns
The certificate is typically issued within about a minute after that. While you wait — with or without the restart — the instance is fully reachable over HTTPS using nginx's self-signed fallback certificate; this is expected, not a failure. To check what cert-manager is actually waiting on:
kubectl -n <namespace> get challenge
kubectl -n <namespace> get challenge -o jsonpath='{.items[0].status.reason}'
Retrying is safe — cert-manager performs a self-check before contacting Let's Encrypt and won't submit a request (and won't consume any rate-limit quota) until that check succeeds.
For HTTPS, spec.domain.tls supports three paths:
| Situation | Configuration | How the certificate is obtained |
|---|---|---|
| Public domain — you or your DNS provider can complete a Let's Encrypt challenge | tls.letsEncrypt: true | cert-manager requests and renews the certificate automatically |
| Public domain, DNS managed by your IT department | same as above — a single DNS record is all IT needs to create | same as above |
| Internal-only domain, no public DNS validation possible | tls.secretName: "<your-secret>" | You provide your own certificate (internal CA or self-signed) as a Kubernetes Secret; Let's Encrypt is not used |
See On-Premise: TLS Certificate for a worked example of the third case.
The Ingress always terminates TLS. If neither Let's Encrypt nor a custom secret is configured correctly, nginx falls back to its own self-signed default certificate — the instance keeps running, but browsers show a certificate warning until a valid certificate is in place.
Deployment Options
Choose the deployment option that best fits your needs:
Local Evaluation
Best for: Single-user evaluation, development, testing
Two options are available:
- TUI Installer — Recommended. A terminal application that automates the full setup: Minikube cluster, Helm repository, operator, and instance. No Kubernetes experience required.
- Manual Setup — For advanced users who need full control over the configuration.
Resource Requirements: 32 GB RAM (minimum), 10 CPU cores, 50 GB free disk, macOS or Linux
Kubernetes Cluster
Best for: Production deployments, multi-user environments, scalability
Deploy Lakehousecat on managed or self-hosted Kubernetes clusters:
- AWS EKS (Elastic Kubernetes Service)
- Google GKE (Google Kubernetes Engine)
- Azure AKS (Azure Kubernetes Service)
- On-Premise - Self-managed Kubernetes clusters
Resource Requirements: 16+ CPU cores, 64+ GB RAM, 500+ GB storage
Deployment Process Overview
Regardless of which option you choose, the deployment follows the same basic steps:
Step 1: Prepare Kubernetes Cluster
Set up your Kubernetes environment (Minikube, EKS, GKE, AKS, or on-premise).
Step 2: Install Lakehousecat Operator
Install the operator using Helm:
helm repo add lakehousecat https://charts.lakehousecat.com
helm repo update
# Create the instance namespace first: the operator only gets rights in the
# namespaces listed in targetNamespaces and does not create them.
kubectl create namespace lhc-instance
helm install lhc-operator lakehousecat/lakehousecat-operator \
--namespace lhc-operator \
--create-namespace \
--set "targetNamespaces={lhc-instance}" \
--wait
Step 3: Configure Handshake Secret
Create a secret with your handshake key from the portal:
kubectl create secret generic lhc-handshake-secret \
--from-literal=handshake-key=YOUR_HANDSHAKE_KEY \
-n lhc-operator
Step 4: Deploy Lakehousecat Instance
Create a Lakehousecat Custom Resource (CR):
apiVersion: lhc.lakehousecat.com/v1alpha2
kind: Lakehousecat
metadata:
name: my-instance
spec:
license:
handshakeSecretName: "lhc-handshake-secret"
customer:
namespace: "lhc-instance"
adminemail: "admin@company.com"
version: "0.0.42"
architecture: "amd64" # amd64 | arm64
# External S3 (optional) — credentials via a pre-created secret, never in the CR.
# Omit to use the default in-cluster SeaweedFS. See "Required Secrets".
objectStorage:
type: s3
region: "us-east-1"
bucketPrefix: "my-data"
credentialsSecretName: "lhc-storage-s3-secret" # keys: access-key, secret-key
arm64 excludes one datasource typearchitecture: "arm64" excludes IBM Db2 as a datasource type — IBM does not ship a Linux/ARM64
driver for it. Every other datasource type is unaffected. See IBM Db2 — Prerequisites for details.
Apply the configuration:
kubectl apply -f lakehousecat-instance.yaml
Step 5: Verify Deployment
Monitor the deployment:
# Check status of your specific instance (replace my-instance and lhc-instance with your values)
kubectl get lakehousecat my-instance -n lhc-instance
# View operator logs
kubectl logs -f deployment/lhc-operator -n lhc-operator
# Check pods in instance namespace
kubectl get pods -n lhc-instance
Verifying a Successful Deployment
Check all instances across all namespaces:
kubectl get lakehousecats -A
Phase: Ready means the Operator has reconciled all components and all pods are online and healthy. If the phase shows Pending, the Operator is still reconciling — check the CR description for details:
kubectl describe lakehousecat <instance-name> -n <namespace>
Check individual pod status:
kubectl -n <instance-namespace> get pods
All pods should show Running status with all containers ready. The full set of expected pods in a healthy deployment:
| Pod | Component |
|---|---|
airflow-api-server | Airflow API |
airflow-dag-processor | DAG processing |
airflow-pgbouncer | Airflow DB connection pool |
airflow-scheduler | Airflow scheduler |
airflow-statsd | Airflow metrics |
airflow-triggerer | Airflow triggerer |
airflow-worker | Airflow task worker |
analytics | Analytics service |
audio | Audio transcription service |
clickhouse-keeper-0 | ClickHouse coordination |
clickhouse-shard0-0 | ClickHouse analytics DB |
lhc | LHC Core backend |
lhc-pi-agent | AI Agent |
llm | LLM integration service |
postgresql-0, postgresql-1 | PostgreSQL (Patroni-managed HA pair — leader/replica roles are dynamic, not pod-name-bound) |
seaweedfs-filer | Object storage — filer |
seaweedfs-master | Object storage — master |
seaweedfs-s3 | Object storage — S3 API (only when objectStorage.type: seaweedfs; skipped for external S3) |
seaweedfs-volume | Object storage — volume server |
semantic | Semantic processing service |
superset | Superset web application |
superset-worker | Superset Celery worker |
ui | Lakehousecat web UI |
valkey-primary | Valkey primary |
This list reflects the default configuration (in-cluster SeaweedFS). With objectStorage.type: s3
the four seaweedfs-* pods are not deployed at all — see Configure Storage.
Image Integrity
The Operator pins the Lakehousecat core images to the exact digests of the released version. The list of released digests is signed and delivered by the Lakehousecat license service during the Operator handshake; the Operator deploys a core image only if its digest matches that list. Third-party upstream images (for example Airflow, ClickHouse, Superset) are not part of this list.
The result is reported as the ImageIntegrity condition on the instance:
kubectl get lakehousecat <instance-name> -n <namespace> \
-o jsonpath='{.status.conditions[?(@.type=="ImageIntegrity")]}' | jq
| Status / Reason | Meaning | What to do |
|---|---|---|
True / DigestsPinned | All core images are pinned to released digests | Nothing |
False / TrustMapUnavailable | No verified digest list received yet | Usually resolves on its own once the digest list for the release is available; if it persists, contact support |
False / TrustMapInvalid | A digest list arrived but its signature did not verify | Contact support |
False / DigestMissing | The list has no entry for an image the instance uses — the message names the first gap | Check that the instance pulls from the official registry or a mirror of it (see below) |
False / DigestMismatch | A pod cannot pull its pinned image — the message names the pod | The registry serves a different image than the one released; see mirrors below |
This condition is independent of licensing: it never puts the instance into read-only mode and never stops pods that are already running. Without a verified digest list, however, new deployments and updates are held back until the list arrives.
If you mirror Lakehousecat images into a private registry, copy them with their manifests
intact — for example with crane copy, skopeo copy --all, or a pull-through/remote repository
in your registry product. A plain docker pull / docker tag / docker push rebuilds the
manifest, changes the digest, and the pinned pull then fails with DigestMismatch.
First start timing: On initial deployment, expect about 10 minutes before all pods reach Running state. Most of that time is spent pulling images from GHCR and the upstream registries, so the exact duration depends on your cluster's network bandwidth — on managed cloud clusters (GKE, EKS, AKS) it is usually faster.
Step 6: Access Lakehousecat
Once deployed, access Lakehousecat via:
Port-forward (for testing):
kubectl port-forward svc/lhc -n lhc-instance 42021:42021
# Access at http://localhost:42021
Ingress (for production): Configure domain and TLS in the Lakehousecat CR (see cluster guides for details).
Subscription Tiers
A handshake key is required for all tiers, including FREE. Register at portal.lakehousecat.com — no paid subscription is required to get started.
For the full tier table (pricing, Max Users, Max Instances, Data Sources) and how seat/instance limits are enforced, see Billing.
Multi-Cloud and On-Premise Support
The Lakehousecat Operator is designed to run on any Kubernetes cluster, regardless of where it's hosted:
- ✅ AWS EKS - Tested and supported
- ⚠️ Google GKE - Not yet tested by Lakehousecat
- ⚠️ Azure AKS - Not yet tested by Lakehousecat
- ✅ On-Premise - Self-managed Kubernetes clusters
- ✅ Minikube - Local development and evaluation
The operator abstracts cloud-specific differences, providing a consistent deployment experience across all environments.
What Gets Deployed
When you create a Lakehousecat instance, the operator automatically deploys:
Application Services
- UI - SvelteKit web application
- LHC Core - Backend API and business logic
- Analytics - Query execution and chart generation
- LLM - Large language model integration
- Semantic - Natural language processing
- Audio - Audio transcription services
Data Storage
- PostgreSQL - Primary relational database
- ClickHouse - Columnar analytics database
- Valkey - In-memory cache and messaging
- SeaweedFS - S3-compatible object storage
Workflow Orchestration
- Apache Airflow - ETL pipeline orchestration
- Scheduler, Workers, Webserver, Triggerer, DAG Processor
Business Intelligence
- Apache Superset - Data visualization and dashboards
- Web application, Celery workers
Observability Endpoints (Bring Your Own Monitoring)
- Each backend service exposes a
/metricsendpoint in Prometheus exposition format - Structured application logs are written to the SeaweedFS
backend_logs/bucket - The Operator does not deploy Prometheus or Grafana — customers integrate with their existing monitoring stack (Prometheus, Datadog, Grafana Mimir, AWS Managed Prometheus, etc.). See Monitoring for details.
All services are configured, networked, and managed automatically by the operator.
Upgrade and Maintenance
Upgrades and scaling are managed through the Operator after initial deployment.
- For upgrade instructions (backup recommendations, Helm commands, verification): see Upgrade
- For scaling configuration: see Scaling
Do not upgrade Airflow, ClickHouse, or Superset via Helm or any other means outside of the Lakehousecat Operator. Independent upgrades break compatibility and are not supported. Component version updates are delivered as part of Lakehousecat releases.
Next Steps
Choose your deployment path:
- TUI Installer - Fastest path: automated local evaluation setup
- Manual Setup - Advanced local evaluation with full configuration control
- Kubernetes Cluster - Deploy to AWS EKS, Google GKE, Azure AKS, or on-premise
Support
- Documentation: https://docs.lakehousecat.com
- Portal: https://portal.lakehousecat.com
- Support: portal.lakehousecat.com
- Slack: Direct support channel for Standard, Premium and Enterprise customers, accessible from the Support page in the portal
- Operator README: Detailed operator documentation in the Helm chart