diff --git a/chart/files/mapcache.xml b/chart/files/mapcache.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c84abb6d4fb052ff60a5f63916b571cf3af59522
--- /dev/null
+++ b/chart/files/mapcache.xml
@@ -0,0 +1,131 @@
+<mapcache>
+  <default_format>mixed</default_format>
+  <format name="mypng" type="PNG">
+    <compression>fast</compression>
+  </format>
+  <format name="myjpeg" type="JPEG">
+    <quality>75</quality>
+    <photometric>ycbcr</photometric>
+  </format>
+  <format name="mixed" type="MIXED">
+    <transparent>mypng</transparent>
+    <opaque>myjpeg</opaque>
+  </format>
+  {{- if .Values.config.cache.services.wms.enabled }}
+  <service type="wms" enabled="true">
+    <full_wms>assemble</full_wms>
+    <resample_mode>bilinear</resample_mode>
+    <format>mixed</format>
+    <maxsize>4096</maxsize>
+  </service>
+  {{- end }}
+  {{- if .Values.config.cache.services.wmts.enabled }}
+  <service type="wmts" enabled="true"/>
+  {{- end }}
+  {{- with .Values.config.cache.metadata }}
+  <metadata>
+    <title>{{ .title }}</title>
+    <abstract>{{ .abstract }}</abstract>
+    <url>{{ .url }}</url>
+    <keyword>{{ .keyword }}</keyword>
+    <accessconstraints>{{ .accessconstraints }}</accessconstraints>
+    <fees>{{ .fees }}</fees>
+    <contactname>{{ .contactname }}</contactname>
+    <contactphone>{{ .contactphone }}</contactphone>
+    <contactfacsimile>{{ .contactfacsimile }}</contactfacsimile>
+    <contactorganization>{{ .contactorganization }}</contactorganization>
+    <contactcity>{{ .contactcity }}</contactcity>
+    <contactstateorprovince>{{ .contactstateorprovince }}</contactstateorprovince>
+    <contactpostcode>{{ .contactpostcode }}</contactpostcode>
+    <contactcountry>{{ .contactcountry }}</contactcountry>
+    <contactelectronicmailaddress>{{ .contactelectronicmailaddress }}</contactelectronicmailaddress>
+    <contactposition>{{ .contactposition }}</contactposition>
+    <providername>{{ .providername }}</providername>
+    <providerurl>{{ .providerurl }}</providerurl>
+    <inspire_profile>{{ .inspire_profile }}</inspire_profile>
+    <inspire_metadataurl>{{ .inspire_metadataurl }}</inspire_metadataurl>
+    <defaultlanguage>{{ .defaultlanguage }}</defaultlanguage>
+    <language>{{ .language }}</language>
+  </metadata>
+  {{- end }}
+  <errors>empty_img</errors>
+  <lock_dir>/tmp</lock_dir>
+  <threaded_fetching>true</threaded_fetching>
+
+  <!-- Cache -->
+  {{- if eq .Values.config.objectStorage.cache.type "swift" }}
+  <cache name="cache" type="swift">
+    <auth_url>{{ .Values.config.objectStorage.cache.auth_url_short }}</auth_url>
+    <auth_version>{{ .Values.config.objectStorage.cache.auth_version }}</auth_version>
+    <tenant>{{ .Values.config.objectStorage.cache.tenant_id }}</tenant>
+    <username>{{ .Values.config.objectStorage.cache.username }}</username>
+    <password>{{ .Values.config.objectStorage.cache.password }}</password>
+    <container>{{ .Values.config.objectStorage.cache.container }}</container>
+    <key>{{ .Values.config.objectStorage.key | default "/{tileset}/{grid}/{dim}/{z}/{x}/{y}.{ext}" }}</key>
+  </cache>
+  {{- else if eq .Values.config.objectStorage.cache.type "S3"}} # TODO
+  {{- else }}
+  <!-- no storage configured -->
+  {{- end }}
+
+  {{- define "mapcache-layerid" -}}{{ .collection_name }}{{ if .level_name }}__{{ .level_name }}{{ end }}{{ if .sub_layer_name }}__{{ .sub_layer_name }}{{ end }}{{- end }}
+
+  {{- range $collection_name, $collection := .Values.config.collections -}}
+
+  {{- $sub_types := list nil -}}
+  {{- range $product_type_name := $collection.product_types }}
+  {{- $sub_types := concat $sub_types (get (get $.Values.config.products.types $product_type_name | default dict) "browses" | default dict | keys) }}
+  {{- $sub_types := concat $sub_types (get (get $.Values.config.products.types $product_type_name | default dict) "masks" | default dict | keys) }}
+  {{- end -}}
+
+  <!-- Sources for {{ $collection_name }} -->
+  {{- range $level_name := (concat (list nil) $collection.product_levels) }}
+  {{- range $sub_type_name := ($sub_types | uniq) }}
+  {{- $layer_id := (include "mapcache-layerid" (dict "collection_name" $collection_name "level_name" $level_name "sub_type_name" $sub_type_name)) | trim }}
+  <source type="wms" name="{{ $layer_id }}">
+    <getmap>
+      <params>
+        <LAYERS>{{ $layer_id }}</LAYERS>
+        <TRANSPARENT>true</TRANSPARENT>
+        <STYLES>{{ get (get $.Values.config.cache.tilesets $layer_id | default dict) "style" }}</STYLES>
+      </params>
+    </getmap>
+    <http>
+      <url>http://renderer/ows</url>
+      <connection_timeout>{{ $.Values.config.cache.connection_timeout | default 10 }}</connection_timeout>
+      <timeout>{{ $.Values.config.cache.timeout | default 120 }}</timeout>
+    </http>
+  </source>
+  {{- end }}
+  {{- end }}
+
+  <!-- Tilesets for {{ $collection_name }} -->
+  {{- range $level_name := (concat (list nil) $collection.product_levels) }}
+  {{- range $sub_type_name := ($sub_types | uniq) }}
+  {{- $layer_id := (include "mapcache-layerid" (dict "collection_name" $collection_name "level_name" $level_name "sub_type_name" $sub_type_name)) | trim }}
+  <tileset name="{{ $layer_id }}">
+    <metadata>
+      <title>{{ (get $.Values.config.cache.tilesets $layer_id | default dict ).title }}</title>
+      <abstract>{{ (get $.Values.config.cache.tilesets $layer_id | default dict ).abstract }}</abstract>
+    </metadata>
+    <source>{{ $layer_id }}</source>
+    <cache>cache</cache>
+    <grid max-cached-zoom="15" out-of-zoom-strategy="reassemble">WGS84</grid>
+    <format>mixed</format>
+    <metatile>1 1</metatile>
+    <expires>{{ $.Values.config.cache.expires | default 3600 }}</expires>
+    <dimensions>
+      <assembly_type>stack</assembly_type>
+      <store_assemblies>false</store_assemblies>
+      <subdimensions_read_only>false</subdimensions_read_only>
+      <dimension type="postgresql" name="time" default="2017/2019" time="true" unit="ISO8601">
+        <connection>host={{ $.Values.config.database.host }} user={{ $.Values.config.database.user }} password={{ $.Values.config.database.password }} dbname={{ $.Values.config.database.name }} port={{ $.Values.config.database.port | default 5432 }}</connection>
+        <list_query>SELECT to_char(MIN(mapcache_items.begin_time), 'YYYY-MM-DD"T"HH24:MI:SS"Z"') || '/' || to_char(MAX(mapcache_items.end_time), 'YYYY-MM-DD"T"HH24:MI:SS"Z"') FROM mapcache_items WHERE mapcache_items.collection = '{{ $layer_id }}';</list_query>
+        <validate_query>SELECT * FROM (SELECT to_char(mapcache_items.begin_time, 'YYYY-MM-DD"T"HH24:MI:SS"Z"') || '/' || to_char(mapcache_items.end_time, 'YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "interval" FROM mapcache_items WHERE (mapcache_items.collection = '{{ $layer_id }}' AND ((mapcache_items."begin_time" &lt; to_timestamp(:end_timestamp) AND mapcache_items."end_time" &gt; to_timestamp(:start_timestamp)) or (mapcache_items."begin_time" = mapcache_items."end_time" AND mapcache_items."begin_time" &lt;= to_timestamp(:end_timestamp) AND mapcache_items."end_time" &gt;= to_timestamp(:start_timestamp)))) AND mapcache_items."footprint" &amp;&amp; ST_MakeEnvelope(:minx, :miny, :maxx, :maxy, 4326) ORDER BY mapcache_items."end_time" DESC LIMIT 20) AS sub ORDER BY interval ASC;</validate_query>
+      </dimension>
+    </dimensions>
+  </tileset>
+  {{- end }}
+  {{- end }}
+</mapcache>
+{{- end -}}
\ No newline at end of file
diff --git a/chart/templates/cache-configmap.yaml b/chart/templates/cache-configmap.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..d72924e9c2aef9db31325844187e43e83613a32a
--- /dev/null
+++ b/chart/templates/cache-configmap.yaml
@@ -0,0 +1,6 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: {{ include "vs.fullname" . }}-cache
+data:
+  {{ (tpl (.Files.Glob "files/mapcache.xml").AsConfig . ) | nindent 2}}