Grafana TankaFlexible, reusable and concise configuration for Kubernetes
Edit page
OverviewRefresher on deployingUsing JsonnetParameterizingAbstractionKubernetes libraryk8s-libsonnetInstallationUsing itFull exampleEnvironments
Writing Jsonnet
Advanced features
Frequently asked questionsKnown issues

Kubernetes library

The last section has shown that using a library for creating Kubernetes objects can drastically simplify the code you need to write. However, there is a huge amount of different kinds of objects and the Kubernetes API is evolving (and thus changing) quite rapidly.

Writing and maintaining such a library could be a full-time job on it's own. Luckily, it is possible to generate such a library from the Kubernetes OpenAPI specification! Even better, it has already been done for you.


The library is called k8s-libsonnet (replacing the discontinued ksonnet-lib), currently available at

Note: The ksonnet project has been abandoned, the library is not maintained anymore. However, the community backed by Grafana Labs has picked up on this with the k8s-libsonnet library.

As k8s-libsonnet has broken compatibility in a few places with ksonnet-lib (for good reason), we have instrumented the widely used ksonnet-util library with a compatibility layer to improve the developer and user experience:

If you do not have any strong reasons against it, just adopt the wrapper as well, it will ease your work. Many of the original ksonnet-util enhancements have already made their way into k8s-libsonnet.

The docs for k8s-libsonnet library can be found here:


Like every other external library, k8s-libsonnet can be installed using jsonnet-bundler. However, Tanka already did this for you during project creation (tk init):

$ tk init
  └─ jb install

This created the following structure in /vendor:

│   ├── grafana
│   │   └── jsonnet-libs
│   │       └── ksonnet-util
│   │           ├── ...
│   │           └── kausal.libsonnet # Grafana's wrapper
│   └── jsonnet-libs
│       └── k8s-libsonnet
│           └── 1.21
│               ├── ...
│               └── main.libsonnet   # k8s-libsonnet entrypoint
├── 1.21 ->
└── ksonnet-util ->

Info: The vendor/ is the location for external libraries, while lib/ can be used for your own ones. Check import paths for more information.


Because of how jb works, the library can be imported as Most external libraries (including our wrapper) expect it as a simple k.libsonnet (without the package prefix).

To support both, Tanka automatically created an alias file for you: /lib/k.libsonnet that just imports the actual library, exposing it under this alternative name as well.

More information: This works, because import behaves like copy-pasting. So the contents of k8s-libsonnet/1.21 are "copied" into our new file, making them behave exactly the same.

Using it

First we need to import it in main.jsonnet:

- local k = import "kubernetes.libsonnet";
+ local k = import "";
  local grafana = import "grafana.jsonnet";
  local prometheus = import "prometheus.jsonnet";
  { /* ... */ }

Note: ksonnet-util imports literal k.libsonnet, so aliasing is a must here. This works, because /lib and /vendor are automatically searched for libraries, and k.libsonnet can be found in /lib due to aforementioned aliasing.

Now that we have installed the correct version, let's use it in /environments/default/grafana.jsonnet instead of our own helper:

local k = import "";

  // use locals to extract the parts we need
  local deploy = k.apps.v1.deployment,
  local container = k.core.v1.container,
  local port = k.core.v1.containerPort,
  local service = k.core.v1.service,
  // defining the objects:
  grafana: {
    // deployment constructor: name, replicas, containers
    deployment:$, replicas=1, containers=[
      // container constructor$, "grafana/grafana")
      + container.withPorts( // add ports to the container
          ["ui", $._config.grafana.port)] // port constructor

    // instead of using a service constructor, our wrapper provides

Full example

Now that creating the individual objects does not take more than 5 lines, we can merge it all back into a single file (main.jsonnet) and take a look at the whole picture:

local k = import "";

  _config:: {
    grafana: {
      port: 3000,
      name: "grafana",
    prometheus: {
      port: 9090,
      name: "prometheus"

  local deployment = k.apps.v1.deployment,
  local container = k.core.v1.container,
  local port = k.core.v1.containerPort,
  local service = k.core.v1.service,

  prometheus: {

That's a pretty big improvement, considering how verbose and error-prone it was before!