diff --git a/cicd_playbook.yml b/cicd_playbook.yml
index 6a76a9617daa7fade1d2e2e3245c9e27e6181e93..6d561146d6731a168b3a96767ad9193843e83f2b 100644
--- a/cicd_playbook.yml
+++ b/cicd_playbook.yml
@@ -3,6 +3,7 @@
   connection: local
   gather_facts: no
   vars:
+    DEBMIRROR: http://debcache.mpifr-bonn.mpg.de/ubuntu/
     register_as_pipeline: False
     docker_registry: eddinfra0
     docker_registry_port: 5000
diff --git a/playbooks/basic_configuration.yml b/playbooks/basic_configuration.yml
index 864b87f9ac3974ab6b4a5e26190beb507d647e76..a67bce1d41b4bb2e65299d59c6e7b0a655fdf3e9 100644
--- a/playbooks/basic_configuration.yml
+++ b/playbooks/basic_configuration.yml
@@ -157,3 +157,15 @@
       - redis_sidecar
   roles:
     - pipeline_builder
+
+
+- name: integrate to monitoring service
+  hosts: monitor_service
+  gather_facts: no
+
+  roles:
+    - monitoring_integration
+  tags:
+    - monitoring_integration
+
+
diff --git a/roles/ansible_interface/tasks/main.yml b/roles/ansible_interface/tasks/main.yml
index f43e6e98f46c2dc4bf5c2a4d7b71d39d894a98f5..48029ec057cb5e0b7ca3295dbfad2dee1d8ed802 100644
--- a/roles/ansible_interface/tasks/main.yml
+++ b/roles/ansible_interface/tasks/main.yml
@@ -63,7 +63,8 @@
     network_mode: host        # host as need access to network for introspection
     pull: yes
     privileged: yes
-    auto_remove: yes
+    auto_remove: no
+    restart_policy: "always"
     force_kill: yes # avoid lag between rm and restart
     volumes:
           - "/var/run/docker.sock:/var/run/docker.sock"
diff --git a/roles/edd_base/defaults/main.yml b/roles/edd_base/defaults/main.yml
index daef02617cb1122e4a4119d68f267f35b3cc3e43..5a6f9586af394b970051b89b77b4ed8f6cadd0ca 100644
--- a/roles/edd_base/defaults/main.yml
+++ b/roles/edd_base/defaults/main.yml
@@ -7,7 +7,7 @@ gcc_architecture: "native"      # Architecture to use for GCC optimizations. Set
 baseimage: "nvidia/cuda:11.0.3-devel-ubuntu{{UBUNTU_VERSION}}"
 baseimage_gpuless: "ubuntu:{{UBUNTU_VERSION}}"
 psrdada_version: bf756866898686065562ac537376cf9d7d1b54ee
