Skip to main content
Version: Next

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.

Best Practice: One Operator per Cluster

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.
Release distribution via GitHub Container Registry

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 GroupResourcesAccess
Core ("")secrets, configmaps, services, persistentvolumeclaims, serviceaccounts, podsManage instance resources
Core ("")pods/log, endpoints, eventsRead; events also written
appsdeployments, statefulsets, replicasetsDeploy and manage application workloads
batchjobs, cronjobsOrchestrate data ingestion workflows
networking.k8s.ioingresses, networkpoliciesConfigure ingress and network policies
rbac.authorization.k8s.ioroles, rolebindingsNamespace-scoped RBAC for instance service accounts
autoscalinghorizontalpodautoscalersEnable HPA-based autoscaling
policypoddisruptionbudgetsEnsure availability during cluster maintenance
coordination.k8s.ioleasesDatabase leader election
discovery.k8s.ioendpointslicesDatabase 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 GroupResourcesAccess
lhc.lakehousecat.comlakehousecats, lakehousecats/status, lakehousecats/finalizersThe operator's own cluster-scoped CRD
Core ("")namespacesget on kube-system (cluster ID for licensing) and the target namespaces; patch on the target namespaces (labels). By name only.
Core ("")nodeslist — detects whether the CNI enforces NetworkPolicies (k3s)
appsdaemonsetslist — same detection, by CNI DaemonSet name
networking.k8s.ioingressclassesget — 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.

Adding an instance

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.

CNAME is not allowed on a zone apex

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:

SituationConfigurationHow the certificate is obtained
Public domain — you or your DNS provider can complete a Let's Encrypt challengetls.letsEncrypt: truecert-manager requests and renews the certificate automatically
Public domain, DNS managed by your IT departmentsame as above — a single DNS record is all IT needs to createsame as above
Internal-only domain, no public DNS validation possibletls.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.

If TLS is misconfigured

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

→ Kubernetes Cluster Guide

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 type

architecture: "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:

PodComponent
airflow-api-serverAirflow API
airflow-dag-processorDAG processing
airflow-pgbouncerAirflow DB connection pool
airflow-schedulerAirflow scheduler
airflow-statsdAirflow metrics
airflow-triggererAirflow triggerer
airflow-workerAirflow task worker
analyticsAnalytics service
audioAudio transcription service
clickhouse-keeper-0ClickHouse coordination
clickhouse-shard0-0ClickHouse analytics DB
lhcLHC Core backend
lhc-pi-agentAI Agent
llmLLM integration service
postgresql-0, postgresql-1PostgreSQL (Patroni-managed HA pair — leader/replica roles are dynamic, not pod-name-bound)
seaweedfs-filerObject storage — filer
seaweedfs-masterObject storage — master
seaweedfs-s3Object storage — S3 API (only when objectStorage.type: seaweedfs; skipped for external S3)
seaweedfs-volumeObject storage — volume server
semanticSemantic processing service
supersetSuperset web application
superset-workerSuperset Celery worker
uiLakehousecat web UI
valkey-primaryValkey 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 / ReasonMeaningWhat to do
True / DigestsPinnedAll core images are pinned to released digestsNothing
False / TrustMapUnavailableNo verified digest list received yetUsually resolves on its own once the digest list for the release is available; if it persists, contact support
False / TrustMapInvalidA digest list arrived but its signature did not verifyContact support
False / DigestMissingThe list has no entry for an image the instance uses — the message names the first gapCheck that the instance pulls from the official registry or a mirror of it (see below)
False / DigestMismatchA pod cannot pull its pinned image — the message names the podThe 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.

Mirroring images into your own registry

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 /metrics endpoint 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 components independently

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:

  1. TUI Installer - Fastest path: automated local evaluation setup
  2. Manual Setup - Advanced local evaluation with full configuration control
  3. Kubernetes Cluster - Deploy to AWS EKS, Google GKE, Azure AKS, or on-premise

Support​