The Kubernetes control plane for platform teams. EntKube is a self-hosted, multi-tenant developer portal that turns a fleet of Kubernetes clusters into self-service infrastructure — register clusters, deploy a curated catalog of production services in one click, manage secrets, ship applications, and observe everything through a built-in telemetry stack, all from a single pane of glass.
Most teams stitch together a dozen tools to run shared infrastructure: GitOps for delivery, a secrets manager, a monitoring stack, an incident tool, a status page, and a pile of Helm charts. EntKube brings all of it into one opinionated portal built on the Kubernetes ecosystem you already trust — so a small team can operate infrastructure for many customers without the operational grind.
- Self-hosted — your clusters, your data, your registry. Nothing leaves your infrastructure.
- Multi-tenant to the core — isolated tenants, customers, environments and groups with role-based access at every level.
- Batteries included — logs, traces, metrics, RUM, alerting, secrets and a service catalog ship in the box.
Model your organisation as tenants → customers → environments, with user groups and role-based access control throughout. Teams get self-service access to exactly the infrastructure they own, and nothing they don't.
Register any Kubernetes cluster and monitor nodes, health, capacity and workloads across the whole fleet from one control plane. Cluster kubeconfigs are stored encrypted in the vault, never in plaintext on disk.
Pick a component, fill in a short form, and EntKube installs and wires up the Helm release — ingress, TLS, storage and credentials included. 30+ curated components across:
| Category | Components |
|---|---|
| Ingress | Traefik (Gateway API), Istio (control plane + external/internal gateways), Gateway API CRDs |
| Certificates | cert-manager, Let's Encrypt ClusterIssuer (HTTP-01 + DNS-01: Azure DNS, Cloudflare, Route53, Google Cloud DNS) |
| Monitoring | Kube Prometheus Stack, Grafana Loki, Mimir, Tempo, Grafana Alloy, EntKube Telemetry Collector (OTLP), eBPF trace auto-instrumentation |
| Storage | MinIO, MinIO Operator |
| Databases | CloudNativePG (PostgreSQL), MongoDB Community Operator |
| Identity | Keycloak (with theme editor) |
| Registry | Harbor |
| Messaging | RabbitMQ (Cluster + Topology operators), Strimzi Kafka |
| Cache | Redis Operator |
| Security | Kyverno |
| Autoscaling | KEDA |
| Networking / VPN | Tailscale, Headscale, WireGuard (wg-easy), StrongSwan IPsec, Submariner |
Assemble components and services into ordered Cluster Blueprints to bootstrap a brand-new cluster end to end, with resume-on-failure.
Ship applications per environment with plain YAML deployments and apply-or-recreate sync (including an optional PVC-delete on recreate). The deployment import wizard scans a live cluster and adopts existing workloads into managed apps — moving secrets into the vault and detecting databases along the way.
A built-in vault encrypts secrets at rest and scopes them per app and environment. Manage plain secrets, TLS certificates, OAuth/OIDC client secrets, and cluster kubeconfigs — all with expiry tracking and safe, env-scoped sync to clusters.
Logs, distributed traces / APM, metrics, real-user monitoring (RUM), dashboards and alerting ship in the box — powered by a self-built S3 + Lucene.NET telemetry segment engine plus Prometheus for metrics. Clusters send data over OTLP to a per-cluster, bearer-authenticated ingest endpoint. Grafana/Loki remain available as optional BYO backends.
Enforce Kyverno admission policies per app and environment, and author a least-privilege connectivity model that generates NetworkPolicies (L3/L4) and Istio ServiceEntry / AuthorizationPolicy (L7) — analysed and previewed before they are ever applied.
The operations layer is built in: alert rules and routing, notification channels, on-call schedules, incident management, SLA targets, and maintenance windows.
Give your customers a self-service view scoped strictly to their data: status dashboards, SLA reports, observability, maintenance notices and incident history.
Export the full platform state and restore it on new hardware — migrate servers or recover from disaster without hand-rebuilding your configuration.
| Layer | Technology |
|---|---|
| Frontend | Blazor (.NET 10) — Server + WebAssembly |
| Backend | ASP.NET Core 10 |
| Auth | ASP.NET Core Identity with passkey support |
| Database | SQLite (dev) / PostgreSQL (prod) |
| ORM | Entity Framework Core 10 |
| Telemetry | Self-built S3 + Lucene.NET segment engine (logs/traces) · Prometheus (metrics) |
| Reverse proxy | Caddy (automatic Let's Encrypt TLS) |
git clone https://github.com/Entit-AB/EntKube.git
cd EntKube
dotnet run --project src/EntKube.WebThe app launches at https://localhost:7136 (see src/EntKube.Web/Properties/launchSettings.json). In development it uses a local SQLite database, so no external services are required to start. Registration is open by default — the first account you create can be promoted to Admin (see Seed__AdminEmail below).
EntKube/
├── EntKube.slnx # Solution file
├── docker-compose.yml # Production stack (Caddy + Postgres + app, optional MinIO)
├── .env.example # Copy to .env and fill in
├── src/
│ ├── EntKube.Web/ # Server-side Blazor host, Identity, data layer, services
│ └── EntKube.Web.Client/ # WebAssembly client project
└── tests/
└── EntKube.Web.Tests/ # Test project
entkube-installer is a small desktop app that installs the management plane onto a server over SSH.
Point it at the server, answer the same questions the command-line installer asks, and watch it
write the configuration, pull the images and start the stack. It can also install the client-side
tools (CLI, MCP server, egress agent, Terraform provider) onto your own machine afterwards.
scripts/release.sh gui --rid osx-arm64 # or linux-x64, win-x64, osx-x64
open "artifacts/gui/Release/osx-arm64/EntKube Installer.app" # macOS
./artifacts/gui/Release/linux-x64/entkube-installer # Linux
artifacts\gui\Release\win-x64\entkube-installer.exe # WindowsBuilding needs the .NET 10 SDK, and Go for the bundled Terraform provider. Move the whole output
directory if you copy it elsewhere — the tools/ folder beside the app holds the client binaries.
It performs exactly the install the command-line installer performs — the same code, pointed at an SSH connection instead of the local shell — so a server built by either is the same server.
See docs/installing.md.
entkube-install is a single self-contained binary that stands up the management plane on a server:
it checks the host, asks a handful of questions, writes docker-compose.yml / Caddyfile / .env,
pulls the images and starts everything.
scripts/release.sh installer --rid linux-x64
scp artifacts/installer/Release/linux-x64/entkube-install server:/tmp/
ssh server 'sudo /tmp/entkube-install --directory /opt/entkube'It is a terminal wizard, so it works over SSH — which is how servers are usually reached. It is also
safe to re-run: an existing .env is read first, every answer defaults to what is already there, and
the vault root key and database password are reused rather than regenerated. Re-running is the
supported way to change a setting or move to a newer image.
Scripted installs use --non-interactive, which asks nothing and takes every answer from a flag:
entkube-install --non-interactive --directory /opt/entkube \
--domain entkube.example.com --acme-email ops@example.com --seed-admin ops@example.comSee docs/installing.md for the full flag list, the scripted form, and what it checks before writing anything. Use this one when you are already on the server, or scripting.
The installer is not required; the sections below are the reference for doing it yourself.
The published image lives at entit.azurecr.io/entkube. entit.azurecr.io is a public Azure Container Registry — pulls require no authentication and no docker login. Docker Compose pulls it automatically.
Multi-architecture: the published image is built for
linux/amd64andlinux/arm64, so it runs on Intel/AMD servers and on Graviton, Ampere or Hetzner ARM alike. If you build your own, build both — the--pushflag is required because a multi-platform image cannot be loaded into the local Docker daemon.
# One-time setup
docker buildx create --use --name multiarch
# Build both architectures and push directly to the registry
docker buildx build --platform linux/amd64,linux/arm64 --provenance=false \
-t entit.azurecr.io/entkube:latest \
--push .
# Tag a versioned release
docker buildx build --platform linux/amd64,linux/arm64 --provenance=false \
-t entit.azurecr.io/entkube:latest \
-t entit.azurecr.io/entkube:1.0.0 \
--push .--provenance=false matters: buildx otherwise adds a manifest entry with platform unknown/unknown, which several container runtimes report as a platform mismatch rather than ignoring.
Replace entit.azurecr.io/entkube with your own registry and image path if you are not pushing to the public one.
scripts/release.sh builds every EntKube artifact and is the shorter form of the buildx commands
above:
scripts/release.sh web --push # both architectures, as CI builds it
scripts/release.sh web --platforms linux/amd64 --push # one, if you only need itSee docs/releasing.md.
Copy .env.example to .env and fill in the required values, then start the stack:
docker compose up -dThe app will be available at https://<DOMAIN> (Caddy terminates TLS on ports 80/443).
Data safety —
docker compose downdoes not delete the database volume. Onlydocker compose down -vremoves volumes.
Configuration comes from environment variables (12-factor style). In Compose, host-level values live in .env and are mapped onto the app's settings inside docker-compose.yml. Nested settings use the ASP.NET Core double-underscore convention (Section__Key).
| Variable | Description |
|---|---|
DOMAIN |
Public domain pointing at the server (e.g. entkube.example.com). Caddy obtains a Let's Encrypt certificate for it on first start. |
ACME_EMAIL |
Email for the Let's Encrypt account and expiry notices. |
POSTGRES_PASSWORD |
Password for the bundled Postgres database. Used by both the postgres service and the app connection string. |
VAULT__ROOTKEY |
32-byte base64 key for secret encryption. Generate with openssl rand -base64 32. Losing this makes existing vault secrets unrecoverable — back it up. |
| Setting | Default | Description |
|---|---|---|
DatabaseProvider |
Postgres (compose) / Sqlite (dev) |
Which provider EF Core uses. |
ConnectionStrings__DefaultConnection |
derived from POSTGRES_PASSWORD |
Full connection string. In Compose it is built for you from the Postgres service; override to point at an external database. |
| Setting | Default | Description |
|---|---|---|
Auth__AllowRegistration |
true |
Whether new users can self-register. Set to false once your admin account exists to lock down sign-ups. |
Seed__AdminEmail |
(unset) | If set, this user is granted the Admin role on every startup (no-op if already admin). Useful for the first admin and for recovering access. |
| Setting | Default | Description |
|---|---|---|
DataProtection__KeyPath |
/app/Data/keys |
Where the DataProtection key ring is persisted. Keep it on a durable volume so auth/antiforgery cookies survive restarts (otherwise every deploy logs everyone out). |
ASPNETCORE_FORWARDEDHEADERS_ENABLED |
true (compose) |
Trust X-Forwarded-* headers from Caddy. Required for Blazor Server SignalR (WebSockets) behind the proxy. |
The native telemetry engine stores logs and traces as sealed segments in object storage (metrics go to Prometheus). See docker-compose.yml for the full annotated block.
| Setting | Default | Description |
|---|---|---|
Telemetry__PublicIngestUrl |
https://${DOMAIN} |
Base URL clusters use to reach this server; the app appends /ingest/otlp. Must be externally reachable from your clusters. |
Telemetry__RetentionDays |
14 |
Days of telemetry to retain before sealed segments older than this are dropped. (TELEMETRY_RETENTION_DAYS in .env.) |
Telemetry__DataPath |
/app/Data/telemetry |
Local state: active index + sealed-segment cache. Keep on a durable volume. |
Object storage for sealed segments (priority: StorageLink → flat config → local disk):
| Setting | Description |
|---|---|
Telemetry__StorageLinkId |
Primary/production. GUID of a registered StorageLink (AWS S3 / Cleura S3 / MinIO) whose credentials live in the vault. |
Telemetry__ObjectStorage__Endpoint / __Bucket / __Region / __AccessKey / __SecretKey / __ForcePathStyle |
Simple/dev alternative. Flat S3/MinIO config used when no StorageLink is set. |
If neither is configured, sealed segments live on local disk under Telemetry__DataPath (single-node only).
The repo bundles an optional MinIO for local/dev object storage, started with the objectstore profile:
docker compose --profile objectstore up -d| Variable | Default | Description |
|---|---|---|
MINIO_ROOT_USER |
entkube |
Root user for the bundled MinIO. |
MINIO_ROOT_PASSWORD |
changeme-minio |
Root password for the bundled MinIO. |
TELEMETRY_BUCKET |
entkube-telemetry |
Bucket created for sealed segments. |
Configure outbound email to deliver alert notifications and account emails.
| Setting | Description |
|---|---|
Smtp__Host |
SMTP server hostname. |
Smtp__Username |
SMTP username. |
Smtp__Password |
SMTP password. |
Smtp__FromAddress |
From address for outgoing mail (e.g. alerts@entkube.io). |
Caddy is the reverse proxy and handles TLS automatically. On first start it obtains a Let's Encrypt certificate for DOMAIN and renews it before expiry. Ports 80 and 443 must be open and your DNS must point at the server before starting the stack.
See LICENSE.