Repository: linkerd/linkerd2
Pull request #5304 (2020-12-01 11:50:51)

jaeger: Add support for override flags
This change adds flags `set`, `set-string`, `values`, `set-files`,
etc flags which are used to override the default values. This is
similar to that of Helm.

This also updates the install workflow to directly use Helm v3
pkg for chart loading and generation, without having to use
our chart type, etc.

Signed-off-by: Tarun Pothulapati <tarunpothulapati@outlook.com>



Patch:
diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml
index fbab3401b..8d2ad93f8 100644
--- a/.github/workflows/static_checks.yml
+++ b/.github/workflows/static_checks.yml
@@ -83,3 +83,12 @@ jobs:
       uses: actions/checkout@722adc6
     - name: Markdown lint
       run: bin/markdownlint-all
+  chart_docs_diff:
+    name: Chart readme diff check
+    runs-on: ubuntu-18.04
+    steps:
+    - name: Checkout code
+      # actions/checkout@v2
+      uses: actions/checkout@722adc6
+    - name: Check docs for diff
+      run: bin/helm-docs-diff
diff --git a/.helmdocsignore b/.helmdocsignore
new file mode 100644
index 000000000..d21bb0e1b
--- /dev/null
+++ b/.helmdocsignore
@@ -0,0 +1,1 @@
+# Add potential chart ignores here
diff --git a/.proxy-version b/.proxy-version
index 42d0cf775..75813ee72 100644
--- a/.proxy-version
+++ b/.proxy-version
@@ -1,1 +1,1 @@
-v2.123.0
+v2.124.0
diff --git a/BUILD.md b/BUILD.md
index 92f9ea6db..7497568e2 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -296,6 +296,46 @@ automatically regenerated with the command:
 go test ./cli/cmd/... --update
 ```
 
+#### Generating helm charts docs
+
+Whenever a new chart is created, or updated a readme should be generated from
+the chart's values.yml. This can be done by utilizing the bundled
+[helm-docs](https://github.com/norwoodj/helm-docs) binary. For adding additional
+information, such as specific installation instructions a readme template is
+required to be created. Check existing charts for example.
+
+##### Annotating values.yml
+
+To allow helm-docs to properly document the values in values.yml a descriptive
+comment is required. This can be done in two ways.
+Either comment the value directly above with
+`# -- This is a really nice value` where the double dashes automatically
+annotates the value. Another explicit usage is to type out the value name.
+`# global.MyNiceValue -- I really like this value`
+
+##### Using helm-docs
+
+Example usage:
+
+```sh
+bin/helm-docs
+bin/helm-docs --dry-run #Prints to cli instead
+bin/helm-docs --chart-search-root=./charts #Sets search root for charts
+bin/helm-docs --template-files=README.md.gotmpl #Sets the template file used
+```
+
+Note:
+The tool searches through the current directory and sub-directories by default.
+For additional information checkout their repo above.
+
+##### Markdown templates
+
+In order to accommodate for extra data that might not have a proper place in the
+´values.yaml´ file the corresponding ´README.md.gotmpl´ can be modified for each
+chart. This template allows the standard markdown syntax as well as the go
+templating functions. Checkout
+[helm-docs](https://github.com/norwoodj/helm-docs) for more info.
+
 ##### Pretty-printed diffs for templated text
 
 When running `go test`, mismatched text is usually displayed as a compact diff.
diff --git a/CHANGES.md b/CHANGES.md
index 9ba1a9a13..a141bc4be 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,20 @@
 
 # Changes
 
+## edge-20.12.1
+
+This edge release continues the work of decoupling non-core Linkerd components
+by moving more tracing related functionality into the Linkerd-jaeger extension.
+
+* Continued work on moving tracing functionality from the main control plane
+  into the `linkerd-jaeger` extension
+* Fixed a potential panic in the proxy when looking up a socket's peer address
+  while under high load
+* Added automatic readme generation for charts (thanks @GMarkfjard!)
+* Fixed zsh completion for the CLI (thanks @jiraguha!)
+* Added support for multicluster gateways of types other than LoadBalancer
+  (thanks @DaspawnW!)
+
 ## edge-20.11.5
 
 This edge release improves the proxy's support high-traffic workloads. It also
diff --git a/bin/helm-docs b/bin/helm-docs
new file mode 100755
index 000000000..da5e72eab
--- /dev/null
+++ b/bin/helm-docs
@@ -0,0 +1,57 @@
+#!/usr/bin/env sh
+
+set -eu
+
+helmdocsv=1.4.0
+bindir=$( cd "${0%/*}" && pwd ) # Change to script dir and set bin dir to this
+targetbin=$( cd "$bindir"/.. && pwd )/target/bin
+helmdocsbin=$targetbin/helm-docs-$helmdocsv
+os=""
+arch=""
+
+if [ ! -f "$helmdocsbin" ]; then
+    case $(uname | tr '[:upper:]' '[:lower:]') in
+        darwin*)
+            os=darwin
+            arch=x86_64
+            ;;
+        linux*)
+            os=linux
+            case $(uname -m) in
+                x86_64) arch=x86_64 ;;
+                amd64) arch=amd64 ;;
+                arm)
+                    tmp=$(dpkg --print-architecture)
+                    if echo "$tmp" | grep -q arm64; then
+                        arch=arm64
+                    elif echo "$tmp" | grep -q armv7; then
+                        arch=armv7 
+                    elif echo "$tmp" | grep -q armv6; then
+                        arch=armv6
+                    fi
+                ;;
+            esac
+            ;;
+        msys*)
+            os=windows
+            arch=x86_64
+            ;;
+    esac
+
+    if [ -z "$os" ]; then
+        echo "Couldn't find a matching binary"
+        exit 126
+    fi
+    helmdocscurl="https://github.com/norwoodj/helm-docs/releases/download/v$helmdocsv/helm-docs_${helmdocsv}_${os}_${arch}.tar.gz"
+    tmp=$(mktemp -d -t helm-docs.XXX)
+    mkdir -p "$targetbin"
+    (
+        cd "$tmp"
+        curl -Lsf -o "./helm-docs.tar.gz" "$helmdocscurl"
+        tar zf "./helm-docs.tar.gz" -x "helm-docs"
+        chmod +x "helm-docs"
+    )
+    mv "$tmp/helm-docs" "$helmdocsbin"
+fi
+
+"$helmdocsbin" "$@"
diff --git a/bin/helm-docs-diff b/bin/helm-docs-diff
new file mode 100755
index 000000000..d874e69d2
--- /dev/null
+++ b/bin/helm-docs-diff
@@ -0,0 +1,15 @@
+#!/usr/bin/env sh
+
+set -eu
+
+bin/helm-docs
+
+dir_dirty=$(git diff HEAD)
+if [ -z "$dir_dirty" ]; then
+  echo "Helm-docs generated readmes match chart readmes."
+  exit 0
+else
+  echo "Helm-docs generated readmes diverge from current chart readmes:"
+  echo "$(git status)"
+  exit 64
+fi
diff --git a/charts/add-ons/grafana/README.md b/charts/add-ons/grafana/README.md
new file mode 100644
index 000000000..5ec9e5f4b
--- /dev/null
+++ b/charts/add-ons/grafana/README.md
@@ -0,0 +1,28 @@
+# grafana
+
+A Helm chart for the grafana add-on in Linkerd
+
+![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square)
+
+![AppVersion: 1.0](https://img.shields.io/badge/AppVersion-1.0-informational?style=flat-square)
+
+## Requirements
+
+| Repository | Name | Version |
+|------------|------|---------|
+| file://../../partials | partials | 0.1.0 |
+
+## Values
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| image.name | string | `"ghcr.io/linkerd/grafana"` | Docker image name for the grafana instance |
+| image.tag | string | `nil` | Docker image tag for the grafana instance |
+| proxy.resources | string | `nil` | Structure analog to the resources fields above, but overriding the resources of the linkerd proxy injected into the grafana pod. |
+| resources.cpu.limit | string | `nil` | Maximum amount of CPU units that the grafana container can use |
+| resources.cpu.request | string | `nil` | Amount of CPU units that the grafana container requests |
+| resources.memory.limit | string | `nil` | Maximum amount of memory that grafana container can use |
+| resources.memory.request | string | `nil` | Amount of memory that the grafana container requests |
+
+----------------------------------------------
+Autogenerated from chart metadata using [helm-docs v1.4.0](https://github.com/norwoodj/helm-docs/releases/v1.4.0)
diff --git a/charts/add-ons/grafana/README.md.gotmpl b/charts/add-ons/grafana/README.md.gotmpl
new file mode 100644
index 000000000..37f510106
--- /dev/null
+++ b/charts/add-ons/grafana/README.md.gotmpl
@@ -0,0 +1,14 @@
+{{ template "chart.header" . }}
+{{ template "chart.description" . }}
+
+{{ template "chart.versionBadge" . }}
+{{ template "chart.typeBadge" . }}
+{{ template "chart.appVersionBadge" . }}
+
+{{ template "chart.homepageLine" . }}
+
+{{ template "chart.requirementsSection" . }}
+
+{{ template "chart.valuesSection" . }}
+
+{{ template "helm-docs.versionFooter" . }}
diff --git a/charts/add-ons/grafana/values.yaml b/charts/add-ons/grafana/values.yaml
index 41328cd36..c16b17081 100644
--- a/charts/add-ons/grafana/values.yaml
+++ b/charts/add-ons/grafana/values.yaml
@@ -1,2 +1,21 @@
 image:
+  # -- Docker image name for the grafana instance
   name: ghcr.io/linkerd/grafana
+  # -- Docker image tag for the grafana instance
+  tag:
+
+resources:
+  cpu:
+    # -- Maximum amount of CPU units that the grafana container can use
+    limit:
+    # -- Amount of CPU units that the grafana container requests
+    request:
+  memory:
+    # -- Maximum amount of memory that grafana container can use
+    limit:
+    # -- Amount of memory that the grafana container requests
+    request:
+proxy:
+  # -- Structure analog to the resources fields above, but overriding the
+  # resources of the linkerd proxy injected into the grafana pod.
+  resources:
\ No newline at end of file
diff --git a/charts/add-ons/prometheus/README.md b/charts/add-ons/prometheus/README.md
new file mode 100644
index 000000000..66b4efaf4
--- /dev/null
+++ b/charts/add-ons/prometheus/README.md
@@ -0,0 +1,35 @@
+# prometheus
+
+A Helm chart for the prometheus add-on in Linkerd
+
+![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square)
+
+![AppVersion: 1.0](https://img.shields.io/badge/AppVersion-1.0-informational?style=flat-square)
+
+## Requirements
+
+| Repository | Name | Version |
+|------------|------|---------|
+| file://../../partials | partials | 0.1.0 |
+
+## Values
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| alertManagers | string | `nil` | Alertmanager instances the Prometheus server sends alerts to configured via the static_configs parameter. |
+| alertRelabelConfigs | string | `nil` | Alert relabeling is applied to alerts before they are sent to the Alertmanager. |
+| args | object | `{"config.file":"/etc/prometheus/prometheus.yml","log.level":"info","storage.tsdb.path":"/data","storage.tsdb.retention.time":"6h"}` | Command line options for Prometheus binary |
+| globalConfig | object | `{"evaluation_interval":"10s","scrape_interval":"10s","scrape_timeout":"10s"}` | The global configuration specifies parameters that are valid in all other configuration contexts. |
+| image | string | `"prom/prometheus:v2.19.3"` | Docker image for the prometheus instance |
+| proxy.resources | string | `nil` | CPU and Memory resources required by proxy injected into prometheus pod (see global.proxy.resources for sub-fields) |
+| remoteWrite | string | `nil` | Allows transparently sending samples to an endpoint. Mostly used for long term storage. |
+| resources.cpu.limit | string | `nil` | Maximum amount of CPU units that the prometheus container can use |
+| resources.cpu.request | string | `nil` | Amount of CPU units that the prometheus container requests |
+| resources.memory.limit | string | `nil` | Maximum amount of memory that prometheus container can use |
+| resources.memory.request | string | `nil` | Amount of memory that the prometheus container requests |
+| ruleConfigMapMounts | string | `nil` | Alerting/recording rule ConfigMap mounts (sub-path names must end in ´_rules.yml´ or ´_rules.yaml´) |
+| scrapeConfigs | string | `nil` | A scrapeConfigs section specifies a set of targets and parameters describing how to scrape them. |
+| sideCarContainers | string | `nil` | A sidecarContainers section specifies a list of secondary containers to run in the prometheus pod e.g. to export data to non-prometheus systems |
+
+----------------------------------------------
+Autogenerated from chart metadata using [helm-docs v1.4.0](https://github.com/norwoodj/helm-docs/releases/v1.4.0)
diff --git a/charts/add-ons/prometheus/README.md.gotmpl b/charts/add-ons/prometheus/README.md.gotmpl
new file mode 100644
index 000000000..37f510106
--- /dev/null
+++ b/charts/add-ons/prometheus/README.md.gotmpl
@@ -0,0 +1,14 @@
+{{ template "chart.header" . }}
+{{ template "chart.description" . }}
+
+{{ template "chart.versionBadge" . }}
+{{ template "chart.typeBadge" . }}
+{{ template "chart.appVersionBadge" . }}
+
+{{ template "chart.homepageLine" . }}
+
+{{ template "chart.requirementsSection" . }}
+
+{{ template "chart.valuesSection" . }}
+
+{{ template "helm-docs.versionFooter" . }}
diff --git a/charts/add-ons/prometheus/values.yaml b/charts/add-ons/prometheus/values.yaml
index 822596321..6f25b187d 100644
--- a/charts/add-ons/prometheus/values.yaml
+++ b/charts/add-ons/prometheus/values.yaml
@@ -3,13 +3,121 @@
 # when installing with the parent linkerd2 chart
 # Do not override them in this file!
 # If installing/upgrading with linkerd's CLI, use the `--config` flag.
+
+# -- Docker image for the prometheus instance
 image: prom/prometheus:v2.19.3
+# -- Command line options for Prometheus binary
 args:
   storage.tsdb.path: /data
   storage.tsdb.retention.time: 6h
   config.file: /etc/prometheus/prometheus.yml
   log.level: info
+# -- The global configuration specifies parameters that are valid in all other
+# configuration contexts.
 globalConfig:
   scrape_interval: 10s
   scrape_timeout: 10s
   evaluation_interval: 10s
+
+# -- Alert relabeling is applied to alerts before they are sent to the
+# Alertmanager.
+alertRelabelConfigs:
+# Ex:
+# - action: labeldrop
+#   regex: prometheus_replica
+
+# -- Alertmanager instances the Prometheus server sends alerts to configured via
+# the static_configs parameter.
+alertManagers:
+# Ex:
+# - scheme: http
+#   static_configs:
+#   - targets:
+#     - "alertmanager.linkerd.svc:9093"
+
+# -- Allows transparently sending samples to an endpoint. Mostly used for long
+# term storage.
+remoteWrite:
+
+# -- Alerting/recording rule ConfigMap mounts (sub-path names must end in
+# ´_rules.yml´ or ´_rules.yaml´)
+ruleConfigMapMounts:
+# Ex:
+# - name: alerting-rules
+#   subPath: alerting_rules.yml
+#   configMap: linkerd-prometheus-rules
+# - name: recording-rules
+#   subPath: recording_rules.yml
+#   configMap: linkerd-prometheus-rules
+
+# -- A scrapeConfigs section specifies a set of targets and parameters
+# describing how to scrape them.
+scrapeConfigs:
+# Ex:
+# - job_name: 'kubernetes-nodes'
+#   scheme: https
+#   tls_config:
+#     ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
+#   bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
+#   kubernetes_sd_configs:
+#   - role: node
+#   relabel_configs:
+#   - action: labelmap
+#     regex: __meta_kubernetes_node_label_(.+)
+
+# -- A sidecarContainers section specifies a list of secondary containers to run
+# in the prometheus pod e.g. to export data to non-prometheus systems
+sideCarContainers:
+# Ex:
+# - name: sidecar
+#   image: gcr.io/myproject/stackdriver-prometheus-sidecar
+#   imagePullPolicy: Always
+#   command:
+#   - /bin/sh
+#   - -c
+#   - |
+#     exec /bin/stackdriver-prometheus-sidecar \
+#       --stackdriver.project-id=myproject \
+#       --stackdriver.kubernetes.location=us-central1 \
+#       --stackdriver.kubernetes.cluster-name=mycluster \
+#       --prometheus.wal-directory=/data/wal \
+#       --log.level=info
+#   volumeMounts:
+#   - mountPath: /data
+#     name: data
+#   ports:
+#   - name: foo
+#     containerPort: 9091
+#     protocol: TCP
+
+
+proxy:
+  # -- CPU and Memory resources required by proxy injected into prometheus pod
+  # (see global.proxy.resources for sub-fields)
+  resources:
+
+### WARNING: persistence is experimental and has not been tested/vetted by the Linkerd team.
+### As such, please refer to https://linkerd.io/2/tasks/exporting-metrics/ for the recommended approach to metrics data retention.
+# if enabled, creates a persistent volume claim for prometheus data
+# https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims
+#persistence:
+# -- Storage class used to create prometheus data PV.
+#  storageClass:
+# -- PVC access mode.
+#  accessMode:
+# -- Prometheus data volume size.
+#  size:
+
+
+resources:
+  cpu:
+    # -- Maximum amount of CPU units that the prometheus container can use
+    limit:
+    # -- Amount of CPU units that the prometheus container requests
+    request:
+  memory:
+    # -- Maximum amount of memory that prometheus container can use
+    limit:
+    # -- Amount of memory that the prometheus container requests
+    request:
+
diff --git a/charts/add-ons/tracing/README.md b/charts/add-ons/tracing/README.md
new file mode 100644
index 000000000..1f0d968bb
--- /dev/null
+++ b/charts/add-ons/tracing/README.md
@@ -0,0 +1,31 @@
+# tracing
+
+A Helm chart for the tracing add-on in Linkerd
+
+![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square)
+
+![AppVersion: 1.0](https://img.shields.io/badge/AppVersion-1.0-informational?style=flat-square)
+
+## Requirements
+
+| Repository | Name | Version |
+|------------|------|---------|
+| file://../../partials | partials | 0.1.0 |
+
+## Values
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| collector.image | string | `"omnition/opencensus-collector:0.1.11"` |  |
+| collector.resources.cpu.limit | string | `nil` | Maximum amount of CPU units that the trace collector container can use |
+| collector.resources.cpu.request | string | `nil` | Amount of CPU units that the trace collector container requests |
+| collector.resources.memory.limit | string | `nil` | Maximum amount of memory that trace collector container can use |
+| collector.resources.memory.request | string | `nil` | Amount of memory that the trace collector container requests |
+| jaeger.image | string | `"jaegertracing/all-in-one:1.19.2"` | Docker image for the jaeger instance |
+| jaeger.resources.cpu.limit | string | `nil` | Maximum amount of CPU units that the jaeger container can use |
+| jaeger.resources.cpu.request | string | `nil` | Amount of CPU units that the jaeger container requests |
+| jaeger.resources.memory.limit | string | `nil` | Maximum amount of memory that jaeger container can use |
+| jaeger.resources.memory.request | string | `nil` | Amount of memory that the jaeger container requests |
+
+----------------------------------------------
+Autogenerated from chart metadata using [helm-docs v1.4.0](https://github.com/norwoodj/helm-docs/releases/v1.4.0)
diff --git a/charts/add-ons/tracing/README.md.gotmpl b/charts/add-ons/tracing/README.md.gotmpl
new file mode 100644
index 000000000..37f510106
--- /dev/null
+++ b/charts/add-ons/tracing/README.md.gotmpl
@@ -0,0 +1,14 @@
+{{ template "chart.header" . }}
+{{ template "chart.description" . }}
+
+{{ template "chart.versionBadge" . }}
+{{ template "chart.typeBadge" . }}
+{{ template "chart.appVersionBadge" . }}
+
+{{ template "chart.homepageLine" . }}
+
+{{ template "chart.requirementsSection" . }}
+
+{{ template "chart.valuesSection" . }}
+
+{{ template "helm-docs.versionFooter" . }}
diff --git a/charts/add-ons/tracing/values.yaml b/charts/add-ons/tracing/values.yaml
index ea38760c5..aee0c159a 100644
--- a/charts/add-ons/tracing/values.yaml
+++ b/charts/add-ons/tracing/values.yaml
@@ -1,6 +1,28 @@
 # Default values for tracing.
 collector:
   image: omnition/opencensus-collector:0.1.11
-  # resources:
+  resources:
+    cpu:
+    # -- Maximum amount of CPU units that the trace collector container can use
+      limit:
+      # -- Amount of CPU units that the trace collector container requests
+      request:
+    memory:
+      # -- Maximum amount of memory that trace collector container can use
+      limit:
+      # -- Amount of memory that the trace collector container requests
+      request:
 jaeger:
-  image: jaegertracing/all-in-one:1.19.2
\ No newline at end of file
+  # -- Docker image for the jaeger instance
+  image: jaegertracing/all-in-one:1.19.2
+  resources:
+    cpu:
+      # -- Maximum amount of CPU units that the jaeger container can use
+      limit:
+      # -- Amount of CPU units that the jaeger container requests
+      request:
+    memory:
+      # -- Maximum amount of memory that jaeger container can use
+      limit:
+      # -- Amount of memory that the jaeger container requests
+      request:
diff --git a/charts/linkerd2-cni/Chart.yaml b/charts/linkerd2-cni/Chart.yaml
index 5ec23c1b9..7450b5afd 100644
--- a/charts/linkerd2-cni/Chart.yaml
+++ b/charts/linkerd2-cni/Chart.yaml
@@ -1,6 +1,11 @@
 apiVersion: v1
 appVersion: edge-XX.X.X
-description: A helm chart containing the resources needed by the Linkerd CNI plugin.
+description: |
+  Linkerd is a *service mesh*, designed to give platform-wide observability,
+  reliability, and security without requiring configuration or code changes. The
+  Linkerd [CNI plugin](https://linkerd.io/2/features/cni/) takes care of setting
+  up your pod's network so  incoming and outgoing traffic is proxied through the
+  data plane.
 kubeVersion: ">=1.13.0-0"
 icon: https://linkerd.io/images/logo-only-200h.png
 name: "linkerd2-cni"
diff --git a/charts/linkerd2-cni/README.md b/charts/linkerd2-cni/README.md
index 16b00452e..b7c14942b 100644
--- a/charts/linkerd2-cni/README.md
+++ b/charts/linkerd2-cni/README.md
@@ -1,5 +1,4 @@
-
-# Linkerd2-cni Helm Chart
+# linkerd2-cni
 
 Linkerd is a *service mesh*, designed to give platform-wide observability,
 reliability, and security without requiring configuration or code changes. The
@@ -7,28 +6,42 @@ Linkerd [CNI plugin](https://linkerd.io/2/features/cni/) takes care of setting
 up your pod's network so  incoming and outgoing traffic is proxied through the
 data plane.
 
-## Configuration
-
-The following table lists the configurable parameters of the Linkerd2-cni chart
-and their default values.
-
-| Parameter                            | Description                                                           | Default                       |
-|--------------------------------------|-----------------------------------------------------------------------|-------------------------------|
-|`cniPluginImage`                      | Docker image for the CNI plugin                                       |`ghcr.io/linkerd/cni-plugin`|
-|`cniPluginVersion`                    | Tag for the CNI container Docker image                                |latest version|
-|`cniResourceAnnotation`               | CNI resource annotation. Do not edit                                  |`linkerd.io/cni-resource`
-|`controllerNamespaceLabel`            | Control plane label. Do not edit                                      |`linkerd.io/control-plane-ns`|
-|`createdByAnnotation`                 | Annotation label for the proxy create. Do not edit.                   |`linkerd.io/created-by`|
-|`destCNIBinDir`                       | Directory on the host where the CNI plugin binaries reside            |`/opt/cni/bin`|
-|`destCNINetDir`                       | Directory on the host where the CNI configuration will be placed      |`/etc/cni/net.d`|
-|`ignoreInboundPorts`                  | Inbound ports the proxy should ignore                                 ||
-|`ignoreOutboundPorts`                 | Outbound ports the proxy should ignore                                ||
-|`inboundProxyPort`                    | Inbound port for the proxy container                                  |`4143`|
-|`logLevel`                            | Log level for the CNI plugin                                          |`info`|
-|`namespace`                           | CNI plugin plane namespace                                            |`linkerd-cni`|
-|`outboundProxyPort`                   | Outbound port for the proxy container                                 |`4140`|
-|`portsToRedirect`                     | Ports to redirect to proxy                                            ||
-|`proxyUID`                            | User id under which the proxy shall be ran                            |`2102`|
-|`useWaitFlag`                         | Configures the CNI plugin to use the -w flag for the iptables command |`false`|
-|`installNamespace`                    | Whether to create the CNI plugin plane namespace or not               |`true`|
-|`priorityClassName`                   | Kubernetes priorityClassName for the CNI plugin's Pods                ||
+![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square)
+
+![AppVersion: edge-XX.X.X](https://img.shields.io/badge/AppVersion-edge--XX.X.X-informational?style=flat-square)
+
+## Requirements
+
+Kubernetes: `>=1.13.0-0`
+
+| Repository | Name | Version |
+|------------|------|---------|
+| file://../partials | partials | 0.1.0 |
+
+## Values
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| cniPluginImage | string | `"ghcr.io/linkerd/cni-plugin"` | Docker image for the CNI plugin |
+| cniPluginVersion | string | `"linkerdVersionValue"` | Tag for the CNI container Docker image |
+| cniResourceLabel | string | `"linkerd.io/cni-resource"` | CNI resource annotation. Do not edit |
+| createdByAnnotation | string | `"linkerd.io/created-by"` | Annotation label for the proxy create. Do not edit.  |
+| destCNIBinDir | string | `"/opt/cni/bin"` | Directory on the host where the CNI configuration will be placed |
+| destCNINetDir | string | `"/etc/cni/net.d"` | Directory on the host where the CNI plugin binaries reside  |
+| ignoreInboundPorts | string | `"25,443,587,3306,11211"` | Inbound ports the proxy should ignore - SMTP (25,587) server-first - HTTPS (443) opaque TLS - MYSQL (3306) server-first - Memcached (11211) clients do not issue any preamble, which breaks detection |
+| ignoreOutboundPorts | string | `"25,443,587,3306,11211"` | Outbound ports the proxy should ignore |
+| imagePullSecrets | string | `nil` |  |
+| inboundProxyPort | int | `4143` | Inbound port for the proxy container |
+| installNamespace | bool | `true` | Whether to create the CNI plugin plane namespace or not  |
+| logLevel | string | `"info"` | Log level for the CNI plugin |
+| namespace | string | `"linkerd-cni"` | CNI plugin plane namespace |
+| outboundProxyPort | int | `4140` | Outbound port for the proxy container |
+| portsToRedirect | string | `""` | Ports to redirect to proxy  |
+| priorityClassName | string | `""` | Kubernetes priorityClassName for the CNI plugin's Pods |
+| proxyInjectAnnotation | string | `"linkerd.io/inject"` |  |
+| proxyInjectDisabled | string | `"disabled"` |  |
+| proxyUID | int | `2102` | User id under which the proxy shall be ran |
+| useWaitFlag | bool | `false` | Configures the CNI plugin to use the -w flag for the iptables command |
+
+----------------------------------------------
+Autogenerated from chart metadata using [helm-docs v1.4.0](https://github.com/norwoodj/helm-docs/releases/v1.4.0)
diff --git a/charts/linkerd2-cni/README.md.gotmpl b/charts/linkerd2-cni/README.md.gotmpl
new file mode 100644
index 000000000..37f510106
--- /dev/null
+++ b/charts/linkerd2-cni/README.md.gotmpl
@@ -0,0 +1,14 @@
+{{ template "chart.header" . }}
+{{ template "chart.description" . }}
+
+{{ template "chart.versionBadge" . }}
+{{ template "chart.typeBadge" . }}
+{{ template "chart.appVersionBadge" . }}
+
+{{ template "chart.homepageLine" . }}
+
+{{ template "chart.requirementsSection" . }}
+
+{{ template "chart.valuesSection" . }}
+
+{{ template "helm-docs.versionFooter" . }}
diff --git a/charts/linkerd2-cni/values.yaml b/charts/linkerd2-cni/values.yaml
index 7cbc5ddd4..c6112ca05 100644
--- a/charts/linkerd2-cni/values.yaml
+++ b/charts/linkerd2-cni/values.yaml
@@ -1,26 +1,40 @@
+# -- CNI plugin plane namespace
 namespace: linkerd-cni
+# -- Whether to create the CNI plugin plane namespace or not 
 installNamespace: true
+# -- CNI resource annotation. Do not edit
 cniResourceLabel: linkerd.io/cni-resource
+# -- Inbound port for the proxy container
 inboundProxyPort: 4143
+# -- Outbound port for the proxy container
 outboundProxyPort: 4140
-
-# Default set of ports to skip via itpables:
+# -- Inbound ports the proxy should ignore
 # - SMTP (25,587) server-first
 # - HTTPS (443) opaque TLS
 # - MYSQL (3306) server-first
 # - Memcached (11211) clients do not issue any preamble, which breaks detection
 ignoreInboundPorts: "25,443,587,3306,11211"
+# -- Outbound ports the proxy should ignore
 ignoreOutboundPorts: "25,443,587,3306,11211"
-
+# -- Annotation label for the proxy create. Do not edit. 
 createdByAnnotation: linkerd.io/created-by
-cniPluginImage: "ghcr.io/linkerd/cni-plugin"
+# -- Docker image for the CNI plugin
+cniPluginImage:   "ghcr.io/linkerd/cni-plugin"
+# -- Tag for the CNI container Docker image
 cniPluginVersion: linkerdVersionValue
-logLevel: info
-portsToRedirect: ""
-proxyUID: 2102
-destCNINetDir: "/etc/cni/net.d"
-destCNIBinDir: "/opt/cni/bin"
-useWaitFlag: false
+# -- Log level for the CNI plugin
+logLevel:         info
+# -- Ports to redirect to proxy 
+portsToRedirect:  ""
+# -- User id under which the proxy shall be ran
+proxyUID:         2102
+# -- Directory on the host where the CNI plugin binaries reside 
+destCNINetDir:    "/etc/cni/net.d"
+# -- Directory on the host where the CNI configuration will be placed
+destCNIBinDir:    "/opt/cni/bin"
+# -- Configures the CNI plugin to use the -w flag for the iptables command
+useWaitFlag:      false
+# -- Kubernetes priorityClassName for the CNI plugin's Pods
 priorityClassName: ""
 
 # namespace annotation and labels - do not edit
diff --git a/charts/linkerd2-multicluster-link/Chart.yaml b/charts/linkerd2-multicluster-link/Chart.yaml
index c6ec380a9..3ab84bafd 100644
--- a/charts/linkerd2-multicluster-link/Chart.yaml
+++ b/charts/linkerd2-multicluster-link/Chart.yaml
@@ -1,6 +1,8 @@
 apiVersion: v1
 appVersion: edge-XX.X.X
-description: A helm chart containing the resources to enable mirroring of services from a remote cluster
+description: |
+  A helm chart containing the resources to enable mirroring
+  of services from a remote cluster
 kubeVersion: ">=1.13.0-0"
 icon: https://linkerd.io/images/logo-only-200h.png
 name: "linkerd2-multicluster-link"
diff --git a/charts/linkerd2-multicluster-link/README.md b/charts/linkerd2-multicluster-link/README.md
index 44beb067b..63f738595 100644
--- a/charts/linkerd2-multicluster-link/README.md
+++ b/charts/linkerd2-multicluster-link/README.md
@@ -1,40 +1,29 @@
+# linkerd2-multicluster-link
 
-# Linkerd2-multicluster-link Helm Chart
-
-Linkerd is a *service mesh*, designed to give platform-wide observability,
-reliability, and security without requiring configuration or code changes. This
-chart provides the components needed to enable communication between clusters.
-
-## Configuration
-
-The following table lists the configurable parameters of the
-linkerd2-multicluster chart and their default values.
-
-| Parameter                       | Description                                                                                 | Default                                      |
-|---------------------------------|---------------------------------------------------------------------------------------------|----------------------------------------------|
-|`controllerComponentLabel`       | Control plane label. Do not edit                                                            |`linkerd.io/control-plane-component`          |
-|`controllerImage`                | Docker image for the Service mirror component (uses the Linkerd controller image)           |`ghcr.io/linkerd/controller`                |
-|`controllerImageVersion`         | Tag for the Service Mirror container Docker image                                           |`latest version`                              |
-|`createdByAnnotation`            | Annotation label for the proxy create. Do not edit.                                         |`linkerd.io/created-by`                       |
-|`gateway`                        | If the gateway component should be installed                                                |`true`                                        |
-|`gatewayLocalProbePath`          | The path that will be used by the local liveness checks to ensure the gateway is alive      |`/health-local`                               |
-|`gatewayLocalProbePort`          | The port that will be used by the local liveness checks to ensure the gateway is alive      |`8888`                                        |
-|`gatewayName`                    | The name of the gateway that will be installed                                              |`linkerd-gateway`                             |
-|`gatewayNginxImage`              | The Nginx image                                                                             |`nginx`                                       |
-|`gatewayNginxImageVersion`       | The version of the Nginx image                                                              |`1.17`                                        |
-|`gatewayPort`                    | The port on which all the gateway will accept incoming traffic                              |`4143`                                        |
-|`gatewayProbePath`               | The path that will be used by remote clusters for determining whether the gateway is alive  |`/health`                                     |
-|`gatewayProbePort`               | The port used for liveliness probing                                                        |`4181`                                        |
-|`gatewayProbeSeconds`            | The interval (in seconds) between liveness probes                                           |`3`                                           |
-|`identityTrustDomain`            | Trust domain used for identity of the existing linkerd installation                         |`cluster.local`                               |
-|`installNamespace`               | If the namespace should be installed                                                        |`true`                                        |
-|`linkerdNamespace`               | The namespace of the existing Linkerd installation                                          |`linkerd`                                     |
-|`linkerdVersion`                 | Control plane version                                                                       | latest version                               |
-|`namespace`                      | Service Mirror component namespace                                                          |`linkerd-multicluster`                        |
-|`proxyOutboundPort`              | The port on which the proxy accepts outbound traffic                                        |`4140`                                        |
-|`remoteMirrorServiceAccountName` | The name of the service account used to allow remote clusters to mirror local services      |`linkerd-service-mirror-remote-access-default`|
-|`remoteMirrorServiceAccount`     | If the remote mirror service account should be installed                                    |`true`                                        |
-|`serviceMirror`                  | If the service mirror component should be installed                                         |`true`                                        |
-|`logLevel`                       | Log level for the Multicluster components                                                   |`info`                                        |
-|`serviceMirrorRetryLimit`        | Number of times update from the remote cluster is allowed to be requeued (retried)          |`3`                                           |
-|`serviceMirrorUID`               | User id under which the Service Mirror shall be ran                                         |`2103`                                        |
+A helm chart containing the resources to enable mirroring
+of services from a remote cluster
+
+![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square)
+
+![AppVersion: edge-XX.X.X](https://img.shields.io/badge/AppVersion-edge--XX.X.X-informational?style=flat-square)
+
+## Requirements
+
+Kubernetes: `>=1.13.0-0`
+
+## Values
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| controllerComponentLabel | string | `"linkerd.io/control-plane-component"` | Control plane label. Do not edit  |
+| controllerImage | string | `"ghcr.io/linkerd/controller"` | Docker image for the Service mirror component (uses the Linkerd controller image) |
+| controllerImageVersion | string | `"linkerdVersionValue"` | Tag for the Service Mirror container Docker image |
+| createdByAnnotation | string | `"linkerd.io/created-by"` | Annotation label for the proxy create. Do not edit. |
+| gatewayProbePort | int | `4181` | The port used for liveliness probing  |
+| logLevel | string | `"info"` | Log level for the Multicluster components  |
+| namespace | string | `"linkerd-multicluster"` | Service Mirror component namespace  |
+| serviceMirrorRetryLimit | int | `3` | Number of times update from the remote cluster is allowed to be requeued (retried) |
+| serviceMirrorUID | int | `2103` | User id under which the Service Mirror shall be ran  |
+
+----------------------------------------------
+Autogenerated from chart metadata using [helm-docs v1.4.0](https://github.com/norwoodj/helm-docs/releases/v1.4.0)
diff --git a/charts/linkerd2-multicluster-link/README.md.gotmpl b/charts/linkerd2-multicluster-link/README.md.gotmpl
new file mode 100644
index 000000000..37f510106
--- /dev/null
+++ b/charts/linkerd2-multicluster-link/README.md.gotmpl
@@ -0,0 +1,14 @@
+{{ template "chart.header" . }}
+{{ template "chart.description" . }}
+
+{{ template "chart.versionBadge" . }}
+{{ template "chart.typeBadge" . }}
+{{ template "chart.appVersionBadge" . }}
+
+{{ template "chart.homepageLine" . }}
+
+{{ template "chart.requirementsSection" . }}
+
+{{ template "chart.valuesSection" . }}
+
+{{ template "helm-docs.versionFooter" . }}
diff --git a/charts/linkerd2-multicluster-link/values.yaml b/charts/linkerd2-multicluster-link/values.yaml
index 310dff194..c4acacb2a 100644
--- a/charts/linkerd2-multicluster-link/values.yaml
+++ b/charts/linkerd2-multicluster-link/values.yaml
@@ -1,9 +1,20 @@
+# -- Control plane label. Do not edit 
 controllerComponentLabel: linkerd.io/control-plane-component
+# -- Docker image for the Service mirror component (uses the Linkerd controller
+# image)
 controllerImage: ghcr.io/linkerd/controller
+# -- Tag for the Service Mirror container Docker image
 controllerImageVersion: linkerdVersionValue
+# --  Annotation label for the proxy create. Do not edit.
 createdByAnnotation: linkerd.io/created-by
+# -- The port used for liveliness probing 
 gatewayProbePort: 4181
+# -- Service Mirror component namespace 
 namespace: linkerd-multicluster
+# -- Log level for the Multicluster components 
 logLevel: info
+# -- Number of times update from the remote cluster is allowed to be requeued
+# (retried)
 serviceMirrorRetryLimit: 3
+# -- User id under which the Service Mirror shall be ran 
 serviceMirrorUID: 2103
diff --git a/charts/linkerd2-multicluster/Chart.yaml b/charts/linkerd2-multicluster/Chart.yaml
index 0870b6926..2405acdea 100644
--- a/charts/linkerd2-multicluster/Chart.yaml
+++ b/charts/linkerd2-multicluster/Chart.yaml
@@ -1,6 +1,8 @@
 apiVersion: v1
 appVersion: edge-XX.X.X
-description: A helm chart containing the resources to support multicluster linking to remote clusters
+description: |
+  A helm chart containing the resources to support multicluster
+  linking to remote clusters
 kubeVersion: ">=1.13.0-0"
 icon: https://linkerd.io/images/logo-only-200h.png
 name: "linkerd2-multicluster"
diff --git a/charts/linkerd2-multicluster/README.md b/charts/linkerd2-multicluster/README.md
index 1c18c415b..79a3225f5 100644
--- a/charts/linkerd2-multicluster/README.md
+++ b/charts/linkerd2-multicluster/README.md
@@ -1,42 +1,39 @@
+# linkerd2-multicluster
 
-# Linkerd2-multicluster Helm Chart
-
-Linkerd is a *service mesh*, designed to give platform-wide observability,
-reliability, and security without requiring configuration or code changes. This
-chart provides the components needed to enable communication between clusters.
-
-## Configuration
-
-The following table lists the configurable parameters of the
-linkerd2-multicluster chart and their default values.
-
-| Parameter                       | Description                                                                                 | Default                                      |
-|---------------------------------|---------------------------------------------------------------------------------------------|----------------------------------------------|
-|`controllerComponentLabel`       | Control plane label. Do not edit                                                            |`linkerd.io/control-plane-component`          |
-|`controllerImage`                | Docker image for the Service mirror component (uses the Linkerd controller image)           |`ghcr.io/linkerd/controller`                  |
-|`controllerImageVersion`         | Tag for the Service Mirror container Docker image                                           |`latest version`                              |
-|`createdByAnnotation`            | Annotation label for the proxy create. Do not edit.                                         |`linkerd.io/created-by`                       |
-|`gateway`                        | If the gateway component should be installed                                                |`true`                                        |
-|`gatewayLocalProbePath`          | The path that will be used by the local liveness checks to ensure the gateway is alive      |`/health-local`                               |
-|`gatewayLocalProbePort`          | The port that will be used by the local liveness checks to ensure the gateway is alive      |`8888`                                        |
-|`gatewayName`                    | The name of the gateway that will be installed                                              |`linkerd-gateway`                             |
-|`gatewayNginxImage`              | The Nginx image                                                                             |`nginx`                                       |
-|`gatewayNginxImageVersion`       | The version of the Nginx image                                                              |`1.17`                                        |
-|`gatewayPort`                    | The port on which all the gateway will accept incoming traffic                              |`4143`                                        |
-|`gatewayProbePath`               | The path that will be used by remote clusters for determining whether the gateway is alive  |`/health`                                     |
-|`gatewayProbePort`               | The port used for liveliness probing                                                        |`4181`                                        |
-|`gatewayProbeSeconds`            | The interval (in seconds) between liveness probes                                           |`3`                                           |
-|`gatewayServiceAnnotations`      | Additional annotations to add to the gateway service                                        |`{}`                                          |
-|`identityTrustDomain`            | Trust domain used for identity of the existing linkerd installation                         |`cluster.local`                               |
-|`installNamespace`               | If the namespace should be installed                                                        |`true`                                        |
-|`linkerdNamespace`               | The namespace of the existing Linkerd installation                                          |`linkerd`                                     |
-|`linkerdVersion`                 | Control plane version                                                                       | latest version                               |
-|`namespace`                      | Service Mirror component namespace                                                          |`linkerd-multicluster`                        |
-|`proxyOutboundPort`              | The port on which the proxy accepts outbound traffic                                        |`4140`                                        |
-|`remoteMirrorServiceAccountName` | The name (or list of names) of the service account(s) used to allow remote clusters to mirror local services      |`linkerd-service-mirror-remote-access-default`|
-|`remoteMirrorServiceAccount`     | If the remote mirror service account should be installed                                    |`true`                                        |
-|`serviceMirror`                  | If the service mirror component should be installed                                         |`true`                                        |
-|`logLevel`                       | Log level for the Multicluster components                                                   |`info`                                        |
-|`serviceMirrorRetryLimit`        | Number of times update from the remote cluster is allowed to be requeued (retried)          |`3`                                           |
-|`serviceMirrorUID`               | User id under which the Service Mirror shall be ran                                         |`2103`                                        |
-|`loadBalancerIP`               | Request a specific IP address for the gateway service (e.g. use an IP address you have reserved)                                  |emptyString                                        |
+A helm chart containing the resources to support multicluster
+linking to remote clusters
+
+![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square)
+
+![AppVersion: edge-XX.X.X](https://img.shields.io/badge/AppVersion-edge--XX.X.X-informational?style=flat-square)
+
+## Requirements
+
+Kubernetes: `>=1.13.0-0`
+
+## Values
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| controllerComponentLabel | string | `"linkerd.io/control-plane-component"` | Control plane label. Do not edit |
+| createdByAnnotation | string | `"linkerd.io/created-by"` | Annotation label for the proxy create. Do not edit. |
+| gateway | bool | `true` | If the gateway component should be installed |
+| gatewayLocalProbePath | string | `"/health-local"` | The path that will be used by the local liveness checks to ensure the gateway is alive |
+| gatewayLocalProbePort | int | `8888` | The port that will be used by the local liveness checks to ensure the gateway is alive |
+| gatewayName | string | `"linkerd-gateway"` | The name of the gateway that will be installed |
+| gatewayNginxImage | string | `"nginx"` | The Nginx image |
+| gatewayNginxImageVersion | float | `1.17` | The version of the Nginx image |
+| gatewayPort | int | `4143` | The port on which all the gateway will accept incoming traffic |
+| gatewayProbePath | string | `"/health"` | The path that will be used by remote clusters for determining whether the gateway is alive |
+| gatewayProbePort | int | `4181` | The port used for liveliness probing |
+| gatewayProbeSeconds | int | `3` | The interval (in seconds) between liveness probes |
+| gatewayServiceType | string | `"LoadBalancer"` | Service Type of gateway Service |
+| installNamespace | bool | `true` | If the namespace should be installed |
+| linkerdVersion | string | `"linkerdVersionValue"` | Control plane version |
+| namespace | string | `"linkerd-multicluster"` | Service Mirror component namespace |
+| proxyOutboundPort | int | `4140` | The port on which the proxy accepts outbound traffic |
+| remoteMirrorServiceAccount | bool | `true` | If the remote mirror service account should be installed |
+| remoteMirrorServiceAccountName | string | `"linkerd-service-mirror-remote-access-default"` | The name of the service account used to allow remote clusters to mirror local services |
+
+----------------------------------------------
+Autogenerated from chart metadata using [helm-docs v1.4.0](https://github.com/norwoodj/helm-docs/releases/v1.4.0)
diff --git a/charts/linkerd2-multicluster/README.md.gotmpl b/charts/linkerd2-multicluster/README.md.gotmpl
new file mode 100644
index 000000000..37f510106
--- /dev/null
+++ b/charts/linkerd2-multicluster/README.md.gotmpl
@@ -0,0 +1,14 @@
+{{ template "chart.header" . }}
+{{ template "chart.description" . }}
+
+{{ template "chart.versionBadge" . }}
+{{ template "chart.typeBadge" . }}
+{{ template "chart.appVersionBadge" . }}
+
+{{ template "chart.homepageLine" . }}
+
+{{ template "chart.requirementsSection" . }}
+
+{{ template "chart.valuesSection" . }}
+
+{{ template "helm-docs.versionFooter" . }}
diff --git a/charts/linkerd2-multicluster/templates/gateway.yaml b/charts/linkerd2-multicluster/templates/gateway.yaml
index 9f45c70ae..4ecef8d4a 100644
--- a/charts/linkerd2-multicluster/templates/gateway.yaml
+++ b/charts/linkerd2-multicluster/templates/gateway.yaml
@@ -133,7 +133,7 @@ spec:
     protocol: TCP
   selector:
     app: {{.Values.gatewayName}}
-  type: LoadBalancer
+  type: {{ .Values.gatewayServiceType }}
 {{- if .Values.loadBalancerIP }}
   loadBalancerIP: {{ .Values.loadBalancerIP }}
 {{- end }}
diff --git a/charts/linkerd2-multicluster/values.yaml b/charts/linkerd2-multicluster/values.yaml
index 6522e7107..e89dc8996 100644
--- a/charts/linkerd2-multicluster/values.yaml
+++ b/charts/linkerd2-multicluster/values.yaml
@@ -1,18 +1,42 @@
+# -- Control plane label. Do not edit
 controllerComponentLabel: linkerd.io/control-plane-component
+# -- Annotation label for the proxy create. Do not edit.
 createdByAnnotation: linkerd.io/created-by
+# -- If the gateway component should be installed
 gateway: true
+# -- The path that will be used by the local liveness checks to ensure the
+# gateway is alive
 gatewayLocalProbePath: /health-local
+# -- The port that will be used by the local liveness checks to ensure the
+# gateway is alive
 gatewayLocalProbePort: 8888
+# -- The name of the gateway that will be installed
 gatewayName: linkerd-gateway
+# -- The Nginx image
 gatewayNginxImage: nginx
+# -- The version of the Nginx image
 gatewayNginxImageVersion: 1.17
+# -- The port on which all the gateway will accept incoming traffic
 gatewayPort: 4143
+# -- The path that will be used by remote clusters for determining whether the
+# gateway is alive
 gatewayProbePath: /health
+# -- The port used for liveliness probing
 gatewayProbePort: 4181
+# -- The interval (in seconds) between liveness probes
 gatewayProbeSeconds: 3
+# -- If the namespace should be installed
 installNamespace: true
+# -- Control plane version
 linkerdVersion: linkerdVersionValue
+# -- Service Mirror component namespace
 namespace: linkerd-multicluster
+# -- The port on which the proxy accepts outbound traffic
 proxyOutboundPort: 4140
+# -- If the remote mirror service account should be installed
 remoteMirrorServiceAccount: true
+# -- The name of the service account used to allow remote clusters to mirror
+# local services
 remoteMirrorServiceAccountName: linkerd-service-mirror-remote-access-default
+# -- Service Type of gateway Service
+gatewayServiceType: LoadBalancer
diff --git a/charts/linkerd2/Chart.yaml b/charts/linkerd2/Chart.yaml
index ce7ee5f95..cbfc8b38b 100644
--- a/charts/linkerd2/Chart.yaml
+++ b/charts/linkerd2/Chart.yaml
@@ -1,7 +1,9 @@
 apiVersion: "v1"
 # this version will be updated by the CI before publishing the Helm tarball
 appVersion: edge-XX.X.X
-description: Linkerd gives you observability, reliability, and security for your microservices — with no code change required.
+description: |
+  Linkerd gives you observability, reliability, and security
+  for your microservices — with no code change required.
 home: https://linkerd.io
 keywords:
 - service-mesh
diff --git a/charts/linkerd2/README.md b/charts/linkerd2/README.md
index 14bbd69ff..e3a468243 100644
--- a/charts/linkerd2/README.md
+++ b/charts/linkerd2/README.md
@@ -1,9 +1,13 @@
-# Linkerd2 Helm Chart
+# linkerd2
 
-Linkerd is a *service mesh*, designed to give platform-wide observability,
-reliability, and security without requiring configuration or code changes.
+Linkerd gives you observability, reliability, and security
+for your microservices — with no code change required.
 
-Linkerd is a Cloud Native Computing Foundation ([CNCF][cncf]) project.
+![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square)
+
+![AppVersion: edge-XX.X.X](https://img.shields.io/badge/AppVersion-edge--XX.X.X-informational?style=flat-square)
+
+**Homepage:** <https://linkerd.io>
 
 ## Quickstart and documentation
 
@@ -29,7 +33,6 @@ Note that the provided certificates must be ECDSA certificates.
 ```bash
 # To add the repo for Linkerd2 stable releases:
 helm repo add linkerd https://helm.linkerd.io/stable
-
 # To add the repo for Linkerd2 edge releases:
 helm repo add linkerd-edge https://helm.linkerd.io/edge
 ```
@@ -79,179 +82,6 @@ helm install \
   linkerd/linkerd2
 ```
 
-## Configuration
-
-The following table lists the configurable parameters of the Linkerd2 chart and
-their default values.
-
-| Parameter                                   | Description                                                                                                                                                                           | Default                              |
-|:--------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------|
-| `controllerImage`                           | Docker image for the controller, tap and identity components                                                                                                                          | `ghcr.io/linkerd/controller`       |
-| `controllerReplicas`                        | Number of replicas for each control plane pod                                                                                                                                         | `1`                                  |
-| `controllerUID`                             | User ID for the control plane components                                                                                                                                              | `2103`                               |
-| `dashboard.replicas`                        | Number of replicas of dashboard                                                                                                                                                       | `1`                                  |
-| `debugContainer.image.name`                 | Docker image for the debug container                                                                                                                                                  | `ghcr.io/linkerd/debug`            |
-| `debugContainer.image.pullPolicy`           | Pull policy for the debug container Docker image                                                                                                                                      | `IfNotPresent`                       |
-| `debugContainer.image.version`              | Tag for the debug container Docker image                                                                                                                                              | latest version                       |
-| `destinationResources`                      | CPU and Memory resources required by destination (see `global.proxy.resources` for sub-fields)             |   |
-| `destinationProxyResources`                 | CPU and Memory resources required by proxy injected into destination pod (see `global.proxy.resources` for sub-fields)             | values in `global.proxy.resources`   |
-| `disableHeartBeat`                          | Set to true to not start the heartbeat cronjob                                                                                                                                        | `false`                              |
-| `enableH2Upgrade`                           | Allow proxies to perform transparent HTTP/2 upgrading                                                                                                                                 | `true`                               |
-| `global.clusterDomain`                      | Kubernetes DNS Domain name to use                                                                                                                                                     | `cluster.local`                      |
-| `global.clusterNetworks`                    | The networks that may include pods & services in this cluscter                                                                                                                         | `10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16` |
-| `global.cniEnabled`                         | Omit the NET_ADMIN capability in the PSP and the proxy-init container when injecting the proxy; requires the linkerd-cni plugin to already be installed                               | `false`                              |
-| `global.controllerComponentLabel`           | Control plane label. Do not edit                                                                                                                                                      | `linkerd.io/control-plane-component` |
-| `global.controllerImageVersion`             | Tag for the controller container docker image                                                                                                                                         | latest version                       |
-| `global.controllerLogLevel`                 | Log level for the control plane components                                                                                                                                            | `info`                               |
-| `global.controllerNamespaceLabel`           | Control plane label. Do not edit                                                                                                                                                      | `linkerd.io/control-plane-ns`        |
-| `global.grafanaUrl`                          | URL of external grafana instance configured with reverse proxy, used by the dashboard                                                                                                                                      |                                 |
-| `global.podLabels`                          | Additional labels to add to all pods                                                                                                                                                  | `{}`                                 |
-| `global.podAnnotations`                     | Additional annotations to add to all pods                                                                                                                                             | `{}`                                 |
-| `global.createdByAnnotation`                | Annotation label for the proxy create. Do not edit.                                                                                                                                   | `linkerd.io/created-by`              |
-| `global.identityTrustAnchorsPEM`            | Trust root certificate (ECDSA). It must be provided during install.                                                                                                                   |                                      |
-| `global.identityTrustDomain`                | Trust domain used for identity                                                                                                                                                        | `cluster.local`                      |
-| `global.imagePullPolicy`                    | Docker image pull policy                                                                                                                                                              | `IfNotPresent`                       |
-| `global.linkerdNamespaceLabel`              | Control plane label. Do not edit                                                                                                                                                      | `linkerd.io/is-control-plane` |
-| `global.linkerdVersion`                     | Control plane version                                                                                                                                                                 | latest version                       |
-| `global.namespace`                          | Control plane namespace                                                                                                                                                               | `linkerd`                            |
-| `global.prometheusUrl`     | URL of external prometheus instance to perform queries, used by the `public-api`                                                                                                                    |                               |
-| `global.proxy.cores`                        | The number of proxy threads to be allocated for each proxy. Must be a whole number, and should be kept in sync with `global.proxy.resources.cpu.limit`, if set.                       |                                      |
-| `global.proxy.enableExternalProfiles`       | Enable service profiles for non-Kubernetes services                                                                                                                                   | `false`                              |
-| `global.proxy.image.name`                   | Docker image for the proxy                                                                                                                                                            | `ghcr.io/linkerd/proxy`            |
-| `global.proxy.image.pullPolicy`             | Pull policy for the proxy container Docker image                                                                                                                                      | `IfNotPresent`                       |
-| `global.proxy.image.version`                | Tag for the proxy container Docker image                                                                                                                                              | latest version                       |
-| `global.proxy.logLevel`                     | Log level for the proxy                                                                                                                                                               | `warn,linkerd=info`                  |
-| `global.proxy.logFormat`                     | Log format (`plain` or `json`) for the proxy                                                                                                                                                               | `plain`                  |
-| `global.proxy.ports.admin`                  | Admin port for the proxy container                                                                                                                                                    | `4191`                               |
-| `global.proxy.ports.control`                | Control port for the proxy container                                                                                                                                                  | `4190`                               |
-| `global.proxy.ports.inbound`                | Inbound port for the proxy container                                                                                                                                                  | `4143`                               |
-| `global.proxy.ports.outbound`               | Outbound port for the proxy container                                                                                                                                                 | `4140`                               |
-| `global.proxy.resources.cpu.limit`          | Maximum amount of CPU units that the proxy can use                                                                                                                                    |                                      |
-| `global.proxy.resources.cpu.request`        | Amount of CPU units that the proxy requests                                                                                                                                           |                                      |
-| `global.proxy.resources.memory.limit`       | Maximum amount of memory that the proxy can use                                                                                                                                       |                                      |
-| `global.proxy.resources.memory.request`     | Amount of memory that the proxy requests                                                                                                                                              |                                      |
-| `global.proxy.trace.collectorSvcAccount`    | Service account associated with the Trace collector instance                                                                                                                          | `default`                            |
-| `global.proxy.trace.collectorSvcAddr`       | Collector Service address for the proxies to send Trace Data                                                                                                                          |                                      |
-| `global.proxy.uid`                          | User id under which the proxy runs                                                                                                                                                    | `2102`                               |
-| `global.proxy.waitBeforeExitSeconds`        | The proxy sidecar will stay alive for at least the given period before receiving SIGTERM signal from Kubernetes but no longer than pod's `terminationGracePeriodSeconds`.             | `0`                                  |
-| `global.proxy.outboundConnectTimeout`       | Maximum time allowed for the proxy to establish an outbound TCP connection                                                                                                            | `1000ms`                             |
-| `global.proxy.inboundConnectTimeout`        | Maximum time allowed for the proxy to establish an inbound TCP connection                                                                                                             | `100ms`                              |
-| `global.proxyInit.ignoreInboundPorts`       | Inbound ports the proxy should ignore                                                                                                                                                 | `25,443,587,3306,11211`              |
-| `global.proxyInit.ignoreOutboundPorts`      | Outbound ports the proxy should ignore                                                                                                                                                | `25,443,587,3306,11211`              |
-| `global.proxyInit.image.name`               | Docker image for the proxy-init container                                                                                                                                             | `ghcr.io/linkerd/proxy-init`       |
-| `global.proxyInit.image.pullPolicy`         | Pull policy for the proxy-init container Docker image                                                                                                                                 | `IfNotPresent`                       |
-| `global.proxyInit.image.version`            | Tag for the proxy-init container Docker image                                                                                                                                         | latest version                       |
-| `global.proxyInit.resources.cpu.limit`      | Maximum amount of CPU units that the proxy-init container can use                                                                                                                     | `100m`                               |
-| `global.proxyInit.resources.cpu.request`    | Amount of CPU units that the proxy-init container requests                                                                                                                            | `10m`                                |
-| `global.ProxyInit.resources.memory.limit`   | Maximum amount of memory that the proxy-init container can use                                                                                                                        | `50Mi`                               |
-| `global.proxyInit.resources.memory.request` | Amount of memory that the proxy-init container requests                                                                                                                               | `10Mi`                               |
-| `global.proxyInjectAnnotation`              | Annotation label to signal injection. Do not edit.                                                                                                                                    | `linkerd.io/inject`                                    |
-| `global.proxyInjectDisabled`                | Annotation value to disable injection. Do not edit.                                                                                                                                   | `disabled`                           |
-| `heartbeatSchedule`                         | Config for the heartbeat cronjob                                                                                                                                                      | `0 0 * * *`                          |
-| `identity.issuer.clockSkewAllowance`        | Amount of time to allow for clock skew within a Linkerd cluster                                                                                                                       | `20s`                                |
-| `identity.issuer.crtExpiry`                 | Expiration timestamp for the issuer certificate. It must be provided during install                                                                                                   |                                      |
-| `identity.issuer.crtExpiryAnnotation`       | Annotation used to identity the issuer certificate expiration timestamp. Do not edit.                                                                                                 | `linkerd.io/identity-issuer-expiry`  |
-| `identity.issuer.issuanceLifetime`          | Amount of time for which the Identity issuer should certify identity                                                                                                                  | `24h0m0s`                             |
-| `identity.issuer.scheme`                    | Which scheme is used for the identity issuer secret format                                                                                                                            | `linkerd.io/tls`                     |
-| `identity.issuer.tls.crtPEM`                | Issuer certificate (ECDSA). It must be provided during install.                                                                                                                       |                                      |
-| `identity.issuer.tls.keyPEM`                | Key for the issuer certificate (ECDSA). It must be provided during install.                                                                                                           |                                      |
-| `identityResources`                         | CPU and Memory resources required by the identity controller (see `global.proxy.resources` for sub-fields)             |   |
-| `identityProxyResources`                    | CPU and Memory resources required by proxy injected into identity pod (see `global.proxy.resources` for sub-fields)             | values in `global.proxy.resources`   |
-| `installNamespace`                          | Set to false when installing Linkerd in a custom namespace. See the [Linkerd documentation](https://linkerd.io/2/tasks/install-helm/#customizing-the-namespace) for more information. | `true`                               |
-| `omitWebhookSideEffects`                    | Omit the `sideEffects` flag in the webhook manifests                                                                                                                                  | `false`                              |
-| `proxyInjector.externalSecret`              | Do not create a secret resource for the profileValidator webhook. If this is set to `true`, the value `proxyInjector.caBundle` must be set (see below).                               | `false`                              |
-| `proxyInjector.namespaceSelector`           | Namespace selector used by admission webhook. If not set defaults to all namespaces without the annotation `config.linkerd.io/admission-webhooks=disabled`                            |                                      |
-| `proxyInjector.crtPEM`                      | Certificate for the proxy injector. If not provided then Helm will generate one.                                                                                                      |                                      |
-| `proxyInjector.keyPEM`                      | Certificate key for the proxy injector. If not provided then Helm will generate one.                                                                                                      |                                      |
-| `proxyInjector.caBundle`                    | Bundle of CA certificates for proxy injector. If not provided then Helm will use the certificate generated  for `proxyInjector.crtPEM`. If `proxyInjector.externalSecret` is set to true, this value must be set, as no certificate will be generated.        |   |
-| `proxyInjectorResources`                    | CPU and Memory resources required by the proxy injector (see `global.proxy.resources` for sub-fields)             |   |
-| `proxyInjectorProxyResources`               | CPU and Memory resources required by proxy injected into the proxy injector pod (see `global.proxy.resources` for sub-fields)             | values in `global.proxy.resources`   |
-| `profileValidator.externalSecret`           | Do not create a secret resource for the profileValidator webhook. If this is set to `true`, the value `profileValidator.caBundle` must be set (see below).                            | false                                |
-| `profileValidator.namespaceSelector`        | Namespace selector used by admission webhook. If not set defaults to all namespaces without the annotation `config.linkerd.io/admission-webhooks=disabled`                            |                                      |
-| `profileValidator.crtPEM`                   | Certificate for the service profile validator. If not provided then Helm will generate one.                                                                                           |                                      |
-| `profileValidator.keyPEM`                   | Certificate key for the service profile validator. If not provided then Helm will generate one.                                                                                       |                                      |
-| `profileValidator.caBundle`                 | Bundle of CA certificates for service profile validator. If not provided then Helm will use the certificate generated  for `profileValidator.crtPEM`. If `profileValidator.externalSecret` is set to true, this value must be set, as no certificate will be generated.         |  |
-| `publicAPIResources`                        | CPU and Memory resources required by controllers publicAPI (see `global.proxy.resources` for sub-fields)             |   |
-| `publicAPIProxyResources`                   | CPU and Memory resources required by proxy injected into controllers public API pod (see `global.proxy.resources` for sub-fields)             |  values  `global.proxy.resources`   |
-| `spValidatorResources`                      | CPU and Memory resources required by the SP validator (see `global.proxy.resources` for sub-fields)             |   |
-| `spValidatorProxyResources`                 | CPU and Memory resources required by proxy injected into the SP validator pod (see `global.proxy.resources` for sub-fields)             | values in `global.proxy.resources`   |
-| `tap.externalSecret`                        | Do not create a secret resource for the Tap component. If this is set to `true`, the value `tap.caBundle` must be set (see below).                                                  | false                                |
-| `tap.crtPEM`                                | Certificate for the Tap component. If not provided then Helm will generate one.                                                                                                       |                                      |
-| `tap.keyPEM`                                | Certificate key for Tap component. If not provided then Helm will generate one.                                                                                                       |                                      |
-| `tap.caBundle`                              | Bundle of CA certificates for Tap component. If not provided then Helm will use the certificate generated  for `tap.crtPEM`. If `tap.externalSecret` is set to true, this value must be set, as no certificate will be generated.                       ||
-| `tapResources`                              | CPU and Memory resources required by tap (see `global.proxy.resources` for sub-fields)             |   |
-| `tapProxyResources`                         | CPU and Memory resources required by proxy injected into tap pod (see `global.proxy.resources` for sub-fields)             | values in `global.proxy.resources`   |
-| `webhookFailurePolicy`                      | Failure policy for the proxy injector                                                                                                                                                 | `Ignore`                             |
-| `webImage`                                  | Docker image for the web container                                                                                                                                                    | `ghcr.io/linkerd/web`              |
-| `webResources`                              | CPU and Memory resources required by web UI (see `global.proxy.resources` for sub-fields)             |   |
-| `webProxyResources`                         | CPU and Memory resources required by proxy injected into web UI pod (see `global.proxy.resources` for sub-fields)             | values in `global.proxy.resources`   |
-| `enforcedHostRegexp`                        | Host header validation regex for the dashboard. See the [Linkerd documentation](https://linkerd.io/2/tasks/exposing-dashboard) for more information                                   | `""`                                 |
-| `nodeSelector`                        | NodeSelector section, See the [K8S documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) for more information                                   | `beta.kubernetes.io/os: linux`                                 |
-| `tolerations`                        | Tolerations section, See the [K8S documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) for more information                                   |                                  |
-
-## Add-Ons Configuration
-
-### Grafana Add-On
-
-The following table lists the configurable parameters for the Grafana Add-On.
-
-| Parameter                             | Description                                                                                                                                                                           | Default                              |
-|:--------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------|
-| `grafana.enabled`                     | Flag to enable grafana instance to be installed                                                                                                                                                | `true`
-| `grafana.image.name`                | Docker image name for the grafana instance                                                                                                                                                 | `ghcr.io/linkerd/grafana`                             |
-| `grafana.image.tag`                | Docker image tag for the grafana instance                                                                                                                                                 | latest version                              |
-| `grafana.resources.cpu.limit`       | Maximum amount of CPU units that the grafana container can use                                                                                                                     ||
-| `grafana.resources.cpu.request`     | Amount of CPU units that the grafana container requests                                                                                                                            ||
-| `grafana.resources.memory.limit`    | Maximum amount of memory that grafana container can use                                                                                                                        ||
-| `grafana.resources.memory.request`  | Amount of memory that the grafana container requests                                                                                                                               ||
-| `grafana.proxy.resources`           | Structure analog to the `resources` fields above, but overriding the resources of the linkerd proxy injected into the grafana pod.   | values in `global.proxy.resources` of the linkerd2 chart. |
-
-### Prometheus Add-On
-
-The following table lists the configurable parameters for the Prometheus Add-On.
-
-| Parameter                             | Description                                                                                                                                                                           | Default                              |
-|:--------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------|
-| `prometheus.enabled`                     | Flag to enable prometheus instance to be installed                                                                                                                                                | `true`                             |
-| `prometheus.alert_relabel_configs`                   | Alert relabeling is applied to alerts before they are sent to the Alertmanager.                                                                            | `[]`                                 |
-| `prometheus.alertManagers`                   | Alertmanager instances the Prometheus server sends alerts to configured via the static_configs parameter.                                                                             | `[]`                                 |
-| `prometheus.args`                       |  Command line options for Prometheus binary                                                                                                                                             | `storage.tsdb.path: /data, storage.tsdb.retention.time: 6h, config.file: /etc/prometheus/prometheus.yml, log.level: info`                                 |
-| `prometheus.globalConfig`             | The global configuration specifies parameters that are valid in all other configuration contexts.                                                                 | `scrape_interval: 10s, scrape_timeout: 10s, evaluation_interval: 10s`                                 |
-| `prometheus.image`                | Docker image for the prometheus instance                                                                                                                                                 | `prom/prometheus:v2.19.3`                             |
-| `prometheus.proxy.resources`                  | CPU and Memory resources required by proxy injected into prometheus pod (see `global.proxy.resources` for sub-fields)             | values in `global.proxy.resources`   |
-| `prometheus.persistence.storageClass`        | Storage class used to create prometheus data PV.                                                                                                                                                                                                                        | `nil`                                |
-| `prometheus.persistence.accessMode`          | PVC access mode.                                                                                                                                                                                                                                                        | `ReadWriteOnce`                      |
-| `prometheus.persistence.size`                | Prometheus data volume size.                                                                                                                                                                                                                                            | `8Gi`                                |
-| `prometheus.remoteWrite`       | Allows transparently sending samples to an endpoint. Mostly used for long term storage.                  ||
-| `prometheus.resources.cpu.limit`       | Maximum amount of CPU units that the prometheus container can use                                                                                                                     ||
-| `prometheus.resources.cpu.request`     | Amount of CPU units that the prometheus container requests                                                                                                                            ||
-| `prometheus.resources.memory.limit`    | Maximum amount of memory that prometheus container can use                                                                                                                        ||
-| `prometheus.resources.memory.request`  | Amount of memory that the prometheus container requests                                                                                                                               ||
-| `prometheus.ruleConfigMapMounts`             | Alerting/recording rule ConfigMap mounts (sub-path names must end in `_rules.yml` or `_rules.yaml`)                                                                                   | `[]`                                 |
-| `prometheus.scrapeConfigs`             | A scrape_config section specifies a set of targets and parameters describing how to scrape them.                                                        | `[]`                                 |
-| `prometheus.sidecarContainers`         | A sidecarContainers section specifies a list of secondary containers to run in the prometheus pod e.g. to export data to non-prometheus systems | `[]`                                 |
-
-Most of the above configuration match directly with the official Prometheus
-configuration which can be found [here](https://prometheus.io/docs/prometheus/latest/configuration/configuration)
-
-### Tracing Add-On
-
-The following table lists the configurable parameters for the Tracing Add-On.
-
-| Parameter                                    | Description                                                            | Default                                |
-|:---------------------------------------------|:-----------------------------------------------------------------------|:---------------------------------------|
-| `tracing.enabled`                            | Flag to enable tracing components to be installed                      | `false`                                |
-| `tracing.collector.image`                    | Docker image for the trace collector                                   | `omnition/opencensus-collector:0.1.10` |
-| `tracing.collector.resources.cpu.limit`      | Maximum amount of CPU units that the trace collector container can use |                                 |
-| `tracing.collector.resources.cpu.request`    | Amount of CPU units that the trace collector container requests        |                                  |
-| `tracing.collector.resources.memory.limit`   | Maximum amount of memory that the trace collector container can use    |                                  |
-| `tracing.collector.resources.memory.request` | Amount of memory that the trace collector container requests           |                                |
-| `tracing.jaeger.image`                       | Docker image for the jaeger instance                                   | `jaegertracing/all-in-one:1.19.2`         |
-| `tracing.jaeger.resources.cpu.limit`         | Maximum amount of CPU units that the jaeger container can use          |                                        |
-| `tracing.jaeger.resources.cpu.request`       | Amount of CPU units that the jaeger container requests                 |                                        |
-| `tracing.jaeger.resources.memory.limit`      | Maximum amount of memory that the jaeger container can use             |                                        |
-| `tracing.jaeger.resources.memory.request`    | Amount of memory that the jaeger container requests                    |                                        |
-
 ## Get involved
 
 * Check out Linkerd's source code at [Github][linkerd2].
@@ -269,3 +99,132 @@ The following table lists the configurable parameters for the Tracing Add-On.
 [linkerd-users]: https://lists.cncf.io/g/cncf-linkerd-users
 [slack]: http://slack.linkerd.io
 [twitter]: https://twitter.com/linkerd
+
+## Addons for linkerd
+
+For the linkerd application there are some addons that can be configured. The
+documentation for the configurations of the addons can be found in their
+respective readme.md
+
+[Prometheus](https://github.com/linkerd/linkerd2/blob/main/charts/add-ons/prometheus/README.md)
+
+[Grafana](https://github.com/linkerd/linkerd2/blob/main/charts/add-ons/grafana/README.md)
+
+[Tracing](https://github.com/linkerd/linkerd2/blob/main/charts/add-ons/tracing/README.md)
+
+## Requirements
+
+Kubernetes: `>=1.13.0-0`
+
+| Repository | Name | Version |
+|------------|------|---------|
+| file://../add-ons/grafana | grafana | 0.1.0 |
+| file://../add-ons/prometheus | prometheus | 0.1.0 |
+| file://../add-ons/tracing | tracing | 0.1.0 |
+| file://../partials | partials | 0.1.0 |
+
+## Values
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| controllerImage | string | `"ghcr.io/linkerd/controller"` | Docker image for the controller, tap and identity components |
+| controllerReplicas | int | `1` | Number of replicas for each control plane pod |
+| controllerUID | int | `2103` | User ID for the control plane components |
+| dashboard.replicas | int | `1` | Number of replicas of dashboard |
+| debugContainer.image.name | string | `"ghcr.io/linkerd/debug"` | Docker image for the debug container |
+| debugContainer.image.pullPolicy | string | `"IfNotPresent"` | Pull policy for the debug container Docker image |
+| debugContainer.image.version | string | `"linkerdVersionValue"` | Tag for the debug container Docker image |
+| disableHeartBeat | bool | `false` | Set to true to not start the heartbeat cronjob  |
+| enableH2Upgrade | bool | `true` | Allow proxies to perform transparent HTTP/2 upgrading   |
+| enforcedHostRegexp | string | `""` | Host header validation regex for the dashboard. See the [Linkerd documentation](https://linkerd.io/2/tasks/exposing-dashboard) for more information |
+| global.clusterDomain | string | `"cluster.local"` | Kubernetes DNS Domain name to use   |
+| global.clusterNetworks | string | `"10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16"` | The cluster networks for which service discovery is performed. This should include the pod network but need not include the node network. By default, all private networks are specified so that resolution works in typical Kubernetes environments. |
+| global.cniEnabled | bool | `false` | enabling this omits the NET_ADMIN capability in the PSP and the proxy-init container when injecting the proxy; requires the linkerd-cni plugin to already be installed |
+| global.controlPlaneTracing | bool | `false` | control plane trace configuration |
+| global.controllerComponentLabel | string | `"linkerd.io/control-plane-component"` | Control plane label. Do not edit |
+| global.controllerLogLevel | string | `"info"` | Log level for the control plane components |
+| global.controllerNamespaceLabel | string | `"linkerd.io/control-plane-ns"` | Control plane label. Do not edit   |
+| global.createdByAnnotation | string | `"linkerd.io/created-by"` | Annotation label for the proxy create. Do not edit.  |
+| global.enableEndpointSlices | bool | `false` | enables the use of EndpointSlice informers for the destination service; enableEndpointSlices should be set to true only if EndpointSlice K8s feature gate is on; the feature is still experimental. |
+| global.grafanaUrl | string | `""` | url of external grafana instance with reverse proxy configured. |
+| global.identityTrustAnchorsPEM | string | `""` | Trust root certificate (ECDSA). It must be provided during install.  |
+| global.identityTrustDomain | string | `"cluster.local"` | Trust domain used for identity  |
+| global.imagePullPolicy | string | `"IfNotPresent"` | Docker image pull policy  |
+| global.imagePullSecrets | list | `[]` | For Private docker registries, authentication is needed.  Registry secrets are applied to the respective service accounts |
+| global.linkerdNamespaceLabel | string | `"linkerd.io/is-control-plane"` | Control plane label. Do not edit  |
+| global.linkerdVersion | string | `"linkerdVersionValue"` | control plane version. See Proxy section for proxy version |
+| global.namespace | string | `"linkerd"` | Control plane namespace |
+| global.podAnnotations | object | `{}` | Additional annotations to add to all pods |
+| global.podLabels | object | `{}` | Additional labels to add to all pods |
+| global.prometheusUrl | string | `""` | url of existing prometheus |
+| global.proxy.cores | int | `0` | The `cpu.limit` and `cores` should be kept in sync. The value of `cores` must be an integer and should typically be set by rounding up from the limit. E.g. if cpu.limit is '1500m', cores should be 2. |
+| global.proxy.enableExternalProfiles | bool | `false` | Enable service profiles for non-Kubernetes services |
+| global.proxy.image.name | string | `"ghcr.io/linkerd/proxy"` | Docker image for the proxy |
+| global.proxy.image.pullPolicy | string | `"IfNotPresent"` | Pull policy for the proxy container Docker image |
+| global.proxy.image.version | string | `"linkerdVersionValue"` | Tag for the proxy container Docker image |
+| global.proxy.inboundConnectTimeout | string | `"100ms"` | Maximum time allowed for the proxy to establish an inbound TCP connection |
+| global.proxy.logFormat | string | `"plain"` | Log format (`plain` or `json`) for the proxy |
+| global.proxy.logLevel | string | `"warn,linkerd=info"` | Log level for the proxy |
+| global.proxy.outboundConnectTimeout | string | `"1000ms"` | Maximum time allowed for the proxy to establish an outbound TCP connection |
+| global.proxy.ports.admin | int | `4191` | Admin port for the proxy container |
+| global.proxy.ports.control | int | `4190` | Control port for the proxy container |
+| global.proxy.ports.inbound | int | `4143` | Inbound port for the proxy container |
+| global.proxy.ports.outbound | int | `4140` | Outbound port for the proxy container   |
+| global.proxy.requireIdentityOnInboundPorts | string | `""` |  |
+| global.proxy.resources.cpu.limit | string | `""` | Maximum amount of CPU units that the proxy can use  |
+| global.proxy.resources.cpu.request | string | `""` | Amount of CPU units that the proxy requests |
+| global.proxy.resources.memory.limit | string | `""` | Maximum amount of memory that the proxy can use  |
+| global.proxy.resources.memory.request | string | `""` | Maximum amount of memory that the proxy requests |
+| global.proxy.trace.collectorSvcAccount | string | `"default"` | Service account associated with the Trace collector instance  |
+| global.proxy.trace.collectorSvcAddr | string | `""` | Collector Service address for the proxies to send Trace Data  |
+| global.proxy.uid | int | `2102` | User id under which the proxy runs |
+| global.proxy.waitBeforeExitSeconds | int | `0` | If set the proxy sidecar will stay alive for at least the given period before receiving SIGTERM signal from Kubernetes but no longer than pod's `terminationGracePeriodSeconds`. See [Lifecycle hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks) for more info on container lifecycle hooks. |
+| global.proxyInit.closeWaitTimeoutSecs | int | `0` |  |
+| global.proxyInit.ignoreInboundPorts | string | `"25,443,587,3306,11211"` | Default set of ports to skip via itpables: - SMTP (25,587) server-first - HTTPS (443) opaque TLS - MYSQL (3306) server-first - Memcached (11211) clients do not issue any preamble, which breaks detection |
+| global.proxyInit.ignoreOutboundPorts | string | `"25,443,587,3306,11211"` | Default set of ports to skip via itpables, same defaults as InboudPorts |
+| global.proxyInit.image.name | string | `"ghcr.io/linkerd/proxy-init"` | Docker image for the proxy-init container |
+| global.proxyInit.image.pullPolicy | string | `"IfNotPresent"` | Pull policy for the proxy-init container Docker image |
+| global.proxyInit.image.version | string | `"v1.3.8"` | Tag for the proxy-init container Docker image |
+| global.proxyInit.resources.cpu.limit | string | `"100m"` | Maximum amount of CPU units that the proxy-init container can use |
+| global.proxyInit.resources.cpu.request | string | `"10m"` | Amount of CPU units that the proxy-init container requests |
+| global.proxyInit.resources.memory.limit | string | `"50Mi"` | Maximum amount of memory that the proxy-init container can use |
+| global.proxyInit.resources.memory.request | string | `"10Mi"` | Amount of memory that the proxy-init container requests |
+| global.proxyInit.xtMountPath.mountPath | string | `"/run"` |  |
+| global.proxyInit.xtMountPath.name | string | `"linkerd-proxy-init-xtables-lock"` |  |
+| global.proxyInjectAnnotation | string | `"linkerd.io/inject"` | Annotation label to signal injection. Do not edit. |
+| global.proxyInjectDisabled | string | `"disabled"` | Annotation value to disable injection. Do not edit.  |
+| global.workloadNamespaceLabel | string | `"linkerd.io/workload-ns"` |  |
+| grafana.enabled | bool | `true` |  |
+| heartbeatSchedule | string | `"0 0 * * *"` | Config for the heartbeat cronjob |
+| identity.issuer.clockSkewAllowance | string | `"20s"` | Amount of time to allow for clock skew within a Linkerd cluster |
+| identity.issuer.crtExpiry | string | `nil` | Expiration timestamp for the issuer certificate. It must be provided during install. Must match the expiry date in crtPEM |
+| identity.issuer.crtExpiryAnnotation | string | `"linkerd.io/identity-issuer-expiry"` | Annotation used to identity the issuer certificate expiration timestamp. Do not edit. |
+| identity.issuer.issuanceLifetime | string | `"24h0m0s"` | Amount of time for which the Identity issuer should certify identity |
+| identity.issuer.scheme | string | `"linkerd.io/tls"` |  |
+| identity.issuer.tls | object | `{"crtPEM":"","keyPEM":""}` | Which scheme is used for the identity issuer secret format  |
+| identity.issuer.tls.crtPEM | string | `""` | Issuer certificate (ECDSA). It must be provided during install. |
+| identity.issuer.tls.keyPEM | string | `""` | Key for the issuer certificate (ECDSA). It must be provided during install |
+| installNamespace | bool | `true` | Set to false when installing Linkerd in a custom namespace. See the [Linkerd documentation](https://linkerd.io/2/tasks/install-helmcustomizing-the-namespace) for more information. |
+| nodeSelector | object | `{"beta.kubernetes.io/os":"linux"}` | NodeSelector section, See the [K8S documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) for more information |
+| omitWebhookSideEffects | bool | `false` | Omit the `sideEffects` flag in the webhook manifests |
+| profileValidator.caBundle | string | `""` | Bundle of CA certificates for service profile validator. If not provided then Helm will use the certificate generated  for `profileValidator.crtPEM`. If `profileValidator.externalSecret` is set to true, this value must be set, as no certificate will be generated.  |
+| profileValidator.crtPEM | string | `""` | Certificate for the service profile validator. If not provided then Helm will generate one. |
+| profileValidator.externalSecret | bool | `false` | Do not create a secret resource for the profileValidator webhook. If this is set to `true`, the value `profileValidator.caBundle` must be set (see below). |
+| profileValidator.keyPEM | string | `""` | Certificate key for the service profile validator. If not provided then Helm will generate one. |
+| profileValidator.namespaceSelector | object | `{"matchExpressions":[{"key":"config.linkerd.io/admission-webhooks","operator":"NotIn","values":["disabled"]}]}` | Namespace selector used by admission webhook |
+| prometheus.enabled | bool | `true` |  |
+| proxyInjector.caBundle | string | `""` | Bundle of CA certificates for proxy injector. If not provided then Helm will use the certificate generated  for `proxyInjector.crtPEM`. If `proxyInjector.externalSecret` is set to true, this value must be set, as no certificate will be generated. |
+| proxyInjector.crtPEM | string | `""` | Certificate for the proxy injector. If not provided then Helm will generate one. |
+| proxyInjector.externalSecret | bool | `false` | Do not create a secret resource for the profileValidator webhook. If this is set to `true`, the value `proxyInjector.caBundle` must be set (see below) |
+| proxyInjector.keyPEM | string | `""` | Certificate key for the proxy injector. If not provided then Helm will generate one.  |
+| proxyInjector.namespaceSelector | object | `{"matchExpressions":[{"key":"config.linkerd.io/admission-webhooks","operator":"NotIn","values":["disabled"]}]}` | Namespace selector used by admission webhook. If not set defaults to all namespaces without the annotation config.linkerd.io/admission-webhooks=disabled |
+| tap.caBundle | string | `""` | Bundle of CA certificates for Tap component. If not provided then Helm will use the certificate generated  for `tap.crtPEM`. If `tap.externalSecret` is set to true, this value must be set, as no certificate will be generated. |
+| tap.crtPEM | string | `""` | Certificate for the Tap component. If not provided then Helm will generate one. |
+| tap.externalSecret | bool | `false` | Do not create a secret resource for the Tap component. If this is set to `true`, the value `tap.caBundle` must be set (see below). |
+| tap.keyPEM | string | `""` | Certificate key for Tap component. If not provided then Helm will generate one.  |
+| tracing.enabled | bool | `false` |  |
+| webImage | string | `"ghcr.io/linkerd/web"` |  |
+| webhookFailurePolicy | string | `"Ignore"` | Failure policy for the proxy injector  |
+
+----------------------------------------------
+Autogenerated from chart metadata using [helm-docs v1.4.0](https://github.com/norwoodj/helm-docs/releases/v1.4.0)
diff --git a/charts/linkerd2/README.md.gotmpl b/charts/linkerd2/README.md.gotmpl
new file mode 100644
index 000000000..d2bb0982c
--- /dev/null
+++ b/charts/linkerd2/README.md.gotmpl
@@ -0,0 +1,118 @@
+{{ template "chart.header" . }}
+{{ template "chart.description" . }}
+
+{{ template "chart.versionBadge" . }}
+{{ template "chart.typeBadge" . }}
+{{ template "chart.appVersionBadge" . }}
+
+{{ template "chart.homepageLine" . }}
+
+## Quickstart and documentation
+
+You can run Linkerd on any Kubernetes 1.13+ cluster in a matter of seconds. See
+the [Linkerd Getting Started Guide][getting-started] for how.
+
+For more comprehensive documentation, start with the [Linkerd
+docs][linkerd-docs].
+
+## Prerequisite: identity certificates
+
+The identity component of Linkerd requires setting up a trust anchor
+certificate, and an issuer certificate with its key. These need to be provided
+to Helm by the user (unlike when using the `linkerd install` CLI which can
+generate these automatically). You can provide your own, or follow [these
+instructions](https://linkerd.io/2/tasks/generate-certificates/) to generate new
+ones.
+
+Note that the provided certificates must be ECDSA certificates.
+
+## Adding Linkerd's Helm repository
+
+```bash
+# To add the repo for Linkerd2 stable releases:
+helm repo add linkerd https://helm.linkerd.io/stable
+# To add the repo for Linkerd2 edge releases:
+helm repo add linkerd-edge https://helm.linkerd.io/edge
+```
+
+The following instructions use the `linkerd` repo. For installing an edge
+release, just replace with `linkerd-edge`.
+
+## Installing the chart
+
+You must provide the certificates and keys described in the preceding section,
+and the same expiration date you used to generate the Issuer certificate.
+
+In this example we set the expiration date to one year ahead:
+
+```bash
+helm install \
+  --set-file global.identityTrustAnchorsPEM=ca.crt \
+  --set-file identity.issuer.tls.crtPEM=issuer.crt \
+  --set-file identity.issuer.tls.keyPEM=issuer.key \
+  --set identity.issuer.crtExpiry=$(date -d '+8760 hour' +"%Y-%m-%dT%H:%M:%SZ") \
+  linkerd/linkerd2
+```
+
+## Setting High-Availability
+
+Besides the default `values.yaml` file, the chart provides a `values-ha.yaml`
+file that overrides some default values as to set things up under a
+high-availability scenario, analogous to the `--ha` option in `linkerd install`.
+Values such as higher number of replicas, higher memory/cpu limits and
+affinities are specified in that file.
+
+You can get ahold of `values-ha.yaml` by fetching the chart files:
+
+```bash
+helm fetch --untar linkerd/linkerd2
+```
+
+Then use the `-f` flag to provide the override file, for example:
+
+```bash
+helm install \
+  --set-file global.identityTrustAnchorsPEM=ca.crt \
+  --set-file identity.issuer.tls.crtPEM=issuer.crt \
+  --set-file identity.issuer.tls.keyPEM=issuer.key \
+  --set identity.issuer.crtExpiry=$(date -d '+8760 hour' +"%Y-%m-%dT%H:%M:%SZ") \
+  -f linkerd2/values-ha.yaml
+  linkerd/linkerd2
+```
+
+## Get involved
+
+* Check out Linkerd's source code at [Github][linkerd2].
+* Join Linkerd's [user mailing list][linkerd-users], [developer mailing
+  list][linkerd-dev], and [announcements mailing list][linkerd-announce].
+* Follow [@linkerd][twitter] on Twitter.
+* Join the [Linkerd Slack][slack].
+
+[cncf]: https://www.cncf.io/
+[getting-started]: https://linkerd.io/2/getting-started/
+[linkerd2]: https://github.com/linkerd/linkerd2
+[linkerd-announce]: https://lists.cncf.io/g/cncf-linkerd-announce
+[linkerd-dev]: https://lists.cncf.io/g/cncf-linkerd-dev
+[linkerd-docs]: https://linkerd.io/2/overview/
+[linkerd-users]: https://lists.cncf.io/g/cncf-linkerd-users
+[slack]: http://slack.linkerd.io
+[twitter]: https://twitter.com/linkerd
+
+## Addons for linkerd
+
+For the linkerd application there are some addons that can be configured. The
+documentation for the configurations of the addons can be found in their
+respective readme.md
+
+[Prometheus](https://github.com/linkerd/linkerd2/blob/main/charts/add-ons/prometheus/README.md)
+
+[Grafana](https://github.com/linkerd/linkerd2/blob/main/charts/add-ons/grafana/README.md)
+
+[Tracing](https://github.com/linkerd/linkerd2/blob/main/charts/add-ons/tracing/README.md)
+
+
+{{ template "chart.requirementsSection" . }}
+
+{{ template "chart.valuesSection" . }}
+
+{{ template "helm-docs.versionFooter" . }}
diff --git a/charts/linkerd2/values.yaml b/charts/linkerd2/values.yaml
index 1840af627..a58b8de95 100644
--- a/charts/linkerd2/values.yaml
+++ b/charts/linkerd2/values.yaml
@@ -4,199 +4,262 @@
 
 # Values that are passed along to sub-charts
 global:
-  # The kubernetes cluster domain.
+  # -- Kubernetes DNS Domain name to use  
   clusterDomain: &cluster_domain cluster.local
 
-  # The cluster networks for which service discovery is performed. This should
+  # -- The cluster networks for which service discovery is performed. This should
   # include the pod network but need not include the node network.
   #
   # By default, all private networks are specified so that resolution works in
   # typical Kubernetes environments.
   clusterNetworks: "10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16"
-
+  # -- Docker image pull policy 
   imagePullPolicy: &image_pull_policy IfNotPresent
+  # -- Log level for the control plane components
   controllerLogLevel: &controller_log_level info
 
-  # control plane trace configuration
+  # -- control plane trace configuration
   controlPlaneTracing: false
 
-  # control plane version. See Proxy section for proxy version
+  # -- control plane version. See Proxy section for proxy version
   linkerdVersion: &linkerd_version linkerdVersionValue
 
+  # -- Control plane namespace
   namespace: linkerd
 
-  # enables the use of EndpointSlice informers for the destination service;
-  # enableEndpointSlices should be set to true only if EndpointSlice K8s feature gate is on;
-  # the feature is still experimental.
+  # -- enables the use of EndpointSlice informers for the destination service;
+  # enableEndpointSlices should be set to true only if EndpointSlice K8s feature
+  # gate is on; the feature is still experimental.
   enableEndpointSlices: false
 
-  # enabling this omits the NET_ADMIN capability in the PSP
+  # -- enabling this omits the NET_ADMIN capability in the PSP
   # and the proxy-init container when injecting the proxy;
   # requires the linkerd-cni plugin to already be installed
   cniEnabled: false
 
+  # -- Trust root certificate (ECDSA). It must be provided during install. 
   identityTrustAnchorsPEM: |
 
+  # -- Trust domain used for identity 
   identityTrustDomain: *cluster_domain
 
-  # url of existing prometheus
+  # -- url of existing prometheus
   prometheusUrl: ""
-  # url of external grafana instance with reverse proxy configured
+  # -- url of external grafana instance with reverse proxy configured.
   grafanaUrl: ""
 
-  # Additional annotations to add to all pods
+  # -- Additional annotations to add to all pods
   podAnnotations: {}
 
-  # Additional labels to add to all pods
+  # -- Additional labels to add to all pods
   podLabels: {}
 
   # proxy configuration
   proxy:
+    # -- Enable service profiles for non-Kubernetes services
     enableExternalProfiles: false
+    # -- Maximum time allowed for the proxy to establish an outbound TCP
+    # connection
     outboundConnectTimeout: 1000ms
+    # -- Maximum time allowed for the proxy to establish an inbound TCP
+    # connection
     inboundConnectTimeout: 100ms
     image:
+      # -- Docker image for the proxy
       name: ghcr.io/linkerd/proxy
+      # -- Pull policy for the proxy container Docker image
       pullPolicy: *image_pull_policy
+      # -- Tag for the proxy container Docker image
       version: *linkerd_version
+    # -- Log level for the proxy
     logLevel: warn,linkerd=info
+    # -- Log format (`plain` or `json`) for the proxy
     logFormat: plain
     ports:
+      # -- Admin port for the proxy container
       admin: 4191
+      # -- Control port for the proxy container
       control: 4190
+      # -- Inbound port for the proxy container
       inbound: 4143
+      # -- Outbound port for the proxy container  
       outbound: 4140
-    # The `cpu.limit` and `cores` should be kept in sync. The value of `cores`
+    # -- The `cpu.limit` and `cores` should be kept in sync. The value of `cores`
     # must be an integer and should typically be set by rounding up from the
     # limit. E.g. if cpu.limit is '1500m', cores should be 2.
     cores: 0
     resources:
       cpu:
+        # -- Maximum amount of CPU units that the proxy can use 
         limit: ""
+        # -- Amount of CPU units that the proxy requests
         request: ""
       memory:
+        # -- Maximum amount of memory that the proxy can use 
         limit: ""
+        # -- Maximum amount of memory that the proxy requests
         request: ""
     trace:
+      # -- Collector Service address for the proxies to send Trace Data 
       collectorSvcAddr: ""
+      # -- Service account associated with the Trace collector instance 
       collectorSvcAccount: default
+    # -- User id under which the proxy runs
     uid: 2102
-    # If set, the proxy's pre-stop hook will postpone the Kubernetes's SIGTERM signal
-    # and wait for this duration before letting the proxy process the SIGTERM signal.
-    # See https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks
+   
+    # -- If set the proxy sidecar will stay alive for at
+    # least the given period before receiving SIGTERM signal from Kubernetes but
+    # no longer than pod's `terminationGracePeriodSeconds`. See [Lifecycle
+    # hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks)
     # for more info on container lifecycle hooks.
     waitBeforeExitSeconds: 0
     requireIdentityOnInboundPorts: ""
 
   # proxy-init configuration
   proxyInit:
-    # Default set of ports to skip via itpables:
+    # -- Default set of ports to skip via itpables:
     # - SMTP (25,587) server-first
     # - HTTPS (443) opaque TLS
     # - MYSQL (3306) server-first
     # - Memcached (11211) clients do not issue any preamble, which breaks detection
     ignoreInboundPorts: "25,443,587,3306,11211"
+    # -- Default set of ports to skip via itpables, same defaults as InboudPorts
     ignoreOutboundPorts: "25,443,587,3306,11211"
     image:
+      # -- Docker image for the proxy-init container
       name: ghcr.io/linkerd/proxy-init
+      # -- Pull policy for the proxy-init container Docker image
       pullPolicy: *image_pull_policy
+      # -- Tag for the proxy-init container Docker image
       version: v1.3.8
     resources:
       cpu:
+        # -- Maximum amount of CPU units that the proxy-init container can use
         limit: 100m
+        # -- Amount of CPU units that the proxy-init container requests
         request: 10m
       memory:
+        # -- Maximum amount of memory that the proxy-init container can use
         limit: 50Mi
+        # -- Amount of memory that the proxy-init container requests
         request: 10Mi
     closeWaitTimeoutSecs: 0
     xtMountPath:
       mountPath: /run
       name: linkerd-proxy-init-xtables-lock
 
-  # control plane annotations - do not edit
+  # -- Annotation label for the proxy create. Do not edit. 
   createdByAnnotation: linkerd.io/created-by
+  # -- Annotation label to signal injection. Do not edit.
   proxyInjectAnnotation: linkerd.io/inject
+  # -- Annotation value to disable injection. Do not edit. 
   proxyInjectDisabled: disabled
 
-  # control plane labels - do not edit
+  # -- Control plane label. Do not edit
   controllerComponentLabel: linkerd.io/control-plane-component
+  # -- Control plane label. Do not edit  
   controllerNamespaceLabel: linkerd.io/control-plane-ns
+  # -- Control plane label. Do not edit 
   linkerdNamespaceLabel: linkerd.io/is-control-plane
   workloadNamespaceLabel: linkerd.io/workload-ns
 
-  # For Private docker registries, authentication is needed.
+  # -- For Private docker registries, authentication is needed.
   #  Registry secrets are applied to the respective service accounts
   imagePullSecrets: []
   # - name: my-private-docker-registry-login-secret
 
-# enforced host validation regular expression
+# -- Host header validation regex for the dashboard. See the [Linkerd
+# documentation](https://linkerd.io/2/tasks/exposing-dashboard) for more
+# information
 enforcedHostRegexp: ""
 
+# -- Allow proxies to perform transparent HTTP/2 upgrading  
 enableH2Upgrade: true
 
+# -- Omit the `sideEffects` flag in the webhook manifests
 omitWebhookSideEffects: false
+# -- Failure policy for the proxy injector 
 webhookFailurePolicy: Ignore
 
-# controller configuration
+
+# controllerImage -- Docker image for the controller, tap and identity
+# components
 controllerImage: ghcr.io/linkerd/controller
+# -- Number of replicas for each control plane pod
 controllerReplicas: 1
+# -- User ID for the control plane components
 controllerUID: 2103
 
 
 # destination configuration
 # set resources for the sp-validator and its linkerd proxy respectively
 # see global.proxy.resources for details.
+# destinationResources -- CPU and Memory resources required by destination (see `global.proxy.resources` for sub-fields)
 #destinationResources:
+#destinationProxyResources -- CPU and Memory resources required by proxy 
+# injected into destination pod (see `global.proxy.resources` for sub-fields)
 #destinationProxyResources:
 
 
 # web dashboard configuration
 dashboard:
+  # -- Number of replicas of dashboard
   replicas: 1
 
 # debug configuration
 debugContainer:
   image:
+    # -- Docker image for the debug container
     name: ghcr.io/linkerd/debug
+    # -- Pull policy for the debug container Docker image
     pullPolicy: *image_pull_policy
+    # -- Tag for the debug container Docker image
     version: *linkerd_version
 
-# identity configuration
 identity:
   issuer:
     scheme: linkerd.io/tls
 
+    # -- Amount of time to allow for clock skew within a Linkerd cluster
     clockSkewAllowance: 20s
 
-    # must match the expiry date in crtPEM
+    # -- Expiration timestamp for the issuer certificate. It must be provided during install. Must match the expiry date in crtPEM
     crtExpiry:
 
-    # control plane annotation - do not edit
+    # -- Annotation used to identity the issuer certificate expiration timestamp. Do not edit.
     crtExpiryAnnotation: linkerd.io/identity-issuer-expiry
-
+    # -- Amount of time for which the Identity issuer should certify identity
     issuanceLifetime: 24h0m0s
-
+    
+    # -- Which scheme is used for the identity issuer secret format 
     tls:
-      # PEM-encoded certificate
+      # -- Issuer certificate (ECDSA). It must be provided during install.
       crtPEM: |
 
-      # PEM-encoded ECDSA private key
+      # -- Key for the issuer certificate (ECDSA). It must be provided during
+      # install
       keyPEM: |
 
-# set resources for identity and its linkerd proxy respectively
-# see global.proxy.resources for details.
+
+# -|- CPU and Memory resources required by the identity controller (see `global.proxy.resources` for sub-fields)
 #identityResources:
+# -|- CPU and Memory resources required by proxy injected into identity pod (see `global.proxy.resources` for sub-fields)
 #identityProxyResources:
 
 # heartbeat configuration
+# disableHeartBeat -- Set to true to not start the heartbeat cronjob 
 disableHeartBeat: false
+# -- Config for the heartbeat cronjob
 heartbeatSchedule: "0 0 * * *"
 
 # proxy injector configuration
 proxyInjector:
+  # -- Do not create a secret resource for the profileValidator webhook. If this is set to `true`, the value `proxyInjector.caBundle` must be set (see below)
   externalSecret: false
 
-  # Namespace selector used by admission webhook
+  # -- Namespace selector used by admission webhook. If not set defaults to all
+  # namespaces without the annotation
+  # config.linkerd.io/admission-webhooks=disabled
   namespaceSelector:
     matchExpressions:
     - key: config.linkerd.io/admission-webhooks
@@ -204,24 +267,28 @@ proxyInjector:
       values:
       - disabled
 
-  # if empty, Helm will auto-generate these fields
+  # -- Certificate for the proxy injector. If not provided then Helm will generate one.
   crtPEM: |
 
+  # -- Certificate key for the proxy injector. If not provided then Helm will generate one. 
   keyPEM: |
 
-  # if empty, Helm will auto-generate this field, unless externalSecret is set to true.
+  # -- Bundle of CA certificates for proxy injector. If not provided then Helm will use the certificate generated  for `proxyInjector.crtPEM`. If `proxyInjector.externalSecret` is set to true, this value must be set, as no certificate will be generated.
   caBundle: |
 
-# set resources for proxy injector and its linkerd proxy respectively
-# see global.proxy.resources for details.
+# -|- CPU and Memory resources required by the proxy injector (see
+#`global.proxy.resources` for sub-fields)
 #proxyInjectorResources:
+#-|- CPU and Memory resources required by proxy injected into the proxy injector
+#pod (see `global.proxy.resources` for sub-fields)
 #proxyInjectorProxyResources:
 
 # service profile validator configuration
 profileValidator:
+  # -- Do not create a secret resource for the profileValidator webhook. If this is set to `true`, the value `profileValidator.caBundle` must be set (see below).
   externalSecret: false
 
-    # Namespace selector used by admission webhook
+  # -- Namespace selector used by admission webhook
   namespaceSelector:
     matchExpressions:
     - key: config.linkerd.io/admission-webhooks
@@ -229,45 +296,68 @@ profileValidator:
       values:
       - disabled
 
-  # if empty, Helm will auto-generate these fields
+  # -- Certificate for the service profile validator. If not provided then Helm
+  # will generate one.
   crtPEM: |
 
+  # -- Certificate key for the service profile validator. If not provided then
+  # Helm will generate one.
   keyPEM: |
 
-  # if empty, Helm will auto-generate this field, unless externalSecret is set to true.
+  # -- Bundle of CA certificates for service profile validator. If not provided
+  # then Helm will use the certificate generated  for `profileValidator.crtPEM`.
+  # If `profileValidator.externalSecret` is set to true, this value must be set,
+  # as no certificate will be generated. 
   caBundle: |
 
-# set resources for the sp-validator and its linkerd proxy respectively
-# see global.proxy.resources for details.
+# -|- CPU and Memory resources required by the SP validator (see
+#`global.proxy.resources` for sub-fields)
 #spValidatorResources:
+# -|- CPU and Memory resources required by proxy injected into the SP validator
+#pod (see `global.proxy.resources` for sub-fields)
 #spValidatorProxyResources:
 
-# set resources for controllers public API and its linkerd proxy respectively
-# see global.proxy.resources for details.
+# -|- CPU and Memory resources required by controllers publicAPI (see
+#`global.proxy.resources` for sub-fields)
 #publicAPIResources:
+#-|- CPU and Memory resources required by proxy injected into controllers public
+#API pod (see `global.proxy.resources` for sub-fields) values
+#`global.proxy.resources`
 #publicAPIProxyResources:
 
 # tap configuration
 tap:
+  # -- Do not create a secret resource for the Tap component. If this is set to
+  # `true`, the value `tap.caBundle` must be set (see below).
   externalSecret: false
-  # if empty, Helm will auto-generate these fields
+  # -- Certificate for the Tap component. If not provided then Helm will
+  # generate one.
   crtPEM: |
 
+  # -- Certificate key for Tap component. If not provided then Helm will
+  # generate one. 
   keyPEM: |
 
-  # if empty, Helm will auto-generate this field, unless externalSecret is set to true.
+  # -- Bundle of CA certificates for Tap component. If not provided then Helm
+  # will use the certificate generated  for `tap.crtPEM`. If
+  # `tap.externalSecret` is set to true, this value must be set, as no
+  # certificate will be generated.
   caBundle: |
 
-# set resources for tap and its linkerd proxy respectively
-# see global.proxy.resources for details.
+# -|- CPU and Memory resources required by tap (see `global.proxy.resources` for
+#sub-fields)
 #tapResources:
+# -|- CPU and Memory resources required by proxy injected into tap pod (see
+#`global.proxy.resources` for sub-fields)
 #tapProxyResources:
 
-# web configuration
+# -- Docker image for the web container
 webImage: ghcr.io/linkerd/web
-# set resources for web UI and its linkerd proxy respectively
-# see global.proxy.resources for details.
+# -<- CPU and Memory resources required by web UI (see `global.proxy.resources`
+#for sub-fields)
 #webResources:
+# -|- CPU and Memory resources required by proxy injected into web UI pod (see
+#`global.proxy.resources` for sub-fields)
 #webProxyResources:
 
 
@@ -276,15 +366,20 @@ webImage: ghcr.io/linkerd/web
 # - The namespace created by the external tool must match the namespace value above
 # - The external tool needs to create the namespace with the label:
 #     config.linkerd.io/admission-webhooks: disabled
+
+# installNamespace -- Set to false when installing Linkerd in a custom namespace. See the
+# [Linkerd documentation](https://linkerd.io/2/tasks/install-helmcustomizing-the-namespace) for more information.
 installNamespace: true
 
-# Node selection constraints for control-plane components
-# https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector.
+# -- NodeSelector section, See the [K8S
+# documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector)
+# for more information
 nodeSelector:
   beta.kubernetes.io/os: linux
 
-# Tolerations constraints for control-plane components
-# https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/
+# -|- Tolerations section, See the 
+# [K8S documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/)
+# for more information
 #tolerations:
 
 # Configuration for Add-ons
diff --git a/charts/partials/Chart.yaml b/charts/partials/Chart.yaml
index 7d91420d6..b269da1a2 100644
--- a/charts/partials/Chart.yaml
+++ b/charts/partials/Chart.yaml
@@ -1,4 +1,6 @@
 apiVersion: v1
-description: A Helm chart containing Linkerd partial templates, depended by the 'linkerd' and 'patch' charts.
+description: |
+  A Helm chart containing Linkerd partial templates,
+  depended by the 'linkerd' and 'patch' charts.
 name: partials
 version: 0.1.0
diff --git a/charts/partials/README.md b/charts/partials/README.md
new file mode 100644
index 000000000..1663c6eca
--- /dev/null
+++ b/charts/partials/README.md
@@ -0,0 +1,9 @@
+# partials
+
+A Helm chart containing Linkerd partial templates,
+depended by the 'linkerd' and 'patch' charts.
+
+![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square)
+
+----------------------------------------------
+Autogenerated from chart metadata using [helm-docs v1.4.0](https://github.com/norwoodj/helm-docs/releases/v1.4.0)
diff --git a/charts/partials/README.md.gotmpl b/charts/partials/README.md.gotmpl
new file mode 100644
index 000000000..37f510106
--- /dev/null
+++ b/charts/partials/README.md.gotmpl
@@ -0,0 +1,14 @@
+{{ template "chart.header" . }}
+{{ template "chart.description" . }}
+
+{{ template "chart.versionBadge" . }}
+{{ template "chart.typeBadge" . }}
+{{ template "chart.appVersionBadge" . }}
+
+{{ template "chart.homepageLine" . }}
+
+{{ template "chart.requirementsSection" . }}
+
+{{ template "chart.valuesSection" . }}
+
+{{ template "helm-docs.versionFooter" . }}
diff --git a/cli/cmd/completion.go b/cli/cmd/completion.go
index a19bf2ce7..28fb3f9ca 100644
--- a/cli/cmd/completion.go
+++ b/cli/cmd/completion.go
@@ -11,26 +11,31 @@ import (
 // newCmdCompletion creates a new cobra command `completion` which contains commands for
 // enabling linkerd auto completion
 func newCmdCompletion() *cobra.Command {
-	example := `  # bash <= 3.2
+	example := `  # bash <= 3.2:
   source /dev/stdin <<< "$(linkerd completion bash)"
 
-  # bash >= 4.0
+  # bash >= 4.0:
   source <(linkerd completion bash)
 
-  # bash <= 3.2 on osx
+  # bash <= 3.2 on osx:
   brew install bash-completion # ensure you have bash-completion 1.3+
   linkerd completion bash > $(brew --prefix)/etc/bash_completion.d/linkerd
 
-  # bash >= 4.0 on osx
+  # bash >= 4.0 on osx:
   brew install bash-completion@2
   linkerd completion bash > $(brew --prefix)/etc/bash_completion.d/linkerd
 
-  # zsh
-  source <(linkerd completion zsh)
+  # zsh:
+  # If shell completion is not already enabled in your environment you will need
+  # to enable it.  You can execute the following once:
 
-  # zsh on osx / oh-my-zsh
+  echo "autoload -U compinit; compinit" >> ~/.zshrc
+
+  # To load completions for each session, execute once:
   linkerd completion zsh > "${fpath[1]}/_linkerd"
 
+  # You will need to start a new shell for this setup to take effect.
+
   # fish:
   linkerd completion fish | source
 
diff --git a/cli/cmd/install.go b/cli/cmd/install.go
index 3dcbfda78..0df846379 100644
--- a/cli/cmd/install.go
+++ b/cli/cmd/install.go
@@ -117,7 +117,7 @@ resources for the Linkerd control plane. This command should be followed by
   linkerd install config | kubectl apply -f -
 
   # Install Linkerd into a non-default namespace.
-  linkerd install config -l linkerdtest | kubectl apply -f -`,
+  linkerd install config -L linkerdtest | kubectl apply -f -`,
 		RunE: func(cmd *cobra.Command, args []string) error {
 			err := flag.ApplySetFlags(values, flags)
 			if err != nil {
diff --git a/cli/cmd/multicluster.go b/cli/cmd/multicluster.go
index 04155a99f..976cd9f02 100644
--- a/cli/cmd/multicluster.go
+++ b/cli/cmd/multicluster.go
@@ -59,6 +59,7 @@ type (
 		gatewayNginxImage       string
 		gatewayNginxVersion     string
 		remoteMirrorCredentials bool
+		gatewayServiceType      string
 	}
 
 	linkOptions struct {
@@ -73,6 +74,7 @@ type (
 		controlPlaneVersion     string
 		dockerRegistry          string
 		selector                string
+		gatewayAddresses        string
 	}
 
 	gatewaysOptions struct {
@@ -97,6 +99,7 @@ func newMulticlusterInstallOptionsWithDefault() (*multiclusterInstallOptions, er
 		gatewayNginxImage:       defaults.GatewayNginxImage,
 		gatewayNginxVersion:     defaults.GatewayNginxImageVersion,
 		remoteMirrorCredentials: true,
+		gatewayServiceType:      defaults.GatewayServiceType,
 	}, nil
 }
 
@@ -113,6 +116,7 @@ func newLinkOptionsWithDefault() (*linkOptions, error) {
 		serviceMirrorRetryLimit: defaults.ServiceMirrorRetryLimit,
 		logLevel:                defaults.LogLevel,
 		selector:                k8s.DefaultExportedServiceSelector,
+		gatewayAddresses:        "",
 	}, nil
 }
 
@@ -198,6 +202,7 @@ func buildMulticlusterInstallValues(ctx context.Context, opts *multiclusterInsta
 	defaults.ProxyOutboundPort = uint32(values.GetGlobal().Proxy.Ports.Outbound)
 	defaults.LinkerdVersion = version.Version
 	defaults.RemoteMirrorServiceAccount = opts.remoteMirrorCredentials
+	defaults.GatewayServiceType = opts.gatewayServiceType
 
 	return defaults, nil
 }
@@ -395,6 +400,7 @@ func newMulticlusterInstallCommand() *cobra.Command {
 	cmd.Flags().StringVar(&options.gatewayNginxImage, "gateway-nginx-image", options.gatewayNginxImage, "The nginx image to be used")
 	cmd.Flags().StringVar(&options.gatewayNginxVersion, "gateway-nginx-image-version", options.gatewayNginxVersion, "The version of nginx to be used")
 	cmd.Flags().BoolVar(&options.remoteMirrorCredentials, "service-mirror-credentials", options.remoteMirrorCredentials, "Whether to install the service account which can be used by service mirror components in source clusters to discover exported services")
+	cmd.Flags().StringVar(&options.gatewayServiceType, "gateway-service-type", options.gatewayServiceType, "Overwrite Service type for gateway service")
 
 	// Hide developer focused flags in release builds.
 	release, err := version.IsReleaseChannel(version.Version)
@@ -617,7 +623,8 @@ func newLinkCommand() *cobra.Command {
 				return err
 			}
 
-			gatewayAddresses := []string{}
+			gatewayAddresses := ""
+			gwAddresses := []string{}
 			for _, ingress := range gateway.Status.LoadBalancer.Ingress {
 				addr := ingress.IP
 				if addr == "" {
@@ -626,10 +633,14 @@ func newLinkCommand() *cobra.Command {
 				if addr == "" {
 					continue
 				}
-				gatewayAddresses = append(gatewayAddresses, addr)
+				gwAddresses = append(gwAddresses, addr)
 			}
-			if len(gatewayAddresses) == 0 {
+			if len(gwAddresses) == 0 && opts.gatewayAddresses == "" {
 				return fmt.Errorf("Gateway %s.%s has no ingress addresses", gateway.Name, gateway.Namespace)
+			} else if len(gwAddresses) > 0 {
+				gatewayAddresses = strings.Join(gwAddresses, ",")
+			} else {
+				gatewayAddresses = opts.gatewayAddresses
 			}
 
 			gatewayIdentity, ok := gateway.Annotations[k8s.GatewayIdentity]
@@ -659,7 +670,7 @@ func newLinkCommand() *cobra.Command {
 				TargetClusterDomain:           configMap.GetGlobal().ClusterDomain,
 				TargetClusterLinkerdNamespace: controlPlaneNamespace,
 				ClusterCredentialsSecret:      fmt.Sprintf("cluster-credentials-%s", opts.clusterName),
-				GatewayAddress:                strings.Join(gatewayAddresses, ","),
+				GatewayAddress:                gatewayAddresses,
 				GatewayPort:                   gatewayPort,
 				GatewayIdentity:               gatewayIdentity,
 				ProbeSpec:                     probeSpec,
@@ -728,6 +739,7 @@ func newLinkCommand() *cobra.Command {
 	cmd.Flags().StringVar(&opts.logLevel, "log-level", opts.logLevel, "Log level for the Multicluster components")
 	cmd.Flags().StringVar(&opts.dockerRegistry, "registry", opts.dockerRegistry, "Docker registry to pull service mirror controller image from")
 	cmd.Flags().StringVarP(&opts.selector, "selector", "l", opts.selector, "Selector (label query) to filter which services in the target cluster to mirror")
+	cmd.Flags().StringVar(&opts.gatewayAddresses, "gateway-addresses", opts.gatewayAddresses, "If specified overwrites gateway addresses when gateway service is not type LoadBalancer (comma separated list)")
 
 	return cmd
 }
diff --git a/cli/cmd/root.go b/cli/cmd/root.go
index 04ad8aa30..2ef98b6ae 100644
--- a/cli/cmd/root.go
+++ b/cli/cmd/root.go
@@ -97,7 +97,7 @@ var RootCmd = &cobra.Command{
 
 func init() {
 	defaultNamespace = getDefaultNamespace()
-	RootCmd.PersistentFlags().StringVarP(&controlPlaneNamespace, "linkerd-namespace", "L", defaultLinkerdNamespace, "Namespace in which Linkerd is installed [$LINKERD_NAMESPACE]")
+	RootCmd.PersistentFlags().StringVarP(&controlPlaneNamespace, "linkerd-namespace", "L", defaultLinkerdNamespace, "Namespace in which Linkerd is installed ($LINKERD_NAMESPACE)")
 	RootCmd.PersistentFlags().StringVarP(&cniNamespace, "cni-namespace", "", defaultCNINamespace, "Namespace in which the Linkerd CNI plugin is installed")
 	RootCmd.PersistentFlags().StringVar(&kubeconfigPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests")
 	RootCmd.PersistentFlags().StringVar(&kubeContext, "context", "", "Name of the kubeconfig context to use")
diff --git a/controller/cmd/identity/main.go b/controller/cmd/identity/main.go
index bcbc1c635..bc5459f38 100644
--- a/controller/cmd/identity/main.go
+++ b/controller/cmd/identity/main.go
@@ -116,7 +116,7 @@ func Main(args []string) {
 	//
 	// Create and start FS creds watcher
 	//
-	watcher := idctl.NewFsCredsWatcher(*issuerPath, issuerEvent, issuerError)
+	watcher := tls.NewFsCredsWatcher(*issuerPath, issuerEvent, issuerError)
 	go func() {
 		if err := watcher.StartWatching(ctx); err != nil {
 			log.Fatalf("Failed to start creds watcher: %s", err)
diff --git a/controller/webhook/launcher.go b/controller/webhook/launcher.go
index 67a93fcf5..e242aa768 100644
--- a/controller/webhook/launcher.go
+++ b/controller/webhook/launcher.go
@@ -10,7 +10,6 @@ import (
 	"github.com/linkerd/linkerd2/controller/k8s"
 	"github.com/linkerd/linkerd2/pkg/admin"
 	pkgk8s "github.com/linkerd/linkerd2/pkg/k8s"
-	"github.com/linkerd/linkerd2/pkg/tls"
 	log "github.com/sirupsen/logrus"
 )
 
@@ -33,12 +32,7 @@ func Launch(
 		log.Fatalf("failed to initialize Kubernetes API: %s", err)
 	}
 
-	cred, err := tls.ReadPEMCreds(pkgk8s.MountPathTLSKeyPEM, pkgk8s.MountPathTLSCrtPEM)
-	if err != nil {
-		log.Fatalf("failed to read TLS secrets: %s", err)
-	}
-
-	s, err := NewServer(k8sAPI, addr, cred, handler, component)
+	s, err := NewServer(ctx, k8sAPI, addr, pkgk8s.MountPathTLSBase, handler, component)
 	if err != nil {
 		log.Fatalf("failed to initialize the webhook server: %s", err)
 	}
diff --git a/controller/webhook/server.go b/controller/webhook/server.go
index 604075f71..b1588b0c1 100644
--- a/controller/webhook/server.go
+++ b/controller/webhook/server.go
@@ -4,10 +4,13 @@ import (
 	"context"
 	"crypto/tls"
 	"encoding/json"
+	"fmt"
 	"io/ioutil"
 	"net/http"
+	"sync/atomic"
 
 	"github.com/linkerd/linkerd2/controller/k8s"
+	pkgk8s "github.com/linkerd/linkerd2/pkg/k8s"
 	pkgTls "github.com/linkerd/linkerd2/pkg/tls"
 	log "github.com/sirupsen/logrus"
 	admissionv1beta1 "k8s.io/api/admission/v1beta1"
@@ -31,28 +34,32 @@ type Handler func(
 // Server describes the https server implementing the webhook
 type Server struct {
 	*http.Server
-	api      *k8s.API
-	handler  Handler
-	recorder record.EventRecorder
+	api       *k8s.API
+	handler   Handler
+	certValue atomic.Value
+	recorder  record.EventRecorder
 }
 
 // NewServer returns a new instance of Server
-func NewServer(api *k8s.API, addr string, cred *pkgTls.Cred, handler Handler, component string) (*Server, error) {
-	var (
-		certPEM = cred.EncodePEM()
-		keyPEM  = cred.EncodePrivateKeyPEM()
-	)
-
-	cert, err := tls.X509KeyPair([]byte(certPEM), []byte(keyPEM))
-	if err != nil {
-		return nil, err
-	}
+func NewServer(
+	ctx context.Context,
+	api *k8s.API,
+	addr, certPath string,
+	handler Handler,
+	component string,
+) (*Server, error) {
+	updateEvent := make(chan struct{})
+	errEvent := make(chan error)
+	watcher := pkgTls.NewFsCredsWatcher(certPath, updateEvent, errEvent)
+	go func() {
+		if err := watcher.StartWatching(ctx); err != nil {
+			log.Fatalf("Failed to start creds watcher: %s", err)
+		}
+	}()
 
 	server := &http.Server{
-		Addr: addr,
-		TLSConfig: &tls.Config{
-			Certificates: []tls.Certificate{cert},
-		},
+		Addr:      addr,
+		TLSConfig: &tls.Config{},
 	}
 
 	eventBroadcaster := record.NewBroadcaster()
@@ -63,11 +70,51 @@ func NewServer(api *k8s.API, addr string, cred *pkgTls.Cred, handler Handler, co
 	})
 	recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: component})
 
-	s := &Server{server, api, handler, recorder}
-	s.Handler = http.HandlerFunc(s.serve)
+	s := getConfiguredServer(server, api, handler, recorder)
+	if err := s.updateCert(); err != nil {
+		log.Fatalf("Failed to initialized certificate: %s", err)
+	}
+
+	go func() {
+		s.run(updateEvent, errEvent)
+	}()
+
 	return s, nil
 }
 
+func getConfiguredServer(
+	httpServer *http.Server,
+	api *k8s.API,
+	handler Handler,
+	recorder record.EventRecorder,
+) *Server {
+	var emptyCert atomic.Value
+	s := &Server{httpServer, api, handler, emptyCert, recorder}
+	s.Handler = http.HandlerFunc(s.serve)
+	httpServer.TLSConfig.GetCertificate = s.getCertificate
+	return s
+}
+
+func (s *Server) updateCert() error {
+	creds, err := pkgTls.ReadPEMCreds(
+		pkgk8s.MountPathTLSKeyPEM,
+		pkgk8s.MountPathTLSCrtPEM,
+	)
+	if err != nil {
+		return fmt.Errorf("failed to read cert from disk: %s", err)
+	}
+
+	certPEM := creds.EncodePEM()
+	keyPEM := creds.EncodePrivateKeyPEM()
+	cert, err := tls.X509KeyPair([]byte(certPEM), []byte(keyPEM))
+	if err != nil {
+		return err
+	}
+	s.certValue.Store(&cert)
+	log.Debug("Certificate has been updated")
+	return nil
+}
+
 // Start starts the https server
 func (s *Server) Start() {
 	log.Infof("listening at %s", s.Server.Addr)
@@ -79,6 +126,27 @@ func (s *Server) Start() {
 	}
 }
 
+// getCertificate provides the TLS server with the current cert
+func (s *Server) getCertificate(_ *tls.ClientHelloInfo) (*tls.Certificate, error) {
+	return s.certValue.Load().(*tls.Certificate), nil
+}
+
+// run reads from the update and error channels and reloads the certs when necessary
+func (s *Server) run(updateEvent <-chan struct{}, errEvent <-chan error) {
+	for {
+		select {
+		case <-updateEvent:
+			if err := s.updateCert(); err != nil {
+				log.Warnf("Skipping update as cert could not be read from disk: %s", err)
+			} else {
+				log.Infof("Updated certificate")
+			}
+		case err := <-errEvent:
+			log.Warnf("Received error from fs watcher: %s", err)
+		}
+	}
+}
+
 func (s *Server) serve(res http.ResponseWriter, req *http.Request) {
 	var (
 		data []byte
diff --git a/go.sum b/go.sum
index 426260fbf..4f8ab028d 100644
--- a/go.sum
+++ b/go.sum
@@ -339,6 +339,7 @@ github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAO
 github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8=
 github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -374,6 +375,7 @@ github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY
 github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
 github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@@ -453,6 +455,7 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6Fm
 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
 github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
 github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
 github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
@@ -572,6 +575,7 @@ github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTK
 github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
+github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
 github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
@@ -1029,6 +1033,7 @@ k8s.io/apimachinery v0.19.3 h1:bpIQXlKjB4cB/oNpnNnV+BybGPR7iP5oYpsOTEJ4hgc=
 k8s.io/apimachinery v0.19.3/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
 k8s.io/apiserver v0.18.8/go.mod h1:12u5FuGql8Cc497ORNj79rhPdiXQC4bf53X/skR/1YM=
 k8s.io/apiserver v0.19.3/go.mod h1:bx6dMm+H6ifgKFpCQT/SAhPwhzoeIMlHIaibomUDec0=
+k8s.io/cli-runtime v0.19.3 h1:vZUTphJIvlh7+867cXiLmyzoCAuQdukbPLIad6eEajQ=
 k8s.io/cli-runtime v0.19.3/go.mod h1:q+l845i5/uWzcUpCrl+L4f3XLaJi8ZeLVQ/decwty0A=
 k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8=
 k8s.io/client-go v0.18.8/go.mod h1:HqFqMllQ5NnQJNwjro9k5zMyfhZlOwpuTLVrxjkYSxU=
@@ -1067,6 +1072,7 @@ k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0=
+sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
 sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
 sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
 sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
diff --git a/jaeger/charts/jaeger/README.md b/jaeger/charts/jaeger/README.md
new file mode 100644
index 000000000..c4f33b5b2
--- /dev/null
+++ b/jaeger/charts/jaeger/README.md
@@ -0,0 +1,42 @@
+# jaeger
+
+A Helm chart for the jaeger add-on in Linkerd
+
+![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square)
+
+![AppVersion: 1.0](https://img.shields.io/badge/AppVersion-1.0-informational?style=flat-square)
+
+## Requirements
+
+| Repository | Name | Version |
+|------------|------|---------|
+| file://../../../charts/partials | partials | 0.1.0 |
+
+## Values
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| collector.image.name | string | `"omnition/opencensus-collector"` |  |
+| collector.image.pullPolicy | string | `"Always"` |  |
+| collector.image.version | string | `"0.1.11"` |  |
+| collectorSvcAccount | string | `"collector"` |  |
+| collectorSvcAddr | string | `"collector.linkerd-jaeger:55678"` |  |
+| jaeger.image.name | string | `"jaegertracing/all-in-one"` |  |
+| jaeger.image.pullPolicy | string | `"Always"` |  |
+| jaeger.image.version | string | `"1.19.2"` |  |
+| linkerdVersion | string | `"linkerdVersionValue"` |  |
+| namespace | string | `"linkerd-jaeger"` |  |
+| webhook.caBundle | string | `""` | if empty, Helm will auto-generate this field, unless externalSecret is set to true. |
+| webhook.crtPEM | string | `""` | if empty, Helm will auto-generate these fields |
+| webhook.externalSecret | bool | `false` |  |
+| webhook.failurePolicy | string | `"Ignore"` |  |
+| webhook.image.name | string | `"ghcr.io/linkerd/jaeger-webhook"` |  |
+| webhook.image.pullPolicy | string | `"IfNotPresent"` |  |
+| webhook.image.version | string | `"linkerdVersionValue"` |  |
+| webhook.keyPEM | string | `""` |  |
+| webhook.logLevel | string | `"info"` |  |
+| webhook.namespaceSelector | string | `nil` |  |
+| webhook.objectSelector | string | `nil` |  |
+
+----------------------------------------------
+Autogenerated from chart metadata using [helm-docs v1.4.0](https://github.com/norwoodj/helm-docs/releases/v1.4.0)
diff --git a/jaeger/charts/jaeger/README.md.gotmpl b/jaeger/charts/jaeger/README.md.gotmpl
new file mode 100644
index 000000000..37f510106
--- /dev/null
+++ b/jaeger/charts/jaeger/README.md.gotmpl
@@ -0,0 +1,14 @@
+{{ template "chart.header" . }}
+{{ template "chart.description" . }}
+
+{{ template "chart.versionBadge" . }}
+{{ template "chart.typeBadge" . }}
+{{ template "chart.appVersionBadge" . }}
+
+{{ template "chart.homepageLine" . }}
+
+{{ template "chart.requirementsSection" . }}
+
+{{ template "chart.valuesSection" . }}
+
+{{ template "helm-docs.versionFooter" . }}
diff --git a/jaeger/charts/jaeger/templates/namespace.yaml b/jaeger/charts/jaeger/templates/namespace.yaml
index c6331bf82..395c9c88d 100644
--- a/jaeger/charts/jaeger/templates/namespace.yaml
+++ b/jaeger/charts/jaeger/templates/namespace.yaml
@@ -3,5 +3,7 @@ kind: Namespace
 apiVersion: v1
 metadata:
   name: {{.Values.namespace}}
+  labels:
+    linkerd.io/extension: linkerd-jaeger
   annotations:
     linkerd.io/inject: enabled
diff --git a/jaeger/charts/jaeger/values.yaml b/jaeger/charts/jaeger/values.yaml
index e3ef54ed0..d9766081d 100644
--- a/jaeger/charts/jaeger/values.yaml
+++ b/jaeger/charts/jaeger/values.yaml
@@ -22,12 +22,12 @@ linkerdVersion: &linkerd_version linkerdVersionValue
 
 webhook:
   externalSecret: false
-  # if empty, Helm will auto-generate these fields
+  # -- if empty, Helm will auto-generate these fields
   crtPEM: |
 
   keyPEM: |
 
-  # if empty, Helm will auto-generate this field, unless externalSecret is set to true.
+  # -- if empty, Helm will auto-generate this field, unless externalSecret is set to true.
   caBundle: |
 
   failurePolicy: Ignore
diff --git a/jaeger/cmd/dashboard.go b/jaeger/cmd/dashboard.go
new file mode 100644
index 000000000..05da6b288
--- /dev/null
+++ b/jaeger/cmd/dashboard.go
@@ -0,0 +1,127 @@
+package cmd
+
+import (
+	"fmt"
+	"os"
+	"os/signal"
+	"time"
+
+	"github.com/linkerd/linkerd2/pkg/k8s"
+	"github.com/pkg/browser"
+	"github.com/spf13/cobra"
+)
+
+const (
+
+	// jaegerDeployment is the name of the jaeger deployment
+	jaegerDeployment = "jaeger"
+
+	// webPort is the http port of the jaeger deployment
+	webPort = 16686
+
+	// defaultHost is the default host used for port-forwarding via `jaeger dashboard`
+	defaultHost = "localhost"
+
+	// defaultPort is for port-forwarding via `jaeger dashboard`
+	defaultPort = 16686
+)
+
+// dashboardOptions holds values for command line flags that apply to the dashboard
+// command.
+type dashboardOptions struct {
+	host    string
+	port    int
+	showURL bool
+	wait    time.Duration
+}
+
+// newDashboardOptions initializes dashboard options with default
+// values for host, port. Also, set max wait time duration for
+// 300 seconds for the dashboard to become available
+//
+// These options may be overridden on the CLI at run-time
+func newDashboardOptions() *dashboardOptions {
+	return &dashboardOptions{
+		host: defaultHost,
+		port: defaultPort,
+		wait: 300 * time.Second,
+	}
+}
+
+// newCmdDashboard creates a new cobra command `dashboard` which contains commands for visualizing jaeger extension's dashboards.
+// After validating flag values, it will use the Kubernetes API to portforward requests to the Jaeger deployment
+// until the process gets killed/canceled
+func newCmdDashboard() *cobra.Command {
+	options := newDashboardOptions()
+
+	cmd := &cobra.Command{
+		Use:   "dashboard [flags]",
+		Short: "Open the Jaeger extension dashboard in a web browser",
+		Args:  cobra.NoArgs,
+		RunE: func(cmd *cobra.Command, args []string) error {
+			if options.port < 0 {
+				return fmt.Errorf("port must be greater than or equal to zero, was %d", options.port)
+			}
+
+			// TODO: Add a jaeger check here
+
+			k8sAPI, err := k8s.NewAPI(kubeconfigPath, kubeContext, impersonate, impersonateGroup, 0)
+			if err != nil {
+				return err
+			}
+
+			signals := make(chan os.Signal, 1)
+			signal.Notify(signals, os.Interrupt)
+			defer signal.Stop(signals)
+
+			portforward, err := k8s.NewPortForward(
+				cmd.Context(),
+				k8sAPI,
+				namespace,
+				jaegerDeployment,
+				options.host,
+				options.port,
+				webPort,
+				verbose,
+			)
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "Failed to initialize port-forward: %s\n", err)
+				os.Exit(1)
+			}
+
+			if err = portforward.Init(); err != nil {
+				// TODO: consider falling back to an ephemeral port if defaultPort is taken
+				fmt.Fprintf(os.Stderr, "Error running port-forward: %s\nCheck for `jaeger dashboard` running in other terminal sessions, or use the `--port` flag.\n", err)
+				os.Exit(1)
+			}
+
+			go func() {
+				<-signals
+				portforward.Stop()
+			}()
+
+			webURL := portforward.URLFor("")
+
+			fmt.Printf("Jaeger extension dashboard available at:\n%s\n", webURL)
+
+			if !options.showURL {
+				err = browser.OpenURL(webURL)
+				if err != nil {
+					fmt.Fprintln(os.Stderr, "Failed to open dashboard automatically")
+					fmt.Fprintf(os.Stderr, "Visit %s in your browser to view the dashboard\n", webURL)
+				}
+			}
+
+			<-portforward.GetStop()
+			return nil
+		},
+	}
+
+	// This is identical to what `kubectl proxy --help` reports, `--port 0` indicates a random port.
+	cmd.PersistentFlags().StringVar(&options.host, "address", options.host, "The address at which to serve requests")
+	cmd.PersistentFlags().IntVarP(&options.port, "port", "p", options.port, "The local port on which to serve requests (when set to 0, a random port will be used)")
+	cmd.PersistentFlags().BoolVar(&options.showURL, "show-url", options.showURL, "show only URL in the CLI, and do not open the browser")
+	cmd.PersistentFlags().DurationVar(&options.wait, "wait", options.wait, "Wait for dashboard to become available if it's not available when the command is run")
+
+	return cmd
+}
diff --git a/jaeger/cmd/install.go b/jaeger/cmd/install.go
index 75c3f5977..b58cd55d5 100644
--- a/jaeger/cmd/install.go
+++ b/jaeger/cmd/install.go
@@ -1,22 +1,26 @@
 package cmd
 
 import (
+	"bytes"
 	"context"
 	"fmt"
 	"io"
 	"os"
+	"path"
 
 	"github.com/linkerd/linkerd2/jaeger/static"
-	jaeger "github.com/linkerd/linkerd2/jaeger/values"
 	"github.com/linkerd/linkerd2/pkg/charts"
+	partials "github.com/linkerd/linkerd2/pkg/charts/static"
+	"github.com/linkerd/linkerd2/pkg/flags"
 	"github.com/linkerd/linkerd2/pkg/healthcheck"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	"github.com/spf13/cobra"
 	"helm.sh/helm/v3/pkg/chart/loader"
 	"helm.sh/helm/v3/pkg/chartutil"
+	"helm.sh/helm/v3/pkg/cli/values"
+	"helm.sh/helm/v3/pkg/engine"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"sigs.k8s.io/yaml"
 )
 
 var (
@@ -30,12 +34,7 @@ var (
 
 func newCmdInstall() *cobra.Command {
 	var skipChecks bool
-
-	values, err := jaeger.NewValues()
-	if err != nil {
-		fmt.Fprint(os.Stderr, err.Error())
-		os.Exit(1)
-	}
+	var options values.Options
 
 	cmd := &cobra.Command{
 		Use:   "install [flags]",
@@ -59,7 +58,7 @@ func newCmdInstall() *cobra.Command {
 				}
 			}
 
-			return install(os.Stdout, values)
+			return install(os.Stdout, options)
 		},
 	}
 
@@ -68,27 +67,29 @@ func newCmdInstall() *cobra.Command {
 		`Skip checks for namespace existence`,
 	)
 
-	// TODO: Add --set flag set and also config
+	flags.AddValueOptionsFlags(cmd.Flags(), &options)
 
 	return cmd
 }
 
-func install(w io.Writer, values *jaeger.Values) error {
+func install(w io.Writer, options values.Options) error {
+
+	// Create values override
+	valuesOverrides, err := options.MergeValues(nil)
+	if err != nil {
+		return err
+	}
 
 	// TODO: Add any validation logic here
 
-	return render(w, values)
+	return render(w, valuesOverrides)
 }
 
-func render(w io.Writer, values *jaeger.Values) error {
-	// Render raw values and create chart config
-	rawValues, err := yaml.Marshal(values)
-	if err != nil {
-		return err
-	}
+func render(w io.Writer, valuesOverrides map[string]interface{}) error {
 
 	files := []*loader.BufferedFile{
 		{Name: chartutil.ChartfileName},
+		{Name: chartutil.ValuesfileName},
 	}
 
 	for _, template := range templatesJaeger {
@@ -97,19 +98,49 @@ func render(w io.Writer, values *jaeger.Values) error {
 		)
 	}
 
-	chart := &charts.Chart{
-		Name:      "jaeger",
-		Dir:       "jaeger",
-		Namespace: values.Namespace,
-		RawValues: rawValues,
-		Files:     files,
-		Fs:        static.Templates,
+	var partialFiles []*loader.BufferedFile
+	for _, template := range charts.L5dPartials {
+		partialFiles = append(partialFiles,
+			&loader.BufferedFile{Name: template},
+		)
+	}
+
+	// Load all jaeger chart files into buffer
+	if err := charts.FilesReader(static.Templates, "jaeger/", files); err != nil {
+		return err
+	}
+
+	// Load all partial chart files into buffer
+	if err := charts.FilesReader(partials.Templates, "", partialFiles); err != nil {
+		return err
+	}
+
+	// Create a Chart obj from the files
+	chart, err := loader.LoadFiles(append(files, partialFiles...))
+	if err != nil {
+		return err
 	}
-	buf, err := chart.Render()
+
+	vals, err := chartutil.CoalesceValues(chart, valuesOverrides)
 	if err != nil {
 		return err
 	}
 
+	// Attach the final values into the `Values` field for rendering to work
+	renderedTemplates, err := engine.Render(chart, map[string]interface{}{"Values": vals})
+	if err != nil {
+		return err
+	}
+
+	// Merge templates and inject
+	var buf bytes.Buffer
+	for _, tmpl := range chart.Templates {
+		t := path.Join(chart.Metadata.Name, tmpl.Name)
+		if _, err := buf.WriteString(renderedTemplates[t]); err != nil {
+			return err
+		}
+	}
+
 	_, err = w.Write(buf.Bytes())
 	return err
 }
diff --git a/jaeger/cmd/root.go b/jaeger/cmd/root.go
index 7da564123..b8dd548a2 100644
--- a/jaeger/cmd/root.go
+++ b/jaeger/cmd/root.go
@@ -10,11 +10,13 @@ import (
 
 const (
 	defaultLinkerdNamespace = "linkerd"
+	defaultJaegerNamespace  = "linkerd-jaeger"
 )
 
 var (
 	apiAddr               string // An empty value means "use the Kubernetes configuration"
 	controlPlaneNamespace string
+	namespace             string
 	kubeconfigPath        string
 	kubeContext           string
 	impersonate           string
@@ -48,7 +50,8 @@ func NewCmdJaeger() *cobra.Command {
 		},
 	}
 
-	jaegerCmd.PersistentFlags().StringVarP(&controlPlaneNamespace, "linkerd-namespace", "L", defaultLinkerdNamespace, "Namespace in which Linkerd is installed [$LINKERD_NAMESPACE]")
+	jaegerCmd.PersistentFlags().StringVarP(&controlPlaneNamespace, "linkerd-namespace", "L", defaultLinkerdNamespace, "Namespace in which Linkerd is installed")
+	jaegerCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", defaultJaegerNamespace, "Namespace in which Jaeger extension is installed")
 	jaegerCmd.PersistentFlags().StringVar(&kubeconfigPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests")
 	jaegerCmd.PersistentFlags().StringVar(&kubeContext, "context", "", "Name of the kubeconfig context to use")
 	jaegerCmd.PersistentFlags().StringVar(&impersonate, "as", "", "Username to impersonate for Kubernetes operations")
@@ -56,6 +59,7 @@ func NewCmdJaeger() *cobra.Command {
 	jaegerCmd.PersistentFlags().StringVar(&apiAddr, "api-addr", "", "Override kubeconfig and communicate directly with the control plane at host:port (mostly for testing)")
 	jaegerCmd.PersistentFlags().BoolVar(&verbose, "verbose", false, "Turn on debug logging")
 	jaegerCmd.AddCommand(newCmdInstall())
+	jaegerCmd.AddCommand(newCmdDashboard())
 
 	return jaegerCmd
 }
diff --git a/pkg/charts/charts.go b/pkg/charts/charts.go
index b3c191fa1..c18fcc4dd 100644
--- a/pkg/charts/charts.go
+++ b/pkg/charts/charts.go
@@ -16,6 +16,29 @@ import (
 
 const versionPlaceholder = "linkerdVersionValue"
 
+var (
+	// L5dPartials is the list of templates in partials chart
+	// Keep this slice synced with the contents of /charts/partials
+	L5dPartials = []string{
+		"charts/partials/" + chartutil.ChartfileName,
+		"charts/partials/templates/_proxy.tpl",
+		"charts/partials/templates/_proxy-init.tpl",
+		"charts/partials/templates/_volumes.tpl",
+		"charts/partials/templates/_resources.tpl",
+		"charts/partials/templates/_metadata.tpl",
+		"charts/partials/templates/_helpers.tpl",
+		"charts/partials/templates/_debug.tpl",
+		"charts/partials/templates/_capabilities.tpl",
+		"charts/partials/templates/_trace.tpl",
+		"charts/partials/templates/_nodeselector.tpl",
+		"charts/partials/templates/_tolerations.tpl",
+		"charts/partials/templates/_affinity.tpl",
+		"charts/partials/templates/_addons.tpl",
+		"charts/partials/templates/_validate.tpl",
+		"charts/partials/templates/_pull-secrets.tpl",
+	}
+)
+
 // Chart holds the necessary info to render a Helm chart
 type Chart struct {
 	Name      string
@@ -80,25 +103,13 @@ func (c *Chart) render(partialsFiles []*loader.BufferedFile) (bytes.Buffer, erro
 // Render returns a bytes buffer with the result of rendering a Helm chart
 func (c *Chart) Render() (bytes.Buffer, error) {
 
-	// Keep this slice synced with the contents of /charts/partials
-	l5dPartials := []*loader.BufferedFile{
-		{Name: "charts/partials/" + chartutil.ChartfileName},
-		{Name: "charts/partials/templates/_proxy.tpl"},
-		{Name: "charts/partials/templates/_proxy-init.tpl"},
-		{Name: "charts/partials/templates/_volumes.tpl"},
-		{Name: "charts/partials/templates/_resources.tpl"},
-		{Name: "charts/partials/templates/_metadata.tpl"},
-		{Name: "charts/partials/templates/_helpers.tpl"},
-		{Name: "charts/partials/templates/_debug.tpl"},
-		{Name: "charts/partials/templates/_capabilities.tpl"},
-		{Name: "charts/partials/templates/_trace.tpl"},
-		{Name: "charts/partials/templates/_nodeselector.tpl"},
-		{Name: "charts/partials/templates/_tolerations.tpl"},
-		{Name: "charts/partials/templates/_affinity.tpl"},
-		{Name: "charts/partials/templates/_addons.tpl"},
-		{Name: "charts/partials/templates/_validate.tpl"},
-		{Name: "charts/partials/templates/_pull-secrets.tpl"},
+	l5dPartials := []*loader.BufferedFile{}
+	for _, template := range L5dPartials {
+		l5dPartials = append(l5dPartials, &loader.BufferedFile{
+			Name: template,
+		})
 	}
+
 	return c.render(l5dPartials)
 }
 
diff --git a/pkg/charts/multicluster/values.go b/pkg/charts/multicluster/values.go
index c7a25b176..ac08c447b 100644
--- a/pkg/charts/multicluster/values.go
+++ b/pkg/charts/multicluster/values.go
@@ -46,6 +46,7 @@ type Values struct {
 	RemoteMirrorServiceAccount     bool   `json:"remoteMirrorServiceAccount"`
 	RemoteMirrorServiceAccountName string `json:"remoteMirrorServiceAccountName"`
 	TargetClusterName              string `json:"targetClusterName"`
+	GatewayServiceType             string `json:"gatewayServiceType"`
 }
 
 // NewInstallValues returns a new instance of the Values type.
diff --git a/pkg/flags/flags.go b/pkg/flags/flags.go
index 3f82c353d..b8e2dbc29 100644
--- a/pkg/flags/flags.go
+++ b/pkg/flags/flags.go
@@ -7,6 +7,8 @@ import (
 
 	"github.com/linkerd/linkerd2/pkg/version"
 	log "github.com/sirupsen/logrus"
+	"github.com/spf13/pflag"
+	"helm.sh/helm/v3/pkg/cli/values"
 	klog "k8s.io/klog/v2"
 )
 
@@ -64,3 +66,11 @@ func maybePrintVersionAndExit(printVersion bool) {
 	}
 	log.Infof("running version %s", version.Version)
 }
+
+// AddValueOptionsFlags adds flags used to override default values
+func AddValueOptionsFlags(f *pflag.FlagSet, v *values.Options) {
+	f.StringSliceVarP(&v.ValueFiles, "values", "f", []string{}, "specify values in a YAML file or a URL (can specify multiple)")
+	f.StringArrayVar(&v.Values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
+	f.StringArrayVar(&v.StringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
+	f.StringArrayVar(&v.FileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
+}
diff --git a/pkg/k8s/labels.go b/pkg/k8s/labels.go
index 23435c4c7..1e7493b69 100644
--- a/pkg/k8s/labels.go
+++ b/pkg/k8s/labels.go
@@ -357,11 +357,14 @@ const (
 	// store identity credentials.
 	MountPathEndEntity = MountPathBase + "/identity/end-entity"
 
+	// MountPathTLSBase is the path at which the TLS cert and key PEM files are mounted
+	MountPathTLSBase = MountPathBase + "/tls"
+
 	// MountPathTLSKeyPEM is the path at which the TLS key PEM file is mounted.
-	MountPathTLSKeyPEM = MountPathBase + "/tls/tls.key"
+	MountPathTLSKeyPEM = MountPathTLSBase + "/tls.key"
 
 	// MountPathTLSCrtPEM is the path at which the TLS cert PEM file is mounted.
-	MountPathTLSCrtPEM = MountPathBase + "/tls/tls.crt"
+	MountPathTLSCrtPEM = MountPathTLSBase + "/tls.crt"
 
 	// MountPathXtablesLock is the path at which the proxy init container mounts xtables
 	// This is necessary for xtables-legacy support
diff --git a/controller/identity/creds_watcher.go b/pkg/tls/creds_watcher.go
similarity index 70%
rename from controller/identity/creds_watcher.go
rename to pkg/tls/creds_watcher.go
index 9ee8222e4..d519f0afd 100644
--- a/controller/identity/creds_watcher.go
+++ b/pkg/tls/creds_watcher.go
@@ -1,4 +1,4 @@
-package identity
+package tls
 
 import (
 	"context"
@@ -12,14 +12,14 @@ const dataDirectoryLnName = "..data"
 
 // FsCredsWatcher is used to monitor tls credentials on the filesystem
 type FsCredsWatcher struct {
-	issuerPath string
-	EventChan  chan<- struct{}
-	ErrorChan  chan<- error
+	certPath  string
+	EventChan chan<- struct{}
+	ErrorChan chan<- error
 }
 
 // NewFsCredsWatcher constructs a FsCredsWatcher instance
-func NewFsCredsWatcher(issuerPath string, issuerEvent chan<- struct{}, issuerError chan<- error) *FsCredsWatcher {
-	return &FsCredsWatcher{issuerPath, issuerEvent, issuerError}
+func NewFsCredsWatcher(certPath string, updateEvent chan<- struct{}, errEvent chan<- error) *FsCredsWatcher {
+	return &FsCredsWatcher{certPath, updateEvent, errEvent}
 }
 
 // StartWatching starts watching the filesystem for cert updates
@@ -31,7 +31,7 @@ func (fscw *FsCredsWatcher) StartWatching(ctx context.Context) error {
 	defer watcher.Close()
 
 	// no point of proceeding if we fail to watch this
-	if err := watcher.Add(fscw.issuerPath); err != nil {
+	if err := watcher.Add(fscw.certPath); err != nil {
 		return err
 	}
 
@@ -43,12 +43,12 @@ LOOP:
 			// Watching the folder for create events as this indicates
 			// that the secret has been updated.
 			if event.Op&fsnotify.Create == fsnotify.Create &&
-				event.Name == filepath.Join(fscw.issuerPath, dataDirectoryLnName) {
+				event.Name == filepath.Join(fscw.certPath, dataDirectoryLnName) {
 				fscw.EventChan <- struct{}{}
 			}
 		case err := <-watcher.Errors:
 			fscw.ErrorChan <- err
-			log.Warnf("Error while watching %s: %s", fscw.issuerPath, err)
+			log.Warnf("Error while watching %s: %s", fscw.certPath, err)
 			break LOOP
 		case <-ctx.Done():
 			if err := ctx.Err(); err != nil {


Test patch:
diff --git a/cli/cmd/testdata/install_addon.golden b/cli/cmd/testdata/install_addon.golden
index f67a406ef..a5bdf9379 100644
--- a/cli/cmd/testdata/install_addon.golden
+++ b/cli/cmd/testdata/install_addon.golden
@@ -2884,6 +2884,7 @@ spec:
           httpGet:
             path: /api/health
             port: 3000
+        resources:
         securityContext:
           runAsUser: 472
         volumeMounts:
@@ -3316,6 +3317,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
@@ -3623,6 +3625,7 @@ spec:
           httpGet:
             path: /
             port: 13133
+        resources:
         volumeMounts:
         - mountPath: /conf
           name: linkerd-collector-config-val
@@ -3847,6 +3850,7 @@ spec:
           name: collection
         - containerPort: 16686
           name: ui
+        resources:
       - env:
         - name: LINKERD2_PROXY_LOG
           value: "warn,linkerd=info"
diff --git a/cli/cmd/testdata/install_controlplane_tracing_output.golden b/cli/cmd/testdata/install_controlplane_tracing_output.golden
index a05fbeaac..998216df0 100644
--- a/cli/cmd/testdata/install_controlplane_tracing_output.golden
+++ b/cli/cmd/testdata/install_controlplane_tracing_output.golden
@@ -2980,6 +2980,7 @@ spec:
           httpGet:
             path: /api/health
             port: 3000
+        resources:
         securityContext:
           runAsUser: 472
         volumeMounts:
@@ -3426,6 +3427,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
diff --git a/cli/cmd/testdata/install_custom_registry.golden b/cli/cmd/testdata/install_custom_registry.golden
index cfdd71db4..ec316c632 100644
--- a/cli/cmd/testdata/install_custom_registry.golden
+++ b/cli/cmd/testdata/install_custom_registry.golden
@@ -2877,6 +2877,7 @@ spec:
           httpGet:
             path: /api/health
             port: 3000
+        resources:
         securityContext:
           runAsUser: 472
         volumeMounts:
@@ -3309,6 +3310,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
diff --git a/cli/cmd/testdata/install_default.golden b/cli/cmd/testdata/install_default.golden
index 323bd70f7..ea2540e08 100644
--- a/cli/cmd/testdata/install_default.golden
+++ b/cli/cmd/testdata/install_default.golden
@@ -2877,6 +2877,7 @@ spec:
           httpGet:
             path: /api/health
             port: 3000
+        resources:
         securityContext:
           runAsUser: 472
         volumeMounts:
@@ -3309,6 +3310,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
diff --git a/cli/cmd/testdata/install_default_override_dst_get_nets.golden b/cli/cmd/testdata/install_default_override_dst_get_nets.golden
index 8d3c0b347..f02eb1182 100644
--- a/cli/cmd/testdata/install_default_override_dst_get_nets.golden
+++ b/cli/cmd/testdata/install_default_override_dst_get_nets.golden
@@ -2877,6 +2877,7 @@ spec:
           httpGet:
             path: /api/health
             port: 3000
+        resources:
         securityContext:
           runAsUser: 472
         volumeMounts:
@@ -3309,6 +3310,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
diff --git a/cli/cmd/testdata/install_grafana_existing.golden b/cli/cmd/testdata/install_grafana_existing.golden
index 3db78cce9..f6d709769 100644
--- a/cli/cmd/testdata/install_grafana_existing.golden
+++ b/cli/cmd/testdata/install_grafana_existing.golden
@@ -2988,6 +2988,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
diff --git a/cli/cmd/testdata/install_heartbeat_disabled_output.golden b/cli/cmd/testdata/install_heartbeat_disabled_output.golden
index aeec8e24f..cc967d3ba 100644
--- a/cli/cmd/testdata/install_heartbeat_disabled_output.golden
+++ b/cli/cmd/testdata/install_heartbeat_disabled_output.golden
@@ -2788,6 +2788,7 @@ spec:
           httpGet:
             path: /api/health
             port: 3000
+        resources:
         securityContext:
           runAsUser: 472
         volumeMounts:
@@ -3220,6 +3221,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
diff --git a/cli/cmd/testdata/install_helm_output.golden b/cli/cmd/testdata/install_helm_output.golden
index 03625c68a..5d53504e8 100644
--- a/cli/cmd/testdata/install_helm_output.golden
+++ b/cli/cmd/testdata/install_helm_output.golden
@@ -1012,6 +1012,7 @@ data:
         workloadNamespaceLabel: linkerd.io/workload-ns
       image:
         name: ghcr.io/linkerd/grafana
+        tag: null
       partials:
         global:
           cliVersion: ""
@@ -1097,6 +1098,15 @@ data:
           proxyInjectAnnotation: linkerd.io/inject
           proxyInjectDisabled: disabled
           workloadNamespaceLabel: linkerd.io/workload-ns
+      proxy:
+        resources: null
+      resources:
+        cpu:
+          limit: null
+          request: null
+        memory:
+          limit: null
+          request: null
     heartbeatResources: null
     heartbeatSchedule: 0 0 * * *
     identity:
@@ -1125,6 +1135,8 @@ data:
           values:
           - disabled
     prometheus:
+      alertManagers: null
+      alertRelabelConfigs: null
       args:
         config.file: /etc/prometheus/prometheus.yml
         log.level: info
@@ -1305,6 +1317,19 @@ data:
           proxyInjectAnnotation: linkerd.io/inject
           proxyInjectDisabled: disabled
           workloadNamespaceLabel: linkerd.io/workload-ns
+      proxy:
+        resources: null
+      remoteWrite: null
+      resources:
+        cpu:
+          limit: null
+          request: null
+        memory:
+          limit: null
+          request: null
+      ruleConfigMapMounts: null
+      scrapeConfigs: null
+      sideCarContainers: null
     proxyInjector:
       caBundle: test-proxy-injector-ca-bundle
       crtPEM: test-proxy-injector-crt-pem
@@ -3175,6 +3200,7 @@ spec:
           httpGet:
             path: /api/health
             port: 3000
+        resources:
         securityContext:
           runAsUser: 472
         volumeMounts:
@@ -3600,6 +3626,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
diff --git a/cli/cmd/testdata/install_helm_output_addons.golden b/cli/cmd/testdata/install_helm_output_addons.golden
index 641ed2729..d02b3009f 100644
--- a/cli/cmd/testdata/install_helm_output_addons.golden
+++ b/cli/cmd/testdata/install_helm_output_addons.golden
@@ -1018,6 +1018,7 @@ data:
         workloadNamespaceLabel: linkerd.io/workload-ns
       image:
         name: ghcr.io/linkerd/grafana
+        tag: null
       partials:
         global:
           cliVersion: ""
@@ -1103,6 +1104,15 @@ data:
           proxyInjectAnnotation: linkerd.io/inject
           proxyInjectDisabled: disabled
           workloadNamespaceLabel: linkerd.io/workload-ns
+      proxy:
+        resources: null
+      resources:
+        cpu:
+          limit: null
+          request: null
+        memory:
+          limit: null
+          request: null
     heartbeatResources: null
     heartbeatSchedule: 0 0 * * *
     identity:
@@ -1131,6 +1141,8 @@ data:
           values:
           - disabled
     prometheus:
+      alertManagers: null
+      alertRelabelConfigs: null
       args:
         config.file: /etc/prometheus/prometheus.yml
         log.level: info
@@ -1311,6 +1323,19 @@ data:
           proxyInjectAnnotation: linkerd.io/inject
           proxyInjectDisabled: disabled
           workloadNamespaceLabel: linkerd.io/workload-ns
+      proxy:
+        resources: null
+      remoteWrite: null
+      resources:
+        cpu:
+          limit: null
+          request: null
+        memory:
+          limit: null
+          request: null
+      ruleConfigMapMounts: null
+      scrapeConfigs: null
+      sideCarContainers: null
     proxyInjector:
       caBundle: test-proxy-injector-ca-bundle
       crtPEM: test-proxy-injector-crt-pem
@@ -1338,6 +1363,13 @@ data:
     tracing:
       collector:
         image: omnition/opencensus-collector:0.1.11
+        resources:
+          cpu:
+            limit: null
+            request: null
+          memory:
+            limit: null
+            request: null
       enabled: true
       global:
         cliVersion: ""
@@ -1425,6 +1457,13 @@ data:
         workloadNamespaceLabel: linkerd.io/workload-ns
       jaeger:
         image: jaegertracing/all-in-one:1.19.2
+        resources:
+          cpu:
+            limit: null
+            request: null
+          memory:
+            limit: null
+            request: null
       partials:
         global:
           cliVersion: ""
@@ -3355,6 +3394,7 @@ spec:
           httpGet:
             path: /api/health
             port: 3000
+        resources:
         securityContext:
           runAsUser: 472
         volumeMounts:
@@ -3780,6 +3820,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
@@ -4080,6 +4121,7 @@ spec:
           httpGet:
             path: /
             port: 13133
+        resources:
         volumeMounts:
         - mountPath: /conf
           name: linkerd-collector-config-val
@@ -4293,6 +4335,7 @@ spec:
           name: collection
         - containerPort: 16686
           name: ui
+        resources:
       - env:
         - name: LINKERD2_PROXY_LOG
           value: "warn,linkerd=info"
diff --git a/cli/cmd/testdata/install_helm_output_ha.golden b/cli/cmd/testdata/install_helm_output_ha.golden
index 09d07bafd..ac550c4f8 100644
--- a/cli/cmd/testdata/install_helm_output_ha.golden
+++ b/cli/cmd/testdata/install_helm_output_ha.golden
@@ -1018,6 +1018,7 @@ data:
         workloadNamespaceLabel: linkerd.io/workload-ns
       image:
         name: ghcr.io/linkerd/grafana
+        tag: null
       partials:
         global:
           cliVersion: ""
@@ -1103,6 +1104,8 @@ data:
           proxyInjectAnnotation: linkerd.io/inject
           proxyInjectDisabled: disabled
           workloadNamespaceLabel: linkerd.io/workload-ns
+      proxy:
+        resources: null
       resources:
         cpu:
           limit: ""
@@ -1150,6 +1153,8 @@ data:
           values:
           - disabled
     prometheus:
+      alertManagers: null
+      alertRelabelConfigs: null
       args:
         config.file: /etc/prometheus/prometheus.yml
         log.level: info
@@ -1330,6 +1335,9 @@ data:
           proxyInjectAnnotation: linkerd.io/inject
           proxyInjectDisabled: disabled
           workloadNamespaceLabel: linkerd.io/workload-ns
+      proxy:
+        resources: null
+      remoteWrite: null
       resources:
         cpu:
           limit: ""
@@ -1337,6 +1345,9 @@ data:
         memory:
           limit: 8192Mi
           request: 300Mi
+      ruleConfigMapMounts: null
+      scrapeConfigs: null
+      sideCarContainers: null
     proxyInjector:
       caBundle: test-proxy-injector-ca-bundle
       crtPEM: test-proxy-injector-crt-pem
diff --git a/cli/cmd/testdata/install_helm_output_ha_labels.golden b/cli/cmd/testdata/install_helm_output_ha_labels.golden
index b919e5f87..010331a76 100644
--- a/cli/cmd/testdata/install_helm_output_ha_labels.golden
+++ b/cli/cmd/testdata/install_helm_output_ha_labels.golden
@@ -1026,6 +1026,7 @@ data:
         workloadNamespaceLabel: linkerd.io/workload-ns
       image:
         name: ghcr.io/linkerd/grafana
+        tag: null
       partials:
         global:
           cliVersion: ""
@@ -1115,6 +1116,8 @@ data:
           proxyInjectAnnotation: linkerd.io/inject
           proxyInjectDisabled: disabled
           workloadNamespaceLabel: linkerd.io/workload-ns
+      proxy:
+        resources: null
       resources:
         cpu:
           limit: ""
@@ -1162,6 +1165,8 @@ data:
           values:
           - disabled
     prometheus:
+      alertManagers: null
+      alertRelabelConfigs: null
       args:
         config.file: /etc/prometheus/prometheus.yml
         log.level: info
@@ -1350,6 +1355,9 @@ data:
           proxyInjectAnnotation: linkerd.io/inject
           proxyInjectDisabled: disabled
           workloadNamespaceLabel: linkerd.io/workload-ns
+      proxy:
+        resources: null
+      remoteWrite: null
       resources:
         cpu:
           limit: ""
@@ -1357,6 +1365,9 @@ data:
         memory:
           limit: 8192Mi
           request: 300Mi
+      ruleConfigMapMounts: null
+      scrapeConfigs: null
+      sideCarContainers: null
     proxyInjector:
       caBundle: test-proxy-injector-ca-bundle
       crtPEM: test-proxy-injector-crt-pem
diff --git a/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden b/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
index 22ae2ffab..9aaf34445 100644
--- a/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
+++ b/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
@@ -1018,6 +1018,7 @@ data:
         workloadNamespaceLabel: linkerd.io/workload-ns
       image:
         name: ghcr.io/linkerd/grafana
+        tag: null
       partials:
         global:
           cliVersion: ""
@@ -1103,6 +1104,8 @@ data:
           proxyInjectAnnotation: linkerd.io/inject
           proxyInjectDisabled: disabled
           workloadNamespaceLabel: linkerd.io/workload-ns
+      proxy:
+        resources: null
       resources:
         cpu:
           limit: ""
@@ -1150,6 +1153,8 @@ data:
           values:
           - enabled
     prometheus:
+      alertManagers: null
+      alertRelabelConfigs: null
       args:
         config.file: /etc/prometheus/prometheus.yml
         log.level: info
@@ -1330,6 +1335,9 @@ data:
           proxyInjectAnnotation: linkerd.io/inject
           proxyInjectDisabled: disabled
           workloadNamespaceLabel: linkerd.io/workload-ns
+      proxy:
+        resources: null
+      remoteWrite: null
       resources:
         cpu:
           limit: ""
@@ -1337,6 +1345,9 @@ data:
         memory:
           limit: 8192Mi
           request: 300Mi
+      ruleConfigMapMounts: null
+      scrapeConfigs: null
+      sideCarContainers: null
     proxyInjector:
       caBundle: test-proxy-injector-ca-bundle
       crtPEM: test-proxy-injector-crt-pem
diff --git a/cli/cmd/testdata/install_no_init_container.golden b/cli/cmd/testdata/install_no_init_container.golden
index 03c478f0e..56791e3e5 100644
--- a/cli/cmd/testdata/install_no_init_container.golden
+++ b/cli/cmd/testdata/install_no_init_container.golden
@@ -2608,6 +2608,7 @@ spec:
           httpGet:
             path: /api/health
             port: 3000
+        resources:
         securityContext:
           runAsUser: 472
         volumeMounts:
@@ -3002,6 +3003,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
diff --git a/cli/cmd/testdata/install_output.golden b/cli/cmd/testdata/install_output.golden
index 9ab5c15b4..67d1bd146 100644
--- a/cli/cmd/testdata/install_output.golden
+++ b/cli/cmd/testdata/install_output.golden
@@ -2892,6 +2892,7 @@ spec:
           httpGet:
             path: /api/health
             port: 3000
+        resources:
         securityContext:
           runAsUser: 472
         volumeMounts:
@@ -3326,6 +3327,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
diff --git a/cli/cmd/testdata/install_prometheus_overwrite.golden b/cli/cmd/testdata/install_prometheus_overwrite.golden
index ce7791cc4..f837f9de7 100644
--- a/cli/cmd/testdata/install_prometheus_overwrite.golden
+++ b/cli/cmd/testdata/install_prometheus_overwrite.golden
@@ -2934,6 +2934,7 @@ spec:
           httpGet:
             path: /api/health
             port: 3000
+        resources:
         securityContext:
           runAsUser: 472
         volumeMounts:
@@ -3411,6 +3412,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
diff --git a/cli/cmd/testdata/install_proxy_ignores.golden b/cli/cmd/testdata/install_proxy_ignores.golden
index f564c63ae..af95de5f1 100644
--- a/cli/cmd/testdata/install_proxy_ignores.golden
+++ b/cli/cmd/testdata/install_proxy_ignores.golden
@@ -2877,6 +2877,7 @@ spec:
           httpGet:
             path: /api/health
             port: 3000
+        resources:
         securityContext:
           runAsUser: 472
         volumeMounts:
@@ -3309,6 +3310,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
diff --git a/cli/cmd/testdata/install_restricted_dashboard.golden b/cli/cmd/testdata/install_restricted_dashboard.golden
index 5e397c603..8e047133d 100644
--- a/cli/cmd/testdata/install_restricted_dashboard.golden
+++ b/cli/cmd/testdata/install_restricted_dashboard.golden
@@ -2809,6 +2809,7 @@ spec:
           httpGet:
             path: /api/health
             port: 3000
+        resources:
         securityContext:
           runAsUser: 472
         volumeMounts:
@@ -3241,6 +3242,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
diff --git a/cli/cmd/testdata/install_tracing.golden b/cli/cmd/testdata/install_tracing.golden
index f67a406ef..a5bdf9379 100644
--- a/cli/cmd/testdata/install_tracing.golden
+++ b/cli/cmd/testdata/install_tracing.golden
@@ -2884,6 +2884,7 @@ spec:
           httpGet:
             path: /api/health
             port: 3000
+        resources:
         securityContext:
           runAsUser: 472
         volumeMounts:
@@ -3316,6 +3317,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
@@ -3623,6 +3625,7 @@ spec:
           httpGet:
             path: /
             port: 13133
+        resources:
         volumeMounts:
         - mountPath: /conf
           name: linkerd-collector-config-val
@@ -3847,6 +3850,7 @@ spec:
           name: collection
         - containerPort: 16686
           name: ui
+        resources:
       - env:
         - name: LINKERD2_PROXY_LOG
           value: "warn,linkerd=info"
diff --git a/cli/cmd/testdata/install_tracing_overwrite.golden b/cli/cmd/testdata/install_tracing_overwrite.golden
index dfa768fd7..594021c97 100644
--- a/cli/cmd/testdata/install_tracing_overwrite.golden
+++ b/cli/cmd/testdata/install_tracing_overwrite.golden
@@ -2886,6 +2886,7 @@ spec:
           httpGet:
             path: /api/health
             port: 3000
+        resources:
         securityContext:
           runAsUser: 472
         volumeMounts:
@@ -3318,6 +3319,7 @@ spec:
             port: 9090
           initialDelaySeconds: 30
           timeoutSeconds: 30
+        resources:
         securityContext:
           runAsNonRoot: true
           runAsUser: 65534
@@ -3625,6 +3627,7 @@ spec:
           httpGet:
             path: /
             port: 13133
+        resources:
         volumeMounts:
         - mountPath: /conf
           name: linkerd-collector-config-val
@@ -3849,6 +3852,7 @@ spec:
           name: collection
         - containerPort: 16686
           name: ui
+        resources:
       - env:
         - name: LINKERD2_PROXY_LOG
           value: "warn,linkerd=info"
diff --git a/controller/webhook/server_test.go b/controller/webhook/server_test.go
index 0e93ce619..1e8e08827 100644
--- a/controller/webhook/server_test.go
+++ b/controller/webhook/server_test.go
@@ -3,6 +3,7 @@ package webhook
 import (
 	"bytes"
 	"context"
+	"crypto/tls"
 	"net/http"
 	"net/http/httptest"
 	"reflect"
@@ -12,14 +13,18 @@ import (
 	"github.com/linkerd/linkerd2/controller/k8s"
 )
 
+var mockHTTPServer = &http.Server{
+	Addr:      ":0",
+	TLSConfig: &tls.Config{},
+}
+
 func TestServe(t *testing.T) {
 	t.Run("with empty http request body", func(t *testing.T) {
 		k8sAPI, err := k8s.NewFakeAPI()
 		if err != nil {
 			panic(err)
 		}
-		testServer := &Server{nil, k8sAPI, nil, nil}
-
+		testServer := getConfiguredServer(mockHTTPServer, k8sAPI, nil, nil)
 		in := bytes.NewReader(nil)
 		request := httptest.NewRequest(http.MethodGet, "/", in)
 
@@ -37,8 +42,7 @@ func TestServe(t *testing.T) {
 }
 
 func TestShutdown(t *testing.T) {
-	server := &http.Server{Addr: ":0"}
-	testServer := &Server{server, nil, nil, nil}
+	testServer := getConfiguredServer(mockHTTPServer, nil, nil, nil)
 
 	go func() {
 		if err := testServer.ListenAndServe(); err != nil {