diff --git a/.travis.yml b/.travis.yml
index 412ab53f1..69b2f4002 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,6 +12,7 @@ jobs:
   # Check generated contents are up to date and code is formatted.
   - stage: Sanity check and unit tests
     script: ./scripts/check-make-generate.sh
+  - script: cd contrib/kube-prometheus && make test
   # Build Prometheus config reloader
   - script: cd contrib/prometheus-config-reloader && make build
   # Ensure vendor folder matches vendor.json
diff --git a/Makefile b/Makefile
index aaf13155b..e1210b125 100644
--- a/Makefile
+++ b/Makefile
@@ -66,7 +66,7 @@ po-docgen:
 	@go install github.com/coreos/prometheus-operator/cmd/po-docgen
 
 docs: embedmd po-docgen
-	$(GOPATH)/bin/embedmd -w `find Documentation contrib/kube-prometheus/README.md -name "*.md"`
+	$(GOPATH)/bin/embedmd -w `find Documentation contrib/kube-prometheus/ -name "*.md"`
 	$(GOPATH)/bin/po-docgen api pkg/client/monitoring/v1/types.go > Documentation/api.md
 	$(GOPATH)/bin/po-docgen compatibility > Documentation/compatibility.md
 
diff --git a/contrib/kube-prometheus/Makefile b/contrib/kube-prometheus/Makefile
index a7903cf83..9fc6113e4 100644
--- a/contrib/kube-prometheus/Makefile
+++ b/contrib/kube-prometheus/Makefile
@@ -14,4 +14,12 @@ generate-raw:
 	jb install
 	./build.sh
 
-.PHONY: image generate crdtojsonnet generate-raw
+test: image
+	@echo ">> Compiling assets and generating Kubernetes manifests"
+	docker run --rm -u=$(shell id -u $(USER)):$(shell id -g $(USER)) -v $(shell dirname $(dir $(abspath $(dir $$PWD)))):/go/src/github.com/coreos/prometheus-operator/ --workdir /go/src/github.com/coreos/prometheus-operator/contrib/kube-prometheus po-jsonnet make test-raw
+
+test-raw: crdtojsonnet
+	jb install
+	./test.sh
+
+.PHONY: image generate crdtojsonnet generate-raw test
diff --git a/contrib/kube-prometheus/README.md b/contrib/kube-prometheus/README.md
index 663f78316..846239b85 100644
--- a/contrib/kube-prometheus/README.md
+++ b/contrib/kube-prometheus/README.md
@@ -87,7 +87,7 @@ set -x
 set -o pipefail
 
                                                # optional, but we would like to generate yaml, not json
-jsonnet -J vendor -m manifests example.jsonnet | xargs -I{} sh -c 'cat $1 | gojsontoyaml > $1.yaml; rm -f $1' -- {}
+jsonnet -J vendor -m manifests ${1-example.jsonnet} | xargs -I{} sh -c 'cat $1 | gojsontoyaml > $1.yaml; rm -f $1' -- {}
 
 ```
 
@@ -145,7 +145,7 @@ A common example is that not all Kubernetes clusters are created exactly the sam
 
 kubeadm:
 
-[embedmd]:# (examples/kubeadm.jsonnet)
+[embedmd]:# (examples/jsonnet-snippets/kubeadm.jsonnet)
 ```jsonnet
 (import "kube-prometheus/kube-prometheus.libsonnet") +
 (import "kube-prometheus/kube-prometheus-kubeadm.libsonnet")
@@ -153,7 +153,7 @@ kubeadm:
 
 bootkube:
 
-[embedmd]:# (examples/bootkube.jsonnet)
+[embedmd]:# (examples/jsonnet-snippets/bootkube.jsonnet)
 ```jsonnet
 (import "kube-prometheus/kube-prometheus.libsonnet") +
 (import "kube-prometheus/kube-prometheus-bootkube.libsonnet")
@@ -161,7 +161,7 @@ bootkube:
 
 Another mixin that may be useful for exploring the stack is to expose the UIs of Prometheus, Alertmanager and Grafana on NodePorts:
 
-[embedmd]:# (examples/node-ports.jsonnet)
+[embedmd]:# (examples/jsonnet-snippets/node-ports.jsonnet)
 ```jsonnet
 (import "kube-prometheus/kube-prometheus.libsonnet") +
 (import "kube-prometheus/kube-prometheus-node-ports.libsonnet")
