# Hubble UI: a service map for Cilium observability

> Hubble UI is an open source user interface for Cilium Hubble, and it exists to answer a question that kubectl cannot: what does this service actually talk to. It discovers the dependency graph of a Kubernetes cluster from observed network flows, at layer three, layer four and even layer seven, and renders it as a service map you can filter. It is not a standalone product, it is installed as part of Hubble.

**cilium/hubble-ui** — Observability & Troubleshooting for Kubernetes Services

- Repository: https://github.com/cilium/hubble-ui
- Website: https://www.cilium.io
- Stars: 543 · Forks: 95
- Language: TypeScript
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/cilium-hubble-ui

## The dependency graph that a pod list cannot show

Hubble UI is an open-source user interface for Cilium Hubble, and the repository description states its job in four words: observability and troubleshooting for Kubernetes services.

The README's own explanation of the problem is the clearest statement of the purpose. Troubleshooting microservices application connectivity is a challenging task, and simply looking at kubectl get pods does not indicate dependencies between each service, external APIs or databases.

That is the specific gap. A pod list tells you what is running and whether it is ready. It does not tell you that the checkout service depends on a payments API that depends on a database in another namespace, and it certainly does not tell you which of those dependencies is failing at three in the morning. Kubernetes manifests declare intent, and intent is not what is happening on the network.

Hubble UI enables zero-effort automatic discovery of the service dependency graph for Kubernetes clusters at L3 and L4 and even L7, allowing user-friendly visualization and filtering of those dataflows as a Service Map. The word automatic is doing real work in that sentence: the graph is derived from the traffic the cluster is actually carrying rather than from configuration somebody wrote.

And L7 is the part that distinguishes it from a generic dependency view. Cilium sits on eBPF, so it can see the HTTP calls rather than only the connections, which is what makes the map specific enough to act on rather than merely interesting.

The project is Apache-2.0 licensed, the primary language is TypeScript, and the topics cover cilium, ebpf, kubernetes, observability, reactjs, security and troubleshooting. The last push to the repository was on 2026-10-22, and the releases are v0.13.4 in February 2026, v0.13.5 in April and v0.13.6 on 2026-09-08.

## You install it as part of Hubble, not on its own

The installation section is one line, and it is the one fact about this project that most affects how you adopt it.

Hubble UI is installed as part of Hubble, and the README points at the Hubble Getting Started Guide for the instructions.

That is not a shortcut in the documentation, it is the architecture. The UI has no data source of its own. Every node in the service map is a flow that Hubble observed, and Hubble only observes if Cilium is deployed with the right options. So the install path runs through the Cilium and Hubble deployment, and the UI appears as part of that stack.

The consequence for planning is that you are not choosing a dashboard here, you are choosing to add a visualisation layer to an observability system you may already run. If your cluster has Cilium and Hubble today, adding the UI is a small step. If it does not, the cost is deploying Cilium with Hubble and the flow retention configuration that goes with it, and the UI is a small part of that decision.

It also means the versions are coupled. The UI talks to Hubble's API, and the API is a protobuf service, so a UI from a different Hubble generation is not going to be an interesting compatibility problem, it is a broken one.

The community links are in the README too, pointing at the Cilium community page, which is where the operational knowledge lives for the parts the documentation does not cover.

## The front end: React, Blueprint, MobX and D3

The dependency list in the package manifest describes what kind of application this is, and the choices are worth reading because they say what the interface does.

The base is React 19.2.8 with react-dom, and routing comes from react-router-dom 7.18.3. The component library is Blueprint, the @blueprintjs/core package at 6.18.0, with the icons and the select components from the same family. Blueprint is a data-dense interface toolkit, which is the right fit for an operator tool.