-spead2_tag: v1.8.0
-mkrecv_tag: f55b8d40fee730a091266ee7d52d3d74b4c32f0a
-mksend_tag: 648e7d3242b097dca82ec0ef4851deec17edbdfc
-psrdadacpp_tag: a2e4755fd88c2d2711543b5efa199e57644e4945
+spead2_tag: v3.11.1
+mkrecv_tag: 65a520d21bd7354deac01d7c667b4b49564152ab
+mksend_tag: 47c064942d3b7f90eb336d6e5701f6b8bd09ee57
+psrdadacpp_tag: 5fc008fb271647d227a2d5d2164151dcacf0a6ed
diff --git a/roles/edd_base/files/mkrecv_fix_issue_12.diff b/roles/edd_base/files/mkrecv_fix_issue_12.diff
deleted file mode 100644
index 24a028965abd05e161774c4e5372215760797c97..0000000000000000000000000000000000000000
--- a/roles/edd_base/files/mkrecv_fix_issue_12.diff
+++ /dev/null
@@ -1,39 +0,0 @@
-diff --git a/mkrecv_options.cpp b/mkrecv_options.cpp
-index bc35998..77ca04f 100644
---- a/mkrecv_options.cpp
-+++ b/mkrecv_options.cpp
-@@ -159,7 +159,7 @@ namespace mkrecv
-     time_t start_utc;
-     struct tm *nowtm;
-     char tbuf[64];
--    char utc_string[64];
-+    char utc_string[128];
- 
-     tv.tv_sec = integral;
-     tv.tv_usec = (int) (fractional*1e6);
-diff --git a/mkrecv_rnt.cpp b/mkrecv_rnt.cpp
-index c5c5d5d..6014004 100644
---- a/mkrecv_rnt.cpp
-+++ b/mkrecv_rnt.cpp
-@@ -444,7 +444,7 @@ namespace mkrecv
-     time_t start_utc;
-     struct tm *nowtm;
-     char tbuf[64];
--    char utc_string[64];
-+    char utc_string[128];
- 
-     tv.tv_sec = integral;
-     tv.tv_usec = (int) (fractional*1e6);
-diff --git a/mkrecv_v4.cpp b/mkrecv_v4.cpp
-index 858272f..a1c68c2 100644
---- a/mkrecv_v4.cpp
-+++ b/mkrecv_v4.cpp
-@@ -450,7 +450,7 @@ void signal_handler(int v);
-     uint64_t   start_ps;
-     struct tm *nowtm;
-     char       tbuf[64];
--    char       utc_string[64];
-+    char       utc_string[128];
-     char       ps_string[64];
- 
-     if (floor(sample_clock) == ceil(sample_clock)) {
diff --git a/roles/edd_base/files/mksend_fix_issue_9.diff b/roles/edd_base/files/mksend_fix_issue_9.diff
deleted file mode 100644
index dbc3fc2332d80672acf7496a37c12ef3890b4478..0000000000000000000000000000000000000000
--- a/roles/edd_base/files/mksend_fix_issue_9.diff
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/mksend_options.cpp b/mksend_options.cpp
-index f2ada4f..4d37df3 100644
---- a/mksend_options.cpp
-+++ b/mksend_options.cpp
-@@ -159,7 +159,7 @@ namespace mksend
-     time_t start_utc;
-     struct tm *nowtm;
-     char tbuf[64];
--    char utc_string[64];
-+    char utc_string[128];
- 
-     tv.tv_sec = integral;
-     tv.tv_usec = (int) (fractional*1e6);
diff --git a/roles/edd_base/templates/Dockerfile b/roles/edd_base/templates/Dockerfile
index 22c6bf436d7675f71c67040575ee9eccce13672f..e41c36253e6756c97727309542448f57c40fd5e2 100644
--- a/roles/edd_base/templates/Dockerfile
+++ b/roles/edd_base/templates/Dockerfile
@@ -72,7 +72,7 @@ RUN mkdir /src && \
 
 ### PSRDADA
 RUN cd /src && \
-    git clone git://git.code.sf.net/p/psrdada/code psrdada && \
+    git clone https://git.code.sf.net/p/psrdada/code psrdada && \
     cd psrdada && \
     git checkout {{ psrdada_version }}&& \
     # Version from  Wed Feb 12 07:46:37 2020 +0200 \
@@ -87,100 +87,85 @@ RUN cd /src && \
 RUN cd /src && \
     git clone https://gitlab.mpcdf.mpg.de/mpifr-bdg/psrdada_cpp.git && \
     cd psrdada_cpp/ &&\
-    ### Could be lib for lib layout in VLBI mode \
     git checkout {{ psrdadacpp_tag }}&& \
-    git submodule init &&\
-    git submodule update &&\
-    mkdir build/ &&\
-    cd build/ &&\
-    cmake -DENABLE_CUDA=True -DARCH={{gcc_architecture}} -DPSRDADA_INCLUDE_DIR=/usr/local/include/psrdada .. &&\
-    make -j 8 &&\
-    make test;\
-		make install
+    cmake -S . -B build/ -DENABLE_CUDA=True -DARCH={{gcc_architecture}} -DPSRDADA_INCLUDE_DIR=/usr/local/include/psrdada .. &&\
+    make -C build/ -j8 && make -C build/ install
+
+### SPEAD 2 dependencies
+RUN   cd /src && \
+    apt-get --no-install-recommends -y install \
+        python3-jinja2 \
+        python3-pycparser \
+        python3-packaging \
+        libdivide-dev
 
 ### SPEAD 2 \
  RUN  cd /src && \
     git clone https://github.com/ska-sa/spead2.git && \
     cd spead2 && \
     git checkout {{ spead2_tag }}  && \
-    # v1.8.0 or later is required by mksend \
-    # v1.9.2 does not work with the ibverbs version MLNX_OFED-4.1-1.0.2.0 used above \
     ./bootstrap.sh --no-python && \
     ./configure && \
     make -j8 &&\
     make install
 
-COPY mkrecv_fix_issue_12.diff /tmp
-
 ### MKRECV \
 RUN  cd /src && \
     git clone https://gitlab.mpifr-bonn.mpg.de/mhein/mkrecv.git && \
     cd mkrecv &&\
     git checkout {{ mkrecv_tag }}  && \
-    git apply /tmp/mkrecv_fix_issue_12.diff && \
-    mkdir build &&\
-    cd build && \
-    # TODO: AVOID LOCAL PATHS
-    cmake .. -DENABLE_CUDA=TRUE -DPSRDADA_CPP_INCLUDE_DIR=/src/psrdada_cpp/ -DPSRDADA_CPP_LIBRARIES=/src/psrdada_cpp/build/psrdada_cpp/libpsrdada_cpp.a -DPSRDADA_INCLUDE_DIR=/usr/local/include/psrdada -DARCH={{gcc_architecture}}&& \
-    make -j8 &&\
-    make install
-
-COPY mksend_fix_issue_9.diff /tmp
+    cmake -S . -B build/ -DENABLE_CUDA=TRUE -DPSRDADA_CPP_INCLUDE_DIR=/src/psrdada_cpp/ -DPSRDADA_CPP_LIBRARIES=/src/psrdada_cpp/build/psrdada_cpp/libpsrdada_cpp.a -DPSRDADA_INCLUDE_DIR=/usr/local/include/psrdada -DARCH={{gcc_architecture}}&& \
+    make -C build/ -j8 && make -C build/ install
 
 ### MKSEND \
 RUN cd /src && \
     git clone https://gitlab.mpifr-bonn.mpg.de/mhein/mksend.git && \
     cd mksend && \
     git checkout {{ mksend_tag }}  && \
-    git apply /tmp/mksend_fix_issue_9.diff && \
-    mkdir build &&\
-    cd build && \
-    cmake .. -DENABLE_CUDA=TRUE -DPSRDADA_CPP_INCLUDE_DIR=/src/psrdada_cpp/ -DPSRDADA_CPP_LIBRARIES=/src/psrdada_cpp/build/psrdada_cpp/libpsrdada_cpp.a -DPSRDADA_INCLUDE_DIR=/usr/local/include/psrdada -DARCH={{gcc_architecture}} && \
-    make -j8 &&\
-    make install
+    cmake -S . -B build/ -DENABLE_CUDA=TRUE -DPSRDADA_CPP_INCLUDE_DIR=/src/psrdada_cpp/ -DPSRDADA_CPP_LIBRARIES=/src/psrdada_cpp/build/psrdada_cpp/libpsrdada_cpp.a -DPSRDADA_INCLUDE_DIR=/usr/local/include/psrdada -DARCH={{gcc_architecture}} && \
+    make -C build/ -j8 && make -C build/ install
 
  ### MPIKAT dependencies\
 RUN   cd /src && \
     apt-get --no-install-recommends -y install \
-      libhdf5-dev \
-      python3-h5py \
-	    python3-setuptools \
-	    python3-setuptools-git \
-	    python3-setuptools-scm \
-	    python3-pip \
-	    python3-pil \
-      python3-jinja2 \
-      python3-cmd2 \
-      libpng-dev \
-      pkg-config \
-      libfreetype6-dev \
-      python3-scipy \
-      python3-pytest \
-      python3-posix-ipc \
-      python3-redis \
-      python3-numpy \
-      python3-netaddr \
-      python3-astropy \
-      python3-future \
-      python3-git \
-      python3-coloredlogs \
-      python3-yaml \
-      &&\
-      \
-  pip3 install --no-deps \
-      omnijson==0.1.2 \
-      ujson==2.0.3 \
-      katpoint==0.9 \
-      katcp==0.9.1 \
-      kiwisolver==1.1.0 \
-      nvector==0.7.4 \
-      ephem==3.7.7.0 \
-      spead2==1.8.0 \
-      tornado==4.5.3 \
-	  tomli==1.0.0 \
-	  typing-extensions==3.7.4.3\
-	  setuptools_scm==7.0.5 \
-      && \
+        libhdf5-dev \
+        python3-h5py \
+        python3-setuptools \
+        python3-setuptools-git \
+        python3-setuptools-scm \
+        python3-pip \
+        python3-pil \
+        python3-cmd2 \
+        libpng-dev \
+        pkg-config \
+        libfreetype6-dev \
+        python3-scipy \
+        python3-pytest \
+        python3-posix-ipc \
+        python3-redis \
+        python3-numpy \
+        python3-netaddr \
+        python3-astropy \
+        python3-future \
+        python3-git \
+        python3-coloredlogs \
+        python3-yaml \
+        &&\
+        \
+    pip3 install --no-deps \
+        omnijson==0.1.2 \
+        ujson==2.0.3 \
+        katpoint==0.9 \
+        katcp==0.9.1 \
+        kiwisolver==1.1.0 \
+        nvector==0.7.4 \
+        ephem==3.7.7.0 \
+        spead2==3.11.1 \
+        tornado==4.5.3 \
+        tomli==1.0.0 \
+        typing-extensions==3.7.4.3\
+        setuptools_scm==7.0.5 \
+        && \
     echo 'DONE!'
 
 WORKDIR /root
diff --git a/roles/edd_base/templates/Dockerfile_gpuless b/roles/edd_base/templates/Dockerfile_gpuless
index 52de2f788851898849a3bc3cdf6c691e4c1201a1..bdd0e4e23f5a8e8a3d28d78a4724d620c3c6c581 100644
--- a/roles/edd_base/templates/Dockerfile_gpuless
+++ b/roles/edd_base/templates/Dockerfile_gpuless
@@ -66,6 +66,14 @@ RUN mkdir /src && \
       gnupg
 
 
+### SPEAD 2 dependencies
+RUN   cd /src && \
+    apt-get --no-install-recommends -y install \
+        python3-jinja2 \
+        python3-pycparser \
+        python3-packaging \
+        libdivide-dev
+
 ### SPEAD 2 \
  RUN  cd /src && \
     git clone https://github.com/ska-sa/spead2.git && \
@@ -89,7 +97,6 @@ RUN   cd /src && \
 	    python3-setuptools-scm \
 	    python3-pip \
 	    python3-pil \
-      python3-jinja2 \
       python3-cmd2 \
       libpng-dev \
       pkg-config \
@@ -115,7 +122,7 @@ RUN   cd /src && \
       kiwisolver==1.1.0 \
       nvector==0.7.4 \
       ephem==3.7.7.0 \
-      spead2==1.8.0 \
+      spead2==3.11.1 \
       tornado==4.5.3 \
 	  tomli==2.0.1 \
 	  typing-extensions==3.7.4.3 \
diff --git a/roles/gs_plotter/tasks/main.yml b/roles/gs_plotter/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b4cbe382bcae37e3acb2aac5b2dc05aad7b489ad
--- /dev/null
+++ b/roles/gs_plotter/tasks/main.yml
@@ -0,0 +1,17 @@
+---
+- name: Plotter for Gated Spectrometer
+  include_role:
+    name: EDD.core.common
+  vars:
+    image_name: gs_plotter
+    default_container_name: gs_plotter
+    container_cmd: "python /src/mpikat/mpikat/pipelines/gs_plotter.py --host={{ edd_subnet }} --port={{ bind_port }}"
+    measurement: "gated_spectrometer"
+    dashboard_is_live: true
+    dashboard_refresh: "30s"
+    dashboard_panels:
+      - "spectrum_plotter.json"
+    dashboard_vars:
+      - "spectrum_plotter_vars.json"
+  tags:
+    - always
diff --git a/roles/gs_plotter/templates/Dockerfile b/roles/gs_plotter/templates/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..717a656324953b5701d9208428ccfa90eabcc0fd
--- /dev/null
+++ b/roles/gs_plotter/templates/Dockerfile
@@ -0,0 +1,17 @@
+FROM {{docker_registry}}:{{docker_registry_port}}/eddbase_gpuless:{{version_tag}}
+MAINTAINER Niclas Esser "nesser@mpifr-bonn.mpg.de"
+
+# Suppress debconf warnings
+ENV DEBIAN_FRONTEND noninteractive
+
+# Switch account to root and adding user accounts and password
+USER root
+
+RUN mkdir -p /src && \
+  cd /src && \
+  git clone {{ mpikat_repository }} mpikat &&\
+  cd mpikat && \
+  git checkout {{ mpikat_branch }}&& \
+  pip3 install .
+
+WORKDIR /root
diff --git a/roles/gs_plotter/templates/spectrum_plotter.json b/roles/gs_plotter/templates/spectrum_plotter.json
new file mode 100644
index 0000000000000000000000000000000000000000..a4b50f343e0415e3315e7b0d7537021318898d39
--- /dev/null
+++ b/roles/gs_plotter/templates/spectrum_plotter.json
@@ -0,0 +1,120 @@
+{
+    "datasource": {
+      "type": "redis-datasource",
+      "uid": "redisdatastreams"
+    },
+    "description": "Plot of the latest spectrum of $data_streams",
+    "gridPos": {
+      "h": 8,
+      "w": 24,
+      "x": 0,
+      "y": 6
+    },
+    "id": 4,
+    "maxPerRow": 4,
+    "options": {
+      "config": "",
+      "data": "",
+      "layout": {
+        "autosize": true,
+        "margin": {
+          "b": 20,
+          "l": 40,
+          "r": 0,
+          "t": 0
+        },
+        "xaxis": {
+          "autorange": true,
+          "range": [
+            349500000,
+            350496096
+          ],
+          "type": "linear"
+        }
+      },
+      "onclick": "",
+      "script": "result = {}\ndata.series[0].fields.forEach((object) => {\n  result[object[\"name\"]] = object[\"values\"].buffer[0]\n});\n\nconst uint8Array = new Uint8Array(atob(result[\"data\"]).split('').map(char => char.charCodeAt(0)));\n// Create a DataView on the Uint8Array\nconst dataView = new DataView(uint8Array.buffer);\n// Get the number of elements in the array (1024 in this example)\nconst numElements = dataView.byteLength / 4;\n// Create a new Float32Array with the same number of elements\nconst x = new Float32Array(numElements);\n\nconst v_min = result[\"f_min\"];\nconst v_max = result[\"f_max\"];\nconst bw = v_max - v_min \nconst res = bw / numElements;\n\n\nconst y = new Float32Array(numElements);\n\nfor (let i = 0; i < numElements; i++) {\n  y[i] = dataView.getFloat32(i * 4, true); // true specifies little-endian byte order\n  x[i] = v_min + i * res\n}\nvar trace = {\n  y: Array.from(y),\n  x: Array.from(x)\n};\n\nreturn {data:[trace]};\n"
+    },
+    "pluginVersion": "9.3.2",
+    "repeat": "data_streams",
+    "repeatDirection": "h",
+    "targets": [
+      {
+        "command": "json.get",
+        "datasource": {
+          "type": "redis-datasource",
+          "uid": "redisdatastreams"
+        },
+        "hide": false,
+        "keyName": "$data_streams",
+        "path": "$",
+        "query": "",
+        "refId": "A",
+        "streaming": true,
+        "streamingCapacity": 1000,
+        "streamingDataType": "DataFrame",
+        "streamingInterval": 1000,
+        "type": "json"
+      }
+    ],
+    "title": "Current Spectrum of $data_streams",
+    "type": "ae3e-plotly-panel"
+  },
+  {
+    "datasource": {
+      "type": "redis-datasource",
+      "uid": "redisdatastreams"
+    },
+    "description": "Waterfall plot of $data_streams",
+    "gridPos": {
+      "h": 8,
+      "w": 24,
+      "x": 0,
+      "y": 38
+    },
+    "id": 7,
+    "maxPerRow": 4,
+    "options": {
+      "config": "",
+      "data": "",
+      "layout": {
+        "autosize": true,
+        "margin": {
+          "b": 20,
+          "l": 100,
+          "r": 0,
+          "t": 0
+        },
+        "xaxis": {
+          "autorange": true,
+          "range": [
+            348501945.52529186,
+            349501945.5252918
+          ],
+          "type": "linear"
+        }
+      },
+      "onclick": "window.updateVariables({query:{'var-project':'test'}, partial: true})",
+      "script": "// FUNCTIONS\nfunction parse_data(series)\n{\n    result = {}\n    series.fields.forEach((object) => {\n      result[object[\"name\"]] = object[\"values\"].buffer[0]\n    });\n    return result;\n}\n\nfunction remove_old_items(name, max_length)\n{\n    var keys = Object.keys(window.sessionStorage)\n        .filter(key => key.includes(name))\n        .sort();\n    for(let i = 0; i < keys.length - max_length; i++)\n    {\n        window.sessionStorage.removeItem(keys[i]);\n    }\n}\n\nfunction initialize_array(n_spectra, nfft)\n{\n    var data_array = new Array(n_spectra);\n    for(var i = 0; i < n_spectra; i++)\n    {\n      data_array[i] = new Array(nfft);\n    }\n    return data_array;\n}\n\nfunction seconds2timestamp(seconds) {\n    return new Date(parseInt(seconds)).toISOString();\n}\n\nfunction compute(n_spectra, f_min, f_max, name)\n{\n    var sorted = Object.keys(window.sessionStorage).sort();\n    var encoded = window.sessionStorage.getItem(sorted[0]);\n    var uint8Array = new Uint8Array(atob(encoded).split('').map(char => char.charCodeAt(0)));\n    var dataView = new DataView(uint8Array.buffer);\n    var nfft = dataView.byteLength / 4;\n    var bw = f_max - f_min\n    var res = bw / nfft;\n    var x = new Array(nfft);\n    var y = new Array(nfft);\n    var z = initialize_array(n_spectra, nfft);\n    for (var i = 0; i < nfft; i++) {\n        x[i] = f_min + i * res;\n        z[0][i] = dataView.getFloat32(i * 4, true); // true specifies little-endian byte order\n    }\n    var cnt = 0;\n    for(var i = 1; i < sorted.length; i++)\n    {\n        if(sorted[i].includes(name))\n        {\n            cnt = cnt + 1;\n            if(cnt >= n_spectra){\n                continue;\n            }\n            encoded = window.sessionStorage.getItem(sorted[i]);\n            uint8Array = new Uint8Array(atob(encoded).split('').map(char => char.charCodeAt(0)));\n            dataView = new DataView(uint8Array.buffer);\n            //y[i] = seconds2timestamp(sorted[i]);\n            y[cnt] = sorted[i].replace(name + \"/\",\"\").split(\".\")[0].replace(\".\");\n            for (var ii = 0; ii < nfft; ii++) {\n                z[cnt][ii] = dataView.getFloat32(ii * 4, true); // true specifies little-endian byte order\n            }\n        }\n    }\n    return {\"x\":x, \"y\":y, \"z\":z};\n}\n// VARIBALES\nvar n_spectra = parseInt(document.getElementById(\"var-n_spectra\").value);\nvar stream_name = data.request.scopedVars.data_streams.value.split(\":\")[1];\nvar res_dict = parse_data(data.series[0]);\nwindow.sessionStorage.setItem(\n    stream_name + \"/\" + res_dict[\"timestamp\"], res_dict[\"data\"]\n);\nremove_old_items(stream_name, n_spectra);\nvar result = compute(n_spectra, res_dict[\"f_min\"], res_dict[\"f_max\"], stream_name);\n\nvar trace = [{\n  x: result[\"x\"],\n  y: result[\"y\"],\n  z: result[\"z\"],\n  type: 'heatmap'\n}];\n\nreturn {data:trace};"
+    },
+    "repeat": "data_streams",
+    "repeatDirection": "h",
+    "targets": [
+      {
+        "command": "json.get",
+        "datasource": {
+          "type": "redis-datasource",
+          "uid": "redisdatastreams"
+        },
+        "keyName": "$data_streams",
+        "path": "$",
+        "query": "",
+        "refId": "A",
+        "streaming": true,
+        "streamingDataType": "DataFrame",
+        "type": "json"
+      }
+    ],
+    "title": "Waterfall plot of $data_streams",
+    "type": "ae3e-plotly-panel"
+  }
\ No newline at end of file
diff --git a/roles/gs_plotter/templates/spectrum_plotter_vars.json b/roles/gs_plotter/templates/spectrum_plotter_vars.json
new file mode 100644
index 0000000000000000000000000000000000000000..dc4232f2bb332b4e9c3b25fc4d42fdc523bef1b4
--- /dev/null
+++ b/roles/gs_plotter/templates/spectrum_plotter_vars.json
@@ -0,0 +1,96 @@
+{
+    "current": {
+      "selected": false,
+      "text": "32",
+      "value": "32"
+    },
+    "description": "The number of spectra displayed in the waterfall plot",
+    "hide": 0,
+    "label": "Number of Spectra",
+    "name": "n_spectra",
+    "options": [
+      {
+        "selected": false,
+        "text": "128",
+        "value": "128"
+      }
+    ],
+    "query": "32",
+    "skipUrlSync": false,
+    "type": "textbox"
+},
+{
+    "current": {
+      "selected": false,
+      "text": [
+        "(empty)"
+      ],
+      "value": [
+        "(empty)"
+      ]
+    },
+    "datasource": {
+      "type": "redis-datasource",
+      "uid": "redisdatastreams"
+    },
+    "definition": "KEYS {{ container_name }}*_stream",
+    "description": "Name of the data stream to subscribe to",
+    "hide": 0,
+    "includeAll": false,
+    "label": "Data stream names",
+    "multi": true,
+    "name": "data_streams",
+    "options": [],
+    "query": "KEYS {{ container_name }}*_stream",
+    "refresh": 1,
+    "regex": "",
+    "skipUrlSync": false,
+    "sort": 1,
+    "type": "query"
+},
+{
+    "auto": false,
+    "auto_count": 30,
+    "auto_min": "10s",
+    "current": {
+      "selected": false,
+      "text": "2000",
+      "value": "2000"
+    },
+    "description": "The interval of updating the plot.",
+    "hide": 0,
+    "label": "Update in ms",
+    "name": "update_ms",
+    "options": [
+      {
+        "selected": false,
+        "text": "1000",
+        "value": "1000"
+      },
+      {
+        "selected": true,
+        "text": "2000",
+        "value": "2000"
+      },
+      {
+        "selected": false,
+        "text": "3000",
+        "value": "3000"
+      },
+      {
+        "selected": false,
+        "text": "4000",
+        "value": "4000"
+      },
+      {
+        "selected": false,
+        "text": "5000",
+        "value": "5000"
+      }
+    ],
+    "query": "1000,2000,3000,4000,5000",
+    "queryValue": "",
+    "refresh": 2,
+    "skipUrlSync": false,
+    "type": "interval"
+}
\ No newline at end of file
diff --git a/roles/influx_sidecar/defaults/main.yml b/roles/influx_sidecar/defaults/main.yml
index e3d7577f99a5a917ecf9298a81f8822dffbe9a1c..43c102f1ec8e182b66c164d3e8fb3c9808948866 100644
--- a/roles/influx_sidecar/defaults/main.yml
+++ b/roles/influx_sidecar/defaults/main.yml
@@ -1,2 +1,2 @@
-interval: 60
+interval: 5
 strategy: "event-rate"
diff --git a/roles/mock_fits_writer/tasks/main.yml b/roles/mock_fits_writer/tasks/main.yml
index a6ce9d3126eb06691b0328b217550f236d60c1b6..ecf3a1e0d1343a33a39880220b6be6d2798ebddd 100644
--- a/roles/mock_fits_writer/tasks/main.yml
+++ b/roles/mock_fits_writer/tasks/main.yml
@@ -5,6 +5,6 @@
    vars:
       image_name: mock_fits_writer
       container_name: mock_fits_writer
-      container_cmd: "/usr/bin/python3 /src/mpikat/mpikat/pipelines/mock_fits_writer.py --host={{ edd_subnet }} --port={{ bind_port }} --fits_interface_host={{ interface_host }}"
+      container_cmd: "/usr/bin/python3 /src/mpikat/mpikat/pipelines/mock_fits_writer.py --host={{ edd_subnet }} --port={{ bind_port }}"
    tags:
      - always
diff --git a/roles/monitoring_integration/files/check_grafana_alerts b/roles/monitoring_integration/files/check_grafana_alerts
new file mode 100755
index 0000000000000000000000000000000000000000..bdeaad101eeb31d706c2d9d06f36ed4f415330e7
--- /dev/null
+++ b/roles/monitoring_integration/files/check_grafana_alerts
@@ -0,0 +1,108 @@
+#!/usr/bin/python3
+"""
+Check grafana for existing alerts and update according services as passive checks. Create services if needed. 
+"""
+import json
+import argparse
+import requests
+
+def get_username_and_password(ffile):
+    """
+    looks up username and passord of the ApiUser from the grafana config
+    """
+
+    username = None
+    password = None
+    for line in ffile:
+        if line.startswith('//'):
+            continue
+        if 'ApiUser' in line:
+            username = line.split()[2].strip("'").strip('"')
+        elif 'password' in line:
+            password = line.split('=')[1].strip().strip("'").strip('"')
+    return username, password
+
+def get_grafana_alerts(uri):
+    """Return list of configured grafana alerts
+    """
+
+    r = requests.get("{uri}/api/prometheus/grafana/api/v1/rules".format(uri=uri))
+
+    if not r.status_code == 200:
+        raise RuntimeError('ERROR querying grafana alerts. ({}, {})'.format(response.status, response.reason))
+
+    data = r.json()
+
+    alerts = []
+
+    for group in data['data']['groups']:
+        for alert in group['rules']:
+            alerts.append(alert)
+
+        #print(alert['name'], alert['lastEvaluation'], alert['state'])
+    return alerts
+
+
+
+def create_service(uri, alert, host, username, password):
+    """
+    Create a new service from the alert on the given host.
+    """
+    name = 'grafana_' + alert['name'].replace(' ', '_')
+    print('Creating new service: {name}'.format(name=name))
+    res = requests.put('{uri}/objects/services/{host}!{name}'.format(uri=uri, name=name, host=host), headers={'Accept': 'application/json'}, auth=requests.auth.HTTPBasicAuth(username, password), data=json.dumps({'templates': ['generic-service'],
+                                                                 "attrs": { "check_command": "passive",
+                                                                           "enable_passive_checks": True,
+                                                                           "enable_active_checks": False,
+                                                                           "display_name": alert['name'],
+                                                                           "groups": [ "EDD_services" ]
+                                                                           } }), verify=False)
+    print(json.dumps(res.json(), indent=4))
+    if res.status_code != 200:
+        raise RuntimeError('Cannot create service {name}:{res}'.format(name=name, res=res))
+
+
+def post_check_result(uri, alert, host, username, password):
+    name = 'grafana_' + alert['name'].replace(' ', '_')
+    print('Posting check result for {name}'.format(name=name))
+    if 'state' not in alert:
+        status = 3
+    elif alert['state'] == 'firing':
+        status = 2
+    else:
+        status = 0
+
+    message = alert['annotations'].get('message', 'Unknown reason')
+    res = requests.post('{uri}/actions/process-check-result'.format(uri=uri), headers={'Accept': 'application/json'},
+                       auth=requests.auth.HTTPBasicAuth(username, password), json={'type': 'Service',
+                                                                     'filter': "host.name==\"{host}\" && service.name==\"{name}\"".format(host=host, name=name),
+                                                                     "exit_status": status,
+                                                                     "plugin_output": message}, verify=False)
+    return res.json()
+
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('grafana_uri', type=str,
+                        help='grafana instance http://HOST:PORT to access')
+
+    parser.add_argument('icinga_uri', type=str,
+                        help='icinga instance http://HOST:PORT to access')
+
+    parser.add_argument('host', type=str, help='name of the icinga host to create the services on')
+    parser.add_argument('--icinga_file', default='/etc/icinga2/features-enabled/api.conf', help='API config file used to get user account and password')
+    args = parser.parse_args()
+
+    alerts = get_grafana_alerts(args.grafana_uri)
+
+    with open(args.icinga_file) as f:
+        username, password = get_username_and_password(f)
+
+    for alert in alerts:
+        res = post_check_result(args.icinga_uri, alert, args.host, username, password)
+        if not res['results']:
+            print('Cannot post result. Creating service')
+            create_service(args.icinga_uri, alert, args.host, username, password)
+
+
diff --git a/roles/monitoring_integration/files/check_katcp_sensor b/roles/monitoring_integration/files/check_katcp_sensor
new file mode 100755
index 0000000000000000000000000000000000000000..1cf195df8da39e6b7b028666210f923b0086d3e4
--- /dev/null
+++ b/roles/monitoring_integration/files/check_katcp_sensor
@@ -0,0 +1,72 @@
+#!/usr/bin/python3
+"""
+Check the status of katcp sensor and eventually raise an icinga warning.
+"""
+
+import argparse
+import socket
+import sys
+
+
+
+
+if __name__ == "__main__":
+    comparators = {'equal': lambda a,b: a == b, 'min': lambda a,b: a <= b, 'max': lambda a,b: a >= b }
+
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('-H', '--host', dest='host', type=str, default='0.0.0.0',
+                      help='Host interface to bind to')
+    parser.add_argument('-p', '--port', dest='port', type=int, default=1235,
+                      help='Port number to bind to')
+    parser.add_argument('-s', '--sensor', dest='sensor',
+                      help='Sensor to query')
+    parser.add_argument('-r', '--reference', dest='reference',
+                      help='Reference sensor value')
+
+    parser.add_argument('-c', '--comparator', dest='comparator', choices=list(comparators.keys()),
+                      help='Comparator for value', required=True)
+    args = parser.parse_args()
+
+
+    # get value from
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    sock.settimeout(10)
+    sock.connect((args.host, args.port))
+    sock.send("?sensor-value {}\n".format(args.sensor).encode('ascii'))
+
+    resp = ""
+
+    print("Checking katcp sensor: {} of {}:{}".format(args.sensor, args.host, args.port))
+    #print(f"Checking katcp sensor: {args.sensor} of {args.host}:{args.port}")
+    while True:
+        resp += sock.recv(4096).decode('ascii')
+
+        if '\n' not in resp:
+            continue
+
+        messages = resp.split('\n')
+        if not resp.endswith('\n'):
+            resp = messages.pop()
+        else:
+            resp = ''
+
+        for message in messages:
+            resp_split = message.split()
+
+            if message.startswith("!sensor-value"):
+                if resp_split[-2] == 'fail':
+                    print("Sensor response: {}".format(message))
+                    #print(f"Sensor response: {message}")
+                    sys.exit(2)
+
+            elif message.startswith("#sensor-value"):
+                print("Sensor response: {}".format(message))
+                #print(f"Sensor response: {message}")
+                if comparators[args.comparator](args.reference, resp_split[5]):
+                    print('Check failed - {} {} {}'.format(args.reference, args.comparator, resp_split[5]))
+                    #print(f'Check failed - {args.reference} {args.comparator} {resp_split[5]}')
+                    sys.exit(2)
+                else:
+                    print('Check passed - fail on {} {} {}'.format(args.reference, args.comparator, resp_split[5]))
+                    #print(f'Check passed - fail on {args.reference} {args.comparator} {resp_split[5]}')
+                    sys.exit(0)
diff --git a/roles/monitoring_integration/templates/commands.conf b/roles/monitoring_integration/templates/commands.conf
new file mode 100644
index 0000000000000000000000000000000000000000..a39aa5bb84a5025002acc7f4df8fc2dd7099dece
--- /dev/null
+++ b/roles/monitoring_integration/templates/commands.conf
@@ -0,0 +1,22 @@
+object CheckCommand "katcp_sensor" {
+  command = [ SysconfDir + "/icinga2/edd.d/check_katcp_sensor" ]
+  arguments = {
+    "-H" = "$katcp_host$"
+    "-p" = "$katcp_port$"
+    "-s" = "$katcp_sensor$"
+    "-r" = "$katcp_reference$"
+    "-c" = "$katcp_comparator$"
+  }
+  vars.katcp_host = ""
+  vars.katcp_port = "1235"
+  vars.katcp_sensor = ""
+  vars.katcp_reference = ""
+  vars.katcp_comparator = ""
+}
+
+object CheckCommand "grafana_alerts" {
+  command = [ SysconfDir + "/icinga2/edd.d/check_grafana_alerts", "$grafana_uri$", "$icinga_uri$", "$host$"  ]
+  vars.host = ""
+  vars.grafana_uri= ""
+  vars.icinga_uri = ""
+}
diff --git a/roles/monitoring_integration/templates/groups.conf b/roles/monitoring_integration/templates/groups.conf
new file mode 100644
index 0000000000000000000000000000000000000000..8ebb3536a3c4097fc86d4b935b562dc063fb0185
--- /dev/null
+++ b/roles/monitoring_integration/templates/groups.conf
@@ -0,0 +1,3 @@
+object ServiceGroup "EDD_services" {
+  display_name = "EDD Services"
+}
diff --git a/roles/monitoring_integration/templates/services.conf b/roles/monitoring_integration/templates/services.conf
new file mode 100644
index 0000000000000000000000000000000000000000..3a46f9e7154f3ce0cdab4c926a8522a9eef67301
--- /dev/null
+++ b/roles/monitoring_integration/templates/services.conf
@@ -0,0 +1,110 @@
+object Service "EDD_master_controler_katcp" {
+
+	import "generic-service"
+	host_name = "{{ interface_host }}." + DomainName
+
+	display_name = "master-controller katcp server"
+
+	check_command = "tcp"
+	vars.tcp_port = {{ master_controller_port }}
+	vars.tcp_expect = "katcp"
+
+	groups = [ "EDD_services" ]
+}
+
+object Service "EDD_master_controler_status" {
+
+	import "generic-service"
+	host_name = "{{ interface_host }}." + DomainName
+
+	display_name = "EDD master controller status"
+
+	check_command = "katcp_sensor"
+  vars.katcp_host = "{{ interface_host }}"
+  vars.katcp_port = {{ master_controller_port }}
+  vars.katcp_sensor = "pipeline-status"
+  vars.katcp_comparator = "equal"
+  vars.katcp_reference = "error"
+	groups = [ "EDD_services" ]
+}
+
+object Service "edd_grafana" {
+
+	import "generic-service"
+	host_name = "{{ grafana_host }}." + DomainName
+
+	display_name = "EDD grafana"
+
+	check_command = "http"
+	vars.http_string = "Grafana"
+	vars.http_port = 3000
+
+	groups = [ "EDD_services" ]
+}
+
+object Service "grafana_alerts" {
+
+	import "generic-service"
+	host_name = "{{ grafana_host }}." + DomainName
+
+	display_name = "EDD grafana alerts"
+
+	check_command = "grafana_alerts"
+	vars.grafana_uri= "http://{{ grafana_host }}:3000"
+	vars.icinga_uri = "https://localhost:5665/v1"
+	vars.host = "{{ grafana_host }}." + DomainName
+
+	groups = [ "EDD_services" ]
+}
+
+object Service "EDD_influx" {
+
+	import "generic-service"
+	host_name = "{{ influx_host }}." + DomainName
+
+	display_name = "EDD influx server"
+
+	check_command = "tcp"
+	vars.tcp_port = {{ influxdb_port }}
+
+	groups = [ "EDD_services" ]
+}
+
+object Service "EDD_Loki" {
+
+	import "generic-service"
+	host_name = "{{ loki_host }}." + DomainName
+
+	display_name = "EDD loki server"
+
+	check_command = "tcp"
+	vars.tcp_port = {{ loki_port }}
+
+	groups = [ "EDD_services" ]
+}
+
+object Service "EDD_REDIS" {
+
+	import "generic-service"
+	host_name = "{{ redis_host }}." + DomainName
+
+	display_name = "EDD redis server"
+
+	check_command = "tcp"
+	vars.tcp_port = {{ redis_port }}
+
+	groups = [ "EDD_services" ]
+}
+
+object Service "EDD_DOCKER" {
+
+	import "generic-service"
+	host_name = "{{ docker_registry }}." + DomainName
+
+	display_name = "EDD docker registry"
+
+	check_command = "tcp"
+	vars.tcp_port = {{ docker_registry_port }}
+
+	groups = [ "EDD_services" ]
+}
diff --git a/roles/packetizer/templates/packer-boot b/roles/packetizer/templates/packer-boot
new file mode 100644
index 0000000000000000000000000000000000000000..ee2d2142a8a2a121aa45391c37bc9727128dae5d
--- /dev/null
+++ b/roles/packetizer/templates/packer-boot
@@ -0,0 +1,3 @@
+### packer options
+PACKETIZER_OPTS="--acceptInvalidFreq"
+
diff --git a/roles/packetizer/templates/packer-boot.service b/roles/packetizer/templates/packer-boot.service
new file mode 100644
index 0000000000000000000000000000000000000000..9c1e19e5cef209b68e771357f5e61c195c8a4aa9
--- /dev/null
+++ b/roles/packetizer/templates/packer-boot.service
@@ -0,0 +1,16 @@
+[Unit]
+Description=Packer with program option after Reboot
+After=network-online.target
+Wants=network-online.target
+Before=packer.service
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+User=root
+EnvironmentFile=-/etc/default/packer-boot
+ExecStart=/usr/local/bin/packer $PACKETIZER_OPTS
+TimeoutStartSec=10
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/pulsar_pipeline/tasks/main.yml b/roles/pulsar_pipeline/tasks/main.yml
index 38e0b5e83d5ea202d83a297201876c03dd725b7c..279e25271298b4d015db9d7830afd5b05d0f8647 100644
--- a/roles/pulsar_pipeline/tasks/main.yml
+++ b/roles/pulsar_pipeline/tasks/main.yml
@@ -52,14 +52,20 @@
     register: additional_build_files
     check_mode: false
 
+  - name: Touch token file to create dummy if not exist
+    ansible.builtin.file:
+      path: "{{ inventory_dir }}/data/git_token"
+      state: touch
+
   - name: Copy git token
     copy:
       src: "{{ inventory_dir }}/data/git_token"
       dest: "{{ additional_build_files.path }}/git_token"
-      mode: go-rwx
       decrypt: yes
+      mode: go-rwx
     when: inventory_dir is defined
 
+
 - name: Pulsar Pipeline
   include_role:
      name: common
diff --git a/roles/ska_proto_status_server/tasks/main.yml b/roles/ska_proto_status_server/tasks/main.yml
index 526c3a29663ca4eddd57eca40c17f911a1a8002d..50f2cae0c9f4f1c5182e7826bc46ff75bc143bff 100644
--- a/roles/ska_proto_status_server/tasks/main.yml
+++ b/roles/ska_proto_status_server/tasks/main.yml
@@ -43,6 +43,39 @@
   tags:
     - dashboard
 
+
+
+- name: Setup processing data source
+  uri:
+    url: "http://{{ grafana_host }}:{{ grafana_port }}/api/datasources"
+    method: POST
+    headers:
+      Content-Type: "application/json;charset=UTF-8"
+      Accept: "application/json"
+    body: "{{ lookup('template', 'processing_db.json' ) }}"
+    body_format: json
+    status_code:
+      - 200
+      - 409
+  tags:
+    - datasource
+
+- name: Setup bucardo data source
+  uri:
+    url: "http://{{ grafana_host }}:{{ grafana_port }}/api/datasources"
+    method: POST
+    headers:
+      Content-Type: "application/json;charset=UTF-8"
+      Accept: "application/json"
+    body: "{{ lookup('template', 'processing_db.json' ) }}"
+    body_format: json
+    status_code:
+      - 200
+      - 409
+  tags:
+    - datasource
+
+
 - name: Setup Process-Bot-Dashboard
   uri:
     url: "http://{{ grafana_host }}:{{ grafana_port }}/api/dashboards/db"
diff --git a/roles/ska_proto_status_server/templates/bucardo_db.json b/roles/ska_proto_status_server/templates/bucardo_db.json
new file mode 100644
index 0000000000000000000000000000000000000000..3fd137547546515c021a286136dd3ffa2d56d7b1
--- /dev/null
+++ b/roles/ska_proto_status_server/templates/bucardo_db.json
@@ -0,0 +1,39 @@
+  {
+    "id": 10,
+    "uid": "IG15EBVSk",
+    "orgId": 1,
+    "name": "PostgreSQL",
+    "type": "postgres",
+    "typeName": "PostgreSQL",
+    "typeLogoUrl": "public/app/plugins/datasource/postgres/img/postgresql_logo.svg",
+    "access": "proxy",
+    "url": "srx02:5432",
+    "user": "skaprotoro",
+    "database": "processing",
+    "basicAuth": false,
+    "isDefault": false,
+    "jsonData": {
+      "maxOpenConns": 2,
+      "sslmode": "disable"
+    },
+    "readOnly": false
+  },
+  {
+    "id": 11,
+    "uid": "_PS92SFSk",
+    "orgId": 1,
+    "name": "PostgreSQL-BUCARDO",
+    "type": "postgres",
+    "typeName": "PostgreSQL",
+    "typeLogoUrl": "public/app/plugins/datasource/postgres/img/postgresql_logo.svg",
+    "access": "proxy",
+    "url": "srx02",
+    "user": "bucardo",
+    "database": "bucardo",
+    "basicAuth": false,
+    "isDefault": false,
+    "jsonData": {
+      "sslmode": "disable"
+    },
+    "readOnly": false
+  }
diff --git a/roles/ska_proto_status_server/templates/processing_db.json b/roles/ska_proto_status_server/templates/processing_db.json
new file mode 100644
index 0000000000000000000000000000000000000000..14ef3bbc186974ba8f1b5c78bc304910082f16a0
--- /dev/null
+++ b/roles/ska_proto_status_server/templates/processing_db.json
@@ -0,0 +1,19 @@
+{
+"name": "Processing DB",
+"type": "postgres",
+"typeName": "PostgreSQL",
+"typeLogoUrl": "public/app/plugins/datasource/postgres/img/postgresql_logo.svg",
+"access": "proxy",
+"url": "srx02:5432",
+"user": "skaprotoro",
+"password": "skaproto",
+"database": "processing",
+"basicAuth": false,
+"isDefault": false,
+"jsonData": {
+  "maxOpenConns": 2,
+  "sslmode": "disable"
+},
+"readOnly": false
+}
+
diff --git a/roles/ska_proto_status_server/templates/transfer_bot_dashboard.json b/roles/ska_proto_status_server/templates/transfer_bot_dashboard.json
new file mode 100644
index 0000000000000000000000000000000000000000..98eafe1ea4c31df5ba50477c5586ee2c6852f558
--- /dev/null
+++ b/roles/ska_proto_status_server/templates/transfer_bot_dashboard.json
@@ -0,0 +1,838 @@
+{
+  "annotations": {
+    "list": [
+      {
+        "builtIn": 1,
+        "datasource": {
+          "type": "grafana",
+          "uid": "-- Grafana --"
+        },
+        "enable": true,
+        "hide": true,
+        "iconColor": "rgba(0, 211, 255, 1)",
+        "name": "Annotations & Alerts",
+        "target": {
+          "limit": 100,
+          "matchAny": false,
+          "tags": [],
+          "type": "dashboard"
+        },
+        "type": "dashboard"
+      }
+    ]
+  },
+  "editable": true,
+  "fiscalYearStartMonth": 0,
+  "graphTooltip": 0,
+  "id": 2432,
+  "links": [],
+  "liveNow": false,
+  "panels": [
+    {
+      "datasource": {
+        "type": "postgres",
+        "uid": "_PS92SFSk"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [],
+          "max": 10800,
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "#EAB839",
+                "value": 3600
+              },
+              {
+                "color": "red",
+                "value": 7200
+              }
+            ]
+          },
+          "unit": "s"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 0
+      },
+      "id": 2,
+      "options": {
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "showThresholdLabels": false,
+        "showThresholdMarkers": true
+      },
+      "pluginVersion": "9.3.2",
+      "targets": [
+        {
+          "datasource": {
+            "type": "postgres",
+            "uid": "_PS92SFSk"
+          },
+          "editorMode": "code",
+          "format": "table",
+          "rawQuery": true,
+          "rawSql": "SELECT $__time(started), EXTRACT(EPOCH FROM(now() - ended)) as duration FROM syncrun  ORDER BY started DESC LIMIT 1",
+          "refId": "A",
+          "sql": {
+            "columns": [
+              {
+                "parameters": [],
+                "type": "function"
+              }
+            ],
+            "groupBy": [
+              {
+                "property": {
+                  "type": "string"
+                },
+                "type": "groupBy"
+              }
+            ],
+            "limit": 50
+          }
+        }
+      ],
+      "title": "Time since last DB sync",
+      "type": "gauge"
+    },
+    {
+      "datasource": {
+        "type": "postgres",
+        "uid": "JeO7-SKIk"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "bars",
+            "fillOpacity": 0,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "normal"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": [
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "duration"
+            },
+            "properties": [
+              {
+                "id": "custom.axisPlacement",
+                "value": "right"
+              },
+              {
+                "id": "custom.drawStyle",
+                "value": "line"
+              },
+              {
+                "id": "unit",
+                "value": "d"
+              }
+            ]
+          }
+        ]
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 0
+      },
+      "id": 8,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.3.2",
+      "targets": [
+        {
+          "datasource": {
+            "type": "postgres",
+            "uid": "JeO7-SKIk"
+          },
+          "editorMode": "code",
+          "format": "time_series",
+          "hide": false,
+          "rawQuery": true,
+          "rawSql": "SELECT $__timeGroup(requested_at, 1d, 0), \nsum(case when state = 0 then 1 else 0 end) as pending,\nsum(case when state = 1 then 1 else 0 end) as staged,\nsum(case when state = 2 then 1 else 0 end) as queued,\nsum(case when state = 3 then 1 else 0 end) as progress,\nsum(case when state = 4 then 1 else 0 end) as done\n FROM transferrequest WHERE $__timeFilter(requested_at) AND destination_id = 3 GROUP BY time ORDER BY time\n",
+          "refId": "A",
+          "sql": {
+            "columns": [
+              {
+                "alias": "\"time\"",
+                "parameters": [
+                  {
+                    "name": "requested_at",
+                    "type": "functionParameter"
+                  }
+                ],
+                "type": "function"
+              },
+              {
+                "name": "COUNT",
+                "parameters": [
+                  {
+                    "name": "state",
+                    "type": "functionParameter"
+                  }
+                ],
+                "type": "function"
+              }
+            ],
+            "groupBy": [
+              {
+                "property": {
+                  "name": "requested_at",
+                  "type": "string"
+                },
+                "type": "groupBy"
+              }
+            ],
+            "limit": 50,
+            "whereJsonTree": {
+              "children1": [
+                {
+                  "id": "9ab89b89-0123-4456-b89a-b18cca7924c7",
+                  "properties": {
+                    "value": [],
+                    "valueSrc": []
+                  },
+                  "type": "rule"
+                }
+              ],
+              "id": "9b8baa8a-89ab-4cde-b012-318cc9c79e96",
+              "type": "group"
+            }
+          },
+          "table": "transferrequest"
+        },
+        {
+          "datasource": {
+            "type": "postgres",
+            "uid": "JeO7-SKIk"
+          },
+          "editorMode": "code",
+          "format": "table",
+          "hide": false,
+          "rawQuery": true,
+          "rawSql": "SELECT $__timeGroup(requested_at, 1d, 0), \nAVG(EXTRACT (EPOCH FROM(modified_at - requested_at))) / (3600 * 24) AS DURATION\n FROM transferrequest WHERE $__timeFilter(requested_at) AND destination_id = 3 GROUP BY time ORDER BY time\n",
+          "refId": "B",
+          "sql": {
+            "columns": [
+              {
+                "parameters": [],
+                "type": "function"
+              }
+            ],
+            "groupBy": [
+              {
+                "property": {
+                  "type": "string"
+                },
+                "type": "groupBy"
+              }
+            ],
+            "limit": 50
+          }
+        }
+      ],
+      "title": "Transfer to BONN",
+      "transformations": [],
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "postgres",
+        "uid": "_PS92SFSk"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 0,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "log": 2,
+              "type": "symlog"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 8
+      },
+      "id": 4,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "postgres",
+            "uid": "_PS92SFSk"
+          },
+          "editorMode": "code",
+          "format": "table",
+          "rawQuery": true,
+          "rawSql": "SELECT $__timeGroupAlias(started, '5m'), sum(inserts) as inserts FROM syncrun WHERE $__timeFilter(started) GROUP BY time ORDER BY time",
+          "refId": "A",
+          "sql": {
+            "columns": [
+              {
+                "parameters": [],
+                "type": "function"
+              }
+            ],
+            "groupBy": [
+              {
+                "property": {
+                  "type": "string"
+                },
+                "type": "groupBy"
+              }
+            ],
+            "limit": 50
+          }
+        },
+        {
+          "datasource": {
+            "type": "postgres",
+            "uid": "_PS92SFSk"
+          },
+          "editorMode": "code",
+          "format": "table",
+          "hide": false,
+          "rawQuery": true,
+          "rawSql": "SELECT $__timeGroupAlias(started, '5m'), sum(deletes) as deletes FROM syncrun WHERE $__timeFilter(started) GROUP BY time ORDER BY time",
+          "refId": "B",
+          "sql": {
+            "columns": [
+              {
+                "parameters": [],
+                "type": "function"
+              }
+            ],
+            "groupBy": [
+              {
+                "property": {
+                  "type": "string"
+                },
+                "type": "groupBy"
+              }
+            ],
+            "limit": 50
+          }
+        },
+        {
+          "datasource": {
+            "type": "postgres",
+            "uid": "_PS92SFSk"
+          },
+          "editorMode": "code",
+          "format": "table",
+          "hide": false,
+          "rawQuery": true,
+          "rawSql": "SELECT $__timeGroupAlias(started, '5m'), sum(conflicts) as conflicts FROM syncrun WHERE $__timeFilter(started) GROUP BY time ORDER BY time",
+          "refId": "C",
+          "sql": {
+            "columns": [
+              {
+                "parameters": [],
+                "type": "function"
+              }
+            ],
+            "groupBy": [
+              {
+                "property": {
+                  "type": "string"
+                },
+                "type": "groupBy"
+              }
+            ],
+            "limit": 50
+          }
+        }
+      ],
+      "title": "Sync Status",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "postgres",
+        "uid": "JeO7-SKIk"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "bars",
+            "fillOpacity": 0,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "normal"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": [
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "duration"
+            },
+            "properties": [
+              {
+                "id": "custom.axisPlacement",
+                "value": "right"
+              },
+              {
+                "id": "custom.drawStyle",
+                "value": "line"
+              },
+              {
+                "id": "unit",
+                "value": "d"
+              }
+            ]
+          }
+        ]
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 8
+      },
+      "id": 10,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.3.2",
+      "targets": [
+        {
+          "datasource": {
+            "type": "postgres",
+            "uid": "JeO7-SKIk"
+          },
+          "editorMode": "code",
+          "format": "time_series",
+          "hide": false,
+          "rawQuery": true,
+          "rawSql": "SELECT $__timeGroup(requested_at, 1d, 0), \nsum(case when state = 0 then 1 else 0 end) as pending,\nsum(case when state = 1 then 1 else 0 end) as staged,\nsum(case when state = 2 then 1 else 0 end) as queued,\nsum(case when state = 3 then 1 else 0 end) as progress,\nsum(case when state = 4 then 1 else 0 end) as done\n FROM transferrequest WHERE $__timeFilter(requested_at) AND destination_id = 2 GROUP BY time ORDER BY time\n",
+          "refId": "A",
+          "sql": {
+            "columns": [
+              {
+                "alias": "\"time\"",
+                "parameters": [
+                  {
+                    "name": "requested_at",
+                    "type": "functionParameter"
+                  }
+                ],
+                "type": "function"
+              },
+              {
+                "name": "COUNT",
+                "parameters": [
+                  {
+                    "name": "state",
+                    "type": "functionParameter"
+                  }
+                ],
+                "type": "function"
+              }
+            ],
+            "groupBy": [
+              {
+                "property": {
+                  "name": "requested_at",
+                  "type": "string"
+                },
+                "type": "groupBy"
+              }
+            ],
+            "limit": 50,
+            "whereJsonTree": {
+              "children1": [
+                {
+                  "id": "9ab89b89-0123-4456-b89a-b18cca7924c7",
+                  "properties": {
+                    "value": [],
+                    "valueSrc": []
+                  },
+                  "type": "rule"
+                }
+              ],
+              "id": "9b8baa8a-89ab-4cde-b012-318cc9c79e96",
+              "type": "group"
+            }
+          },
+          "table": "transferrequest"
+        },
+        {
+          "datasource": {
+            "type": "postgres",
+            "uid": "JeO7-SKIk"
+          },
+          "editorMode": "code",
+          "format": "table",
+          "hide": false,
+          "rawQuery": true,
+          "rawSql": "SELECT $__timeGroup(requested_at, 1d, 0), \nAVG(EXTRACT (EPOCH FROM(modified_at - requested_at))) / (3600 * 24) AS DURATION\n FROM transferrequest WHERE $__timeFilter(requested_at) AND destination_id = 2 GROUP BY time ORDER BY time\n",
+          "refId": "B",
+          "sql": {
+            "columns": [
+              {
+                "parameters": [],
+                "type": "function"
+              }
+            ],
+            "groupBy": [
+              {
+                "property": {
+                  "type": "string"
+                },
+                "type": "groupBy"
+              }
+            ],
+            "limit": 50
+          }
+        }
+      ],
+      "title": "Transfer to GLOW",
+      "transformations": [],
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "postgres",
+        "uid": "JeO7-SKIk"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "bars",
+            "fillOpacity": 35,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "normal"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 16
+      },
+      "id": 6,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "postgres",
+            "uid": "JeO7-SKIk"
+          },
+          "editorMode": "code",
+          "format": "table",
+          "rawQuery": true,
+          "rawSql": "SELECT $__timeGroup(\"modified_at\", 1d, 0), COUNT(modified_at) as archive FROM transferrequest WHERE $__timeFilter(\"modified_at\") AND state=4 AND DESTINATION_ID=3 GROUP BY time ORDER BY time",
+          "refId": "A",
+          "sql": {
+            "columns": [
+              {
+                "parameters": [
+                  {
+                    "name": "requested_at",
+                    "type": "functionParameter"
+                  }
+                ],
+                "type": "function"
+              }
+            ],
+            "groupBy": [
+              {
+                "property": {
+                  "type": "string"
+                },
+                "type": "groupBy"
+              }
+            ],
+            "limit": 50
+          },
+          "table": "transferrequest"
+        },
+        {
+          "datasource": {
+            "type": "postgres",
+            "uid": "JeO7-SKIk"
+          },
+          "editorMode": "code",
+          "format": "table",
+          "hide": false,
+          "rawQuery": true,
+          "rawSql": "SELECT $__timeGroup(\"modified_at\", 1d, 0), COUNT(modified_at) as glow FROM transferrequest WHERE $__timeFilter(\"modified_at\") AND state=4 AND DESTINATION_ID=2 GROUP BY time ORDER BY time",
+          "refId": "B",
+          "sql": {
+            "columns": [
+              {
+                "parameters": [],
+                "type": "function"
+              }
+            ],
+            "groupBy": [
+              {
+                "property": {
+                  "type": "string"
+                },
+                "type": "groupBy"
+              }
+            ],
+            "limit": 50
+          }
+        }
+      ],
+      "title": "Finished Transfers",
+      "type": "timeseries"
+    }
+  ],
+  "refresh": false,
+  "schemaVersion": 37,
+  "style": "dark",
+  "tags": [],
+  "templating": {
+    "list": []
+  },
+  "time": {
+    "from": "2023-05-10T15:44:21.594Z",
+    "to": "2023-12-23T22:11:09.362Z"
+  },
+  "timepicker": {},
+  "timezone": "",
+  "title": "Transferbot",
+  "uid": "yHP6hIKSk",
+  "version": 12,
+  "weekStart": ""
+}