@@ -186,17 +186,21 @@ Standard Kubernetes manifests are all written using [ksonnet-lib](https://github
 
 [embedmd]:# (examples/ksonnet-example.jsonnet)
 ```jsonnet
-local k = import "ksonnet/ksonnet.beta.3/k.libsonnet";
+local k = import 'ksonnet/ksonnet.beta.3/k.libsonnet';
 local daemonset = k.apps.v1beta2.daemonSet;
 
-((import "kube-prometheus/kube-prometheus.libsonnet") + {
-	nodeExporter+: {
-		daemonset+:
-            daemonset.mixin.metadata.withNamespace("my-custom-namespace")
-    }
-}).nodeExporter.daemonset
+((import 'kube-prometheus/kube-prometheus.libsonnet') + {
+   nodeExporter+: {
+     daemonset+:
+       daemonset.mixin.metadata.withNamespace('my-custom-namespace'),
+   },
+ }).nodeExporter.daemonset
 ```
 
+### Customizing Prometheus alerting/recording rules and Grafana dashboards
+
+See [developing alerts and dashboards](developing-alerts-and-dashboards.md) guide.
+
 ## Example
 
 To use an easy to reproduce example, let's take the minikube setup as demonstrated in [prerequisites](#Prerequisites). It is a kubeadm cluster (as we use the kubeadm bootstrapper) and because we would like easy access to our Prometheus, Alertmanager and Grafana UI we want the services to be exposed as NodePort type services:
diff --git a/contrib/kube-prometheus/build.sh b/contrib/kube-prometheus/build.sh
index 9c22672ee..d8af66818 100755
--- a/contrib/kube-prometheus/build.sh
+++ b/contrib/kube-prometheus/build.sh
@@ -5,5 +5,5 @@ set -x
 set -o pipefail
 
                                                # optional, but we would like to generate yaml, not json
-jsonnet -J vendor -m manifests example.jsonnet | xargs -I{} sh -c 'cat $1 | gojsontoyaml > $1.yaml; rm -f $1' -- {}
+jsonnet -J vendor -m manifests ${1-example.jsonnet} | xargs -I{} sh -c 'cat $1 | gojsontoyaml > $1.yaml; rm -f $1' -- {}
 
diff --git a/contrib/kube-prometheus/docs/developing-alerts-and-dashboards.md b/contrib/kube-prometheus/docs/developing-alerts-and-dashboards.md
deleted file mode 100644
index ed3a2a060..000000000
--- a/contrib/kube-prometheus/docs/developing-alerts-and-dashboards.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Developing Prometheus Rules and Grafana Dashboards
-
-`kube-prometheus` ships with a set of default [Prometheus rules](https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/) and [Grafana](http://grafana.com/) dashboards. At some point one might like to extend them, the purpose of this document is to explain how to do this.
-
-For both the Prometheus rules and the Grafana dashboards there are Kubernetes `ConfigMap`s, that are generated from content in the `assets/` directory.
-
-The source of truth for the alerts and dashboards are the files in the `assets/` directory. The respective files have to be changed there and then the `make generate` make target is executed to re-generate the Kubernetes manifests.
-
-Note: `make generate` should be executed from kube-prometheus base directory.
-
-## Prometheus Rules
-
-The `ConfigMap` that is generated and holds the Prometheus rule files can be found in `manifests/prometheus/prometheus-k8s-rules.yaml`.
-
-It is generated from all the `*.rules.yaml` files in the `assets/prometheus/rules/` directory.
-
-To extend the rules simply add a new `.rules.yaml` file into the `assets/prometheus/rules/` directory and re-generate the manifests. To modify the existing rules, simply edit the respective `.rules.yaml` file and re-generate the manifest.
-
-Then the generated manifest can be applied against a Kubernetes cluster.
-
-## Dashboards
-
-The generated `ConfigMap`s holding the Grafana dashboard definitions can be found in `manifests/grafana/grafana-dashboards.yaml`.
-
-The dashboards themselves get generated from Python scripts: assets/grafana/\*.dashboard.py.
-These scripts are loaded by the [grafanalib](https://github.com/aknuds1/grafanalib)
-Grafana dashboard generator, which turns them into dashboards.
-
-Bear in mind that we are for now using a fork of grafanalib as we needed to make extensive
-changes to it, in order to be able to generate our dashboards. We are hoping to be able to
-consolidate our version with the original.
-
-After changing grafanalib scripts in assets/grafana, or adding your own, you'll have to run
-`make generate` in the kube-prometheus root directory in order to re-generate the dashboards
-manifest. You can deploy the latter with kubectl similar to the following:
-
-```
-kubectl -n monitoring apply -f manifests/grafana/grafana-dashboards.yaml
-```
-
-This should cause Grafana to re-load its dashboards automatically.
diff --git a/contrib/kube-prometheus/docs/developing-prometheus-rules-and-grafana-dashboards.md b/contrib/kube-prometheus/docs/developing-prometheus-rules-and-grafana-dashboards.md
new file mode 100644
index 000000000..edd7c6568
--- /dev/null
+++ b/contrib/kube-prometheus/docs/developing-prometheus-rules-and-grafana-dashboards.md
@@ -0,0 +1,217 @@
+# Developing Prometheus Rules and Grafana Dashboards
+
+`kube-prometheus` ships with a set of default [Prometheus rules](https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/) and [Grafana](http://grafana.com/) dashboards. At some point one might like to extend them, the purpose of this document is to explain how to do this.
+
+All manifests of kube-prometheus are generated using [jsonnet](https://jsonnet.org/) and Prometheus rules and Grafana dashboards in specific follow the [Prometheus Monitoring Mixins proposal](https://docs.google.com/document/d/1A9xvzwqnFVSOZ5fD3blKODXfsat5fg6ZhnKu9LK3lB4/).
+
+For both the Prometheus rules and the Grafana dashboards Kubernetes `ConfigMap`s are generated within kube-prometheus. In order to add additional rules and dashboards simply merge them onto the existing json objects. This document illustrates examples for rules as well as dashboards.
+
+As a basis, all examples in this guide are based on the base example of the kube-prometheus [readme](../README.md):
+
+[embedmd]:# (../example.jsonnet)
+```jsonnet
+local kp = (import 'kube-prometheus/kube-prometheus.libsonnet') + {
+  _config+:: {
+    namespace: 'monitoring',
+  },
+};
+
+{ ['00namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } +
+{ ['0prometheus-operator-' + name]: kp.prometheusOperator[name] for name in std.objectFields(kp.prometheusOperator) } +
+{ ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } +
+{ ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } +
+{ ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } +
+{ ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } +
+{ ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) }
+```
+
+## Prometheus rules
+
+### Alerting rules
+
+According to the [Prometheus Monitoring Mixins proposal](https://docs.google.com/document/d/1A9xvzwqnFVSOZ5fD3blKODXfsat5fg6ZhnKu9LK3lB4/) Prometheus alerting rules are under the key `prometheusAlerts` in the top level object, so in order to add an additional alerting rule, we can simply merge an extra rule into the existing object.
+
+The format is exactly the Prometheus format, so there should be no changes necessary should you have existing rules that you want to include.
+
+> Note that alerts can just as well be included into this file, using the jsonnet `import` function. In this example it is just inlined in order to demonstrate their use in a single file.
+
+[embedmd]:# (../examples/prometheus-additional-alert-rule-example.jsonnet)
+```jsonnet
+local kp = (import 'kube-prometheus/kube-prometheus.libsonnet') + {
+  _config+:: {
+    namespace: 'monitoring',
+  },
+  prometheusAlerts+:: {
+    groups+: [
+      {
+        name: 'example-group',
+        rules: [
+          {
+            alert: 'DeadMansSwitch',
+            expr: 'vector(1)',
+            labels: {
+              severity: 'none',
+            },
+            annotations: {
+              description: 'This is a DeadMansSwitch meant to ensure that the entire alerting pipeline is functional.',
+            },
+          },
+        ],
+      },
+    ],
+  },
+};
+
+{ ['00namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } +
+{ ['0prometheus-operator-' + name]: kp.prometheusOperator[name] for name in std.objectFields(kp.prometheusOperator) } +
+{ ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } +
+{ ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } +
+{ ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } +
+{ ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } +
+{ ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) }
+```
+
+### Recording rules
+
+In order to add a recording rule, simply do the same with the `prometheusRules` field.
+
+> Note that rules can just as well be included into this file, using the jsonnet `import` function. In this example it is just inlined in order to demonstrate their use in a single file.
+
+[embedmd]:# (../examples/prometheus-additional-recording-rule-example.jsonnet)
+```jsonnet
+local kp = (import 'kube-prometheus/kube-prometheus.libsonnet') + {
+  _config+:: {
+    namespace: 'monitoring',
+  },
+  prometheusRules+:: {
+    groups+: [
+      {
+        name: 'example-group',
+        rules: [
+          {
+            record: 'some_recording_rule_name',
+            expr: 'vector(1)',
+          },
+        ],
+      },
+    ],
+  },
+};
+
+{ ['00namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } +
+{ ['0prometheus-operator-' + name]: kp.prometheusOperator[name] for name in std.objectFields(kp.prometheusOperator) } +
+{ ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } +
+{ ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } +
+{ ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } +
+{ ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } +
+{ ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) }
+```
+
+### Pre-rendered rules
+
+We acknowledge, that users may need to transition existing rules, and therefore allow an option to add additional pre-rendered rules. This can be done simply by importing the existing rules in the [Prometheus rule format](https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/) using the jsonnet function `importstr`. In this example we are importing a [provided example rule](examples/example.rules.yaml).
+
+[embedmd]:# (../examples/prometheus-additional-rendered-rule-example.jsonnet)
+```jsonnet
+local kp = (import 'kube-prometheus/kube-prometheus.libsonnet') + {
+  _config+:: {
+    namespace: 'monitoring',
+    prometheus+:: {
+      renderedRules: {
+        'example.rules.yaml': (importstr 'example.rules.yaml'),
+      },
+    },
+  },
+};
+
+{ ['00namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } +
+{ ['0prometheus-operator-' + name]: kp.prometheusOperator[name] for name in std.objectFields(kp.prometheusOperator) } +
+{ ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } +
+{ ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } +
+{ ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } +
+{ ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } +
+{ ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) }
+```
+
+## Dashboards
+
+Dashboards can either be added using jsonnet or simply a pre-rendered json dashboard.
+
+### Jsonnet dashboard
+
+We recommend using the [grafonnet]() library for jsonnet, which gives you a simple DSL to generate Grafana dashboards. Following the [Prometheus Monitoring Mixins proposal](https://docs.google.com/document/d/1A9xvzwqnFVSOZ5fD3blKODXfsat5fg6ZhnKu9LK3lB4/) additional dashboards are added to the `grafanaDashboards` key, located in the top level object. To add new jsonnet dashboards, simply add one.
+
+> Note that dashboards can just as well be included into this file, using the jsonnet `import` function. In this example it is just inlined in order to demonstrate their use in a single file.
+
+[embedmd]:# (../examples/grafana-additional-jsonnet-dashboard-example.jsonnet)
+```jsonnet
+local grafana = import 'grafonnet/grafana.libsonnet';
+local dashboard = grafana.dashboard;
+local row = grafana.row;
+local prometheus = grafana.prometheus;
+local template = grafana.template;
+local graphPanel = grafana.graphPanel;
+
+local kp = (import 'kube-prometheus/kube-prometheus.libsonnet') + {
+  _config+:: {
+    namespace: 'monitoring',
+  },
+  grafanaDashboards+:: {
+    'my-dashboard.json':
+      dashboard.new('My Dashboard')
+      .addTemplate(
+        {
+          current: {
+            text: 'Prometheus',
+            value: 'Prometheus',
+          },
+          hide: 0,
+          label: null,
+          name: 'datasource',
+          options: [],
+          query: 'prometheus',
+          refresh: 1,
+          regex: '',
+          type: 'datasource',
+        },
+      )
+      .addRow(
+        row.new()
+        .addPanel(graphPanel.new('My Panel', span=6, datasource='$datasource')
+                  .addTarget(prometheus.target('vector(1)')))
+      ),
+  },
+};
+
+{ ['00namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } +
+{ ['0prometheus-operator-' + name]: kp.prometheusOperator[name] for name in std.objectFields(kp.prometheusOperator) } +
+{ ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } +
+{ ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } +
+{ ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } +
+{ ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } +
+{ ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) }
+```
+
+### Pre-rendered Grafana dashboards
+
+As jsonnet is a superset of json, the jsonnet `import` function can be used to include Grafana dashboard json blobs. In this example we are importing a [provided example dashboard](examples/example-grafana-dashboard.json).
+
+[embedmd]:# (../examples/grafana-additional-rendered-dashboard-example.jsonnet)
+```jsonnet
+local kp = (import 'kube-prometheus/kube-prometheus.libsonnet') + {
+  _config+:: {
+    namespace: 'monitoring',
+  },
+  grafanaDashboards+:: {
+    'my-dashboard.json': (import 'example-grafana-dashboard.json'),
+  },
+};
+
+{ ['00namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } +
+{ ['0prometheus-operator-' + name]: kp.prometheusOperator[name] for name in std.objectFields(kp.prometheusOperator) } +
+{ ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } +
+{ ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } +
+{ ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } +
+{ ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } +
+{ ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) }
+```
diff --git a/contrib/kube-prometheus/docs/Monitoring external etcd.md b/contrib/kube-prometheus/docs/monitoring-external-etcd.md
similarity index 100%
rename from contrib/kube-prometheus/docs/Monitoring external etcd.md
rename to contrib/kube-prometheus/docs/monitoring-external-etcd.md
diff --git a/contrib/kube-prometheus/examples/example-grafana-dashboard.json b/contrib/kube-prometheus/examples/example-grafana-dashboard.json
new file mode 100644
index 000000000..a891040be
--- /dev/null
+++ b/contrib/kube-prometheus/examples/example-grafana-dashboard.json
@@ -0,0 +1,177 @@
+{
+    "annotations": {
+        "list": [
+
+        ]
+    },
+    "editable": false,
+    "gnetid": null,
+    "graphtooltip": 0,
+    "hidecontrols": false,
+    "id": null,
+    "links": [
+
+    ],
+    "refresh": "",
+    "rows": [
+        {
+            "collapse": false,
+            "collapsed": false,
+            "height": "250px",
+            "panels": [
+                {
+                    "aliascolors": {
+
+                    },
+                    "bars": false,
+                    "dashlength": 10,
+                    "dashes": false,
+                    "datasource": "$datasource",
+                    "fill": 1,
+                    "gridpos": {
+
+                    },
+                    "id": 2,
+                    "legend": {
+                        "alignastable": false,
+                        "avg": false,
+                        "current": false,
+                        "max": false,
+                        "min": false,
+                        "rightside": false,
+                        "show": true,
+                        "total": false,
+                        "values": false
+                    },
+                    "lines": true,
+                    "linewidth": 1,
+                    "nullpointmode": "null",
+                    "percentage": false,
+                    "pointradius": 5,
+                    "points": false,
+                    "renderer": "flot",
+                    "repeat": null,
+                    "seriesoverrides": [
+
+                    ],
+                    "spacelength": 10,
+                    "span": 6,
+                    "stack": false,
+                    "steppedline": false,
+                    "targets": [
+                        {
+                            "expr": "vector(1)",
+                            "format": "time_series",
+                            "intervalfactor": 2,
+                            "legendformat": "",
+                            "refid": "a"
+                        }
+                    ],
+                    "thresholds": [
+
+                    ],
+                    "timefrom": null,
+                    "timeshift": null,
+                    "title": "my panel",
+                    "tooltip": {
+                        "shared": true,
+                        "sort": 0,
+                        "value_type": "individual"
+                    },
+                    "type": "graph",
+                    "xaxis": {
+                        "buckets": null,
+                        "mode": "time",
+                        "name": null,
+                        "show": true,
+                        "values": [
+
+                        ]
+                    },
+                    "yaxes": [
+                        {
+                            "format": "short",
+                            "label": null,
+                            "logbase": 1,
+                            "max": null,
+                            "min": null,
+                            "show": true
+                        },
+                        {
+                            "format": "short",
+                            "label": null,
+                            "logbase": 1,
+                            "max": null,
+                            "min": null,
+                            "show": true
+                        }
+                    ]
+                }
+            ],
+            "repeat": null,
+            "repeatiteration": null,
+            "repeatrowid": null,
+            "showtitle": false,
+            "title": "dashboard row",
+            "titlesize": "h6",
+            "type": "row"
+        }
+    ],
+    "schemaversion": 14,
+    "style": "dark",
+    "tags": [
+
+    ],
+    "templating": {
+        "list": [
+            {
+                "current": {
+                    "text": "prometheus",
+                    "value": "prometheus"
+                },
+                "hide": 0,
+                "label": null,
+                "name": "datasource",
+                "options": [
+
+                ],
+                "query": "prometheus",
+                "refresh": 1,
+                "regex": "",
+                "type": "datasource"
+            }
+        ]
+    },
+    "time": {
+        "from": "now-6h",
+        "to": "now"
+    },
+    "timepicker": {
+        "refresh_intervals": [
+            "5s",
+            "10s",
+            "30s",
+            "1m",
+            "5m",
+            "15m",
+            "30m",
+            "1h",
+            "2h",
+            "1d"
+        ],
+        "time_options": [
+            "5m",
+            "15m",
+            "1h",
+            "6h",
+            "12h",
+            "24h",
+            "2d",
+            "7d",
+            "30d"
+        ]
+    },
+    "timezone": "browser",
+    "title": "my dashboard",
+    "version": 0
+}
diff --git a/contrib/kube-prometheus/examples/example.rules.yaml b/contrib/kube-prometheus/examples/example.rules.yaml
new file mode 100644
index 000000000..94d9d6915
--- /dev/null
+++ b/contrib/kube-prometheus/examples/example.rules.yaml
@@ -0,0 +1,9 @@
+groups:
+- name: example-group
+  rules:
+  - alert: DeadMansSwitch
+    expr: vector(1)
+    labels:
+      severity: "none"
+    annotations:
+      description: This is a DeadMansSwitch meant to ensure that the entire alerting pipeline is functional.
diff --git a/contrib/kube-prometheus/examples/grafana-additional-jsonnet-dashboard-example.jsonnet b/contrib/kube-prometheus/examples/grafana-additional-jsonnet-dashboard-example.jsonnet
new file mode 100644
index 000000000..578d6a1fe
--- /dev/null
+++ b/contrib/kube-prometheus/examples/grafana-additional-jsonnet-dashboard-example.jsonnet
@@ -0,0 +1,45 @@
+local grafana = import 'grafonnet/grafana.libsonnet';
+local dashboard = grafana.dashboard;
+local row = grafana.row;
+local prometheus = grafana.prometheus;
+local template = grafana.template;
+local graphPanel = grafana.graphPanel;
+
+local kp = (import 'kube-prometheus/kube-prometheus.libsonnet') + {
+  _config+:: {
+    namespace: 'monitoring',
+  },
+  grafanaDashboards+:: {
+    'my-dashboard.json':
+      dashboard.new('My Dashboard')
+      .addTemplate(
+        {
+          current: {
+            text: 'Prometheus',
+            value: 'Prometheus',
+          },
+          hide: 0,
+          label: null,
+          name: 'datasource',
+          options: [],
+          query: 'prometheus',
+          refresh: 1,
+          regex: '',
+          type: 'datasource',
+        },
+      )
+      .addRow(
+        row.new()
+        .addPanel(graphPanel.new('My Panel', span=6, datasource='$datasource')
+                  .addTarget(prometheus.target('vector(1)')))
+      ),
+  },
+};
+
+{ ['00namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } +
+{ ['0prometheus-operator-' + name]: kp.prometheusOperator[name] for name in std.objectFields(kp.prometheusOperator) } +
+{ ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } +
+{ ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } +
+{ ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } +
+{ ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } +
+{ ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) }
diff --git a/contrib/kube-prometheus/examples/grafana-additional-rendered-dashboard-example.jsonnet b/contrib/kube-prometheus/examples/grafana-additional-rendered-dashboard-example.jsonnet
new file mode 100644
index 000000000..8aa26bdc0
--- /dev/null
+++ b/contrib/kube-prometheus/examples/grafana-additional-rendered-dashboard-example.jsonnet
@@ -0,0 +1,16 @@
+local kp = (import 'kube-prometheus/kube-prometheus.libsonnet') + {
+  _config+:: {
+    namespace: 'monitoring',
+  },
+  grafanaDashboards+:: {
+    'my-dashboard.json': (import 'example-grafana-dashboard.json'),
+  },
+};
+
+{ ['00namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } +
+{ ['0prometheus-operator-' + name]: kp.prometheusOperator[name] for name in std.objectFields(kp.prometheusOperator) } +
+{ ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } +
+{ ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } +
+{ ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } +
+{ ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } +
+{ ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) }
diff --git a/contrib/kube-prometheus/examples/jsonnet-build-snippet/build-snippet.jsonnet b/contrib/kube-prometheus/examples/jsonnet-build-snippet/build-snippet.jsonnet
new file mode 100644
index 000000000..5a11cef62
--- /dev/null
+++ b/contrib/kube-prometheus/examples/jsonnet-build-snippet/build-snippet.jsonnet
@@ -0,0 +1,7 @@
+{ ['00namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } +
+{ ['0prometheus-operator-' + name]: kp.prometheusOperator[name] for name in std.objectFields(kp.prometheusOperator) } +
+{ ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } +
+{ ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } +
+{ ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } +
+{ ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } +
+{ ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) }
diff --git a/contrib/kube-prometheus/examples/bootkube.jsonnet b/contrib/kube-prometheus/examples/jsonnet-snippets/bootkube.jsonnet
similarity index 100%
rename from contrib/kube-prometheus/examples/bootkube.jsonnet
rename to contrib/kube-prometheus/examples/jsonnet-snippets/bootkube.jsonnet
diff --git a/contrib/kube-prometheus/examples/kubeadm.jsonnet b/contrib/kube-prometheus/examples/jsonnet-snippets/kubeadm.jsonnet
similarity index 100%
rename from contrib/kube-prometheus/examples/kubeadm.jsonnet
rename to contrib/kube-prometheus/examples/jsonnet-snippets/kubeadm.jsonnet
diff --git a/contrib/kube-prometheus/examples/node-ports.jsonnet b/contrib/kube-prometheus/examples/jsonnet-snippets/node-ports.jsonnet
similarity index 100%
rename from contrib/kube-prometheus/examples/node-ports.jsonnet
rename to contrib/kube-prometheus/examples/jsonnet-snippets/node-ports.jsonnet
diff --git a/contrib/kube-prometheus/examples/ksonnet-example.jsonnet b/contrib/kube-prometheus/examples/ksonnet-example.jsonnet
index e83ceaf07..565d113fd 100644
--- a/contrib/kube-prometheus/examples/ksonnet-example.jsonnet
+++ b/contrib/kube-prometheus/examples/ksonnet-example.jsonnet
@@ -1,9 +1,9 @@
-local k = import "ksonnet/ksonnet.beta.3/k.libsonnet";
+local k = import 'ksonnet/ksonnet.beta.3/k.libsonnet';
 local daemonset = k.apps.v1beta2.daemonSet;
 
-((import "kube-prometheus/kube-prometheus.libsonnet") + {
-	nodeExporter+: {
-		daemonset+:
-            daemonset.mixin.metadata.withNamespace("my-custom-namespace")
-    }
-}).nodeExporter.daemonset
+((import 'kube-prometheus/kube-prometheus.libsonnet') + {
+   nodeExporter+: {
+     daemonset+:
+       daemonset.mixin.metadata.withNamespace('my-custom-namespace'),
+   },
+ }).nodeExporter.daemonset
diff --git a/contrib/kube-prometheus/examples/prometheus-additional-alert-rule-example.jsonnet b/contrib/kube-prometheus/examples/prometheus-additional-alert-rule-example.jsonnet
new file mode 100644
index 000000000..b8d16af87
--- /dev/null
+++ b/contrib/kube-prometheus/examples/prometheus-additional-alert-rule-example.jsonnet
@@ -0,0 +1,32 @@
+local kp = (import 'kube-prometheus/kube-prometheus.libsonnet') + {
+  _config+:: {
+    namespace: 'monitoring',
+  },
+  prometheusAlerts+:: {
+    groups+: [
+      {
+        name: 'example-group',
+        rules: [
+          {
+            alert: 'DeadMansSwitch',
+            expr: 'vector(1)',
+            labels: {
+              severity: 'none',
+            },
+            annotations: {
+              description: 'This is a DeadMansSwitch meant to ensure that the entire alerting pipeline is functional.',
+            },
+          },
+        ],
+      },
+    ],
+  },
+};
+
+{ ['00namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } +
+{ ['0prometheus-operator-' + name]: kp.prometheusOperator[name] for name in std.objectFields(kp.prometheusOperator) } +
+{ ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } +
+{ ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } +
+{ ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } +
+{ ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } +
+{ ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) }
diff --git a/contrib/kube-prometheus/examples/prometheus-additional-recording-rule-example.jsonnet b/contrib/kube-prometheus/examples/prometheus-additional-recording-rule-example.jsonnet
new file mode 100644
index 000000000..7974e338c
--- /dev/null
+++ b/contrib/kube-prometheus/examples/prometheus-additional-recording-rule-example.jsonnet
@@ -0,0 +1,26 @@
+local kp = (import 'kube-prometheus/kube-prometheus.libsonnet') + {
+  _config+:: {
+    namespace: 'monitoring',
+  },
+  prometheusRules+:: {
+    groups+: [
+      {
+        name: 'example-group',
+        rules: [
+          {
+            record: 'some_recording_rule_name',
+            expr: 'vector(1)',
+          },
+        ],
+      },
+    ],
+  },
+};
+
+{ ['00namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } +
+{ ['0prometheus-operator-' + name]: kp.prometheusOperator[name] for name in std.objectFields(kp.prometheusOperator) } +
+{ ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } +
+{ ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } +
+{ ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } +
+{ ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } +
+{ ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) }
diff --git a/contrib/kube-prometheus/examples/prometheus-additional-rendered-rule-example.jsonnet b/contrib/kube-prometheus/examples/prometheus-additional-rendered-rule-example.jsonnet
new file mode 100644
index 000000000..4ee7317d8
--- /dev/null
+++ b/contrib/kube-prometheus/examples/prometheus-additional-rendered-rule-example.jsonnet
@@ -0,0 +1,18 @@
+local kp = (import 'kube-prometheus/kube-prometheus.libsonnet') + {
+  _config+:: {
+    namespace: 'monitoring',
+    prometheus+:: {
+      renderedRules: {
+        'example.rules.yaml': (importstr 'example.rules.yaml'),
+      },
+    },
+  },
+};
+
+{ ['00namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } +
+{ ['0prometheus-operator-' + name]: kp.prometheusOperator[name] for name in std.objectFields(kp.prometheusOperator) } +
+{ ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } +
+{ ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } +
+{ ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } +
+{ ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } +
+{ ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) }
diff --git a/contrib/kube-prometheus/jsonnet/kube-prometheus/prometheus/prometheus.libsonnet b/contrib/kube-prometheus/jsonnet/kube-prometheus/prometheus/prometheus.libsonnet
index 3b2d415c1..d2ae7ae6b 100644
--- a/contrib/kube-prometheus/jsonnet/kube-prometheus/prometheus/prometheus.libsonnet
+++ b/contrib/kube-prometheus/jsonnet/kube-prometheus/prometheus/prometheus.libsonnet
@@ -15,6 +15,7 @@ local k = import 'ksonnet/ksonnet.beta.3/k.libsonnet';
     prometheus+:: {
       replicas: 2,
       rules: {},
+      renderedRules: {},
     },
   },
 
@@ -36,7 +37,7 @@ local k = import 'ksonnet/ksonnet.beta.3/k.libsonnet';
     rules:
       local configMap = k.core.v1.configMap;
 
-      configMap.new('prometheus-k8s-rules', { 'all.rules.yaml': std.manifestYamlDoc($._config.prometheus.rules) }) +
+      configMap.new('prometheus-k8s-rules', ({ 'all.rules.yaml': std.manifestYamlDoc($._config.prometheus.rules) } + $._config.prometheus.renderedRules)) +
       configMap.mixin.metadata.withLabels({ role: 'alert-rules', prometheus: 'k8s' }) +
       configMap.mixin.metadata.withNamespace($._config.namespace),
     roleBindingDefault:
diff --git a/contrib/kube-prometheus/test.sh b/contrib/kube-prometheus/test.sh
new file mode 100755
index 000000000..dad4e75fa
--- /dev/null
+++ b/contrib/kube-prometheus/test.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+set -e
+# only exit with zero if all commands of the pipeline exit successfully
+set -o pipefail
+
+
+for i in examples/jsonnet-snippets/*.jsonnet; do
+    [ -f "$i" ] || break
+    echo "Testing: ${i}"
+    echo ""
+    snippet="local kp = $(<${i});
+
+$(<examples/jsonnet-build-snippet/build-snippet.jsonnet)"
+    echo "${snippet}" > "test.jsonnet"
+    echo "\`\`\`"
+    echo "${snippet}"
+    echo "\`\`\`"
+    echo ""
+    jsonnet -J vendor "test.jsonnet" > /dev/null
+    rm -rf "test.jsonnet"
+done
+
+for i in examples/*.jsonnet; do
+    [ -f "$i" ] || break
+    echo "Testing: ${i}"
+    echo ""
+    echo "\`\`\`"
+    echo "$(<${i})"
+    echo "\`\`\`"
+    echo ""
+    jsonnet -J vendor ${i} > /dev/null
+done