State is handled with MobX, at 7.0.3 with mobx-react 10.0.2, rather than with a reducer-based store. For an interface that is reacting to a stream of flow data arriving over the wire, observable state is a natural fit, and it explains the presence of the other interactive libraries: @use-gesture/react 10.3.1 for pan and zoom, @react-spring/web 10.1.2 for animation, and @react-hook/resize-observer for layout.

The graph drawing is D3, at 7.9.0, which is the usual choice for a force directed dependency map.

The data transport is the part that is specific to Cilium. @improbable-eng/grpc-web at 0.15.0 and google-protobuf 4.0.2 mean the browser talks to Hubble over gRPC-web, the HTTP and JSON friendly variant of gRPC, so a plain front end can subscribe to a stream of flow records without a proxy that speaks native gRPC. The manifest also pins a protocVersion of 36.1 in the package configuration, which is the compiler used to generate the client stubs from Hubble's protobuf definitions.

Around those are the ordinary supporting libraries: date-fns for timestamps, lodash, classnames, pluralize, prop-types, history and query-string for URL state, core-js for polyfills and the Babel runtime. React 19 with Blueprint 6 is a modern combination, and the versions in the manifest are pinned exactly rather than with ranges, which is a sign of a project that rebuilds its image from a lockfile.

## The build, the linting and the two test runners

The npm scripts describe a build with several distinct checks, and the split between them is worth noting.

```json
    "build": "run-s check:types build:webpack",
    "build:webpack": "NODE_ENV=production webpack",
    "check:types": "tsc --noEmit",
```

So the production build type checks first and then runs webpack, and the type check is a separate step rather than something the bundler does.

The linting is split by language. lint:ts runs eslint over the TypeScript, TSX, JavaScript and JSX files, using eslint.config.mjs for the flat configuration, and lint:scss runs stylelint over the stylesheets with .stylelintrc.js. The lint:fix variants run the same with the fix flags, and lint-staged runs the fix variants on the files a commit touches, using the patterns that exclude the generated proto directory.

There are two test runners. The unit tests are jest, with jest.config.mjs, and there is a separate cypress.config.ts for end to end tests, with tsconfig.test.json beside the main tsconfig.json.

Two other scripts are worth knowing about. The postinstall step runs patch-package and then bash ./scripts/post-install.sh, and the patches/ directory at the root is where the patched dependencies live, which is the standard way to carry a fix to a dependency that has not been released upstream. And upgrade-deps runs ncu, the npm-check-updates tool, in interactive mode grouped by dependency type, so an upgrade session is a guided one rather than a bulk edit.

The development loop has two halves: watch:client runs webpack in development mode with progress output, and watch:server serves the built output with servor on port 8080, and the watch script runs both in parallel with run-p. The development and production environments have their own .env.development and .env.production files.

## The container image and how a release happens

The Dockerfile is a two stage build that produces a small runtime image, and the details are the kind a reader can learn from.

The build stage is pinned to a specific Node image on Alpine by digest, with make, git and bash added, and the package manifest, the lockfile, the .npmrc, the scripts directory and the patches directory are copied in first so that the dependency install can be cached separately from the source. The install step passes the target architecture, and then the postinstall script runs.

The runtime stage is an unprivileged nginx image, also pinned by digest, running as user 101 after an apk upgrade, and the only thing copied into it is the built output from the server's public directory.

That last detail is the interesting one. The image ships a web server and a static bundle, not a Node runtime, so the running container has no JavaScript engine in it at all. The size and the attack surface of the thing that runs in your cluster are both smaller for it.

The Makefile handles the multi-architecture build. With ARCH set it configures a Docker Buildx builder for linux/arm64 and linux/amd64, and with the special value multi it overrides the platform list for a single platform. The registry defaults to quay.io and the development account defaults to cilium.

The release procedure is described in the README in two sentences. You push a tag into GitHub and ping a maintainer to accept the GitHub action run, which pushes the built images into the official repositories.

That is an unusual release model, and it is a deliberate one: the tag is the source of truth, but a human approves the run that publishes the image, so nothing reaches the registry without someone looking at it.

## Where Hubble UI is the wrong tool

There are clear cases where this interface is not what you need, and most of them come from what the map actually is.

The map is derived from observed traffic, not from declared intent. A service that is deployed and configured to call a database but never actually calls it in this window will not appear as a dependency. That is the correct behaviour for a troubleshooting tool and the wrong behaviour for a documentation tool, and it means the map reflects the cluster as it is being used rather than as it was designed.

It also means the resolution of the map depends on the Hubble configuration. L3 and L4 flows are available with the usual deployment, and L7 flows need Hubble configured to observe them. A map that looks sparse is not necessarily evidence of a sparse application, and the difference between a service map that is empty and a service map that is under-configured is worth checking before you go looking for a dependency problem.

The second limit is that a service map is not a trace viewer. When a single request fails, the question is which hop failed and what the error was, and that is a different tool with a different data model. The graph tells you where to look.

And the obvious one: without Cilium there is no Hubble, and without Hubble there is no data for this interface to draw. For a cluster on a different networking stack, the equivalent tooling would come from that stack's own instrumentation.

The alternative approach for the same question is the Hubble command line interface, which answers the same queries about the same flows without a browser. The difference in approach is that the CLI is for one investigation at a time and gives you exact records, while the interface is for orientation, showing you the shape of the traffic in a cluster and letting you filter it down until the interesting edge appears.

## Conclusion

Hubble UI suits an operator of a Cilium cluster who needs to know which service talks to which, which external API a workload depends on, and where a failure is coming from, and who wants that as a live graph rather than a query result. It does not suit a cluster that does not run Cilium, because the graph is derived from Hubble's flow data rather than from the cluster's declared configuration, and it does not suit somebody looking for a distributed trace viewer, since a flow map and a trace timeline answer different questions. Check three things before you deploy it: that Cilium and Hubble are already running with the flow data you need, that the L7 visibility you care about is actually enabled in the Hubble configuration rather than assumed from the UI, and that the image you pull matches the Cilium version you run, since the release process ties the built images to a tag and a maintainer accepting the action run. The last push to the repository was on 2026-10-22 and the newest release is v0.13.6.

## FAQ

### What is Hubble UI?

It is an open-source user interface for Cilium Hubble that provides observability and troubleshooting for Kubernetes services, enabling automatic discovery of the service dependency graph at L3, L4 and even L7 and rendering it as a filterable Service Map. It is Apache-2.0 licensed and written in TypeScript.

### How do I install Hubble UI?

It is installed as part of Hubble rather than on its own, and the README points at the Hubble Getting Started Guide for the deployment instructions. The UI depends on Hubble's flow data, so Cilium and Hubble have to be deployed first.

### Why does kubectl get pods not answer the same question?

The README says that simply looking at kubectl get pods does not indicate dependencies between each service, external APIs or databases. Hubble UI derives that graph from the traffic the cluster is actually carrying, which is why it can show L7 level dependencies.

### How is a Hubble UI release published?

You push a tag into GitHub and ping a maintainer to accept the GitHub action run, which pushes the built images into the official repositories. The recent releases are v0.13.4, v0.13.5 and v0.13.6, the last published on 2026-09-08.

### What does the Hubble UI container image contain?

The build stage compiles the front end with a pinned Node image on Alpine, and the runtime stage copies only the built output into an unprivileged nginx image running as user 101, so no JavaScript runtime is shipped in the container that runs in the cluster.

## Sources

- [cilium/hubble-ui on GitHub](https://github.com/cilium/hubble-ui)
- [License: Apache-2.0](https://github.com/cilium/hubble-ui/blob/master/LICENSE)
- [Project website](https://www.cilium.io)
- [README](https://github.com/cilium/hubble-ui/blob/master/README.md)
- [Releases](https://github.com/cilium/hubble-ui/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cilium-hubble-ui
