diff --git a/.editorconfig b/.editorconfig
index 2823e7bda5aa7e45fdb1edcd301392a9fad9438f..5468693772cdfaa67cb119c74b232d85b9383d53 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -147,5 +147,5 @@ ij_yaml_space_before_colon = false
 ij_yaml_spaces_within_braces = true
 ij_yaml_spaces_within_brackets = true
 
-[{*.hcl,*.hcl.tmpl}]
+[{*.hcl,*.hcl.tmpl,*.nomad}]
 indent_size = 2
diff --git a/.gitignore b/.gitignore
index 95968218c813af7e5461d064a864d4b93d8cefd6..4ccabd3716b8e8e40abe61bf82c11916c4dbfbc7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@
 **/env
 **/pending_log_messages.db
 **/vscode-server.tar
+.orig
 
 tangostationcontrol/build
 tangostationcontrol/cover
@@ -34,4 +35,10 @@ deploy/hosts
 docker-compose/alerta-web/alerta-secrets.json
 docker-compose/tmp
 
+infra/dev/jobs/*.nomad
+infra/dev/nomad/tmp/*
+!infra/dev/nomad/tmp/.keep
+
 **/CDB/dump*.json
+
+bin/jumppad
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 627270c72249efb4bb862946e6752f14ee2f6b8f..a68710f0a4419499f04606de025347fc2e03604d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -118,14 +118,13 @@ docker_build_image:
     matrix:
       - IMAGE:
           - ec-sim
-          - http-json-schemas
           - prometheus
           - tango-prometheus-exporter
           - itango
           - grafana
           - loki
-          - logstash
           - jupyter-lab
+          - dsconfig
   rules:
     - if: $CI_PIPELINE_SOURCE == "merge_request_event"
       changes:
@@ -224,7 +223,7 @@ CDB_correctness:
 
 sast:
   stage: static-analysis
-  needs: []
+  needs: [ ]
   variables:
     SAST_EXCLUDED_PATHS: "*.tox"
     SAST_EXCLUDED_ANALYZERS: brakeman, flawfinder, kubesec, nodejs-scan, phpcs-security-audit,
@@ -232,13 +231,13 @@ sast:
 
 dependency_scanning:
   stage: static-analysis
-  needs: []
+  needs: [ ]
   before_script:
     - rm -r `ls | grep -v "^tangostationcontrol$"`
 
 secret_detection:
   stage: static-analysis
-  needs: []
+  needs: [ ]
 
 sphinx_documentation:
   stage: documentation
@@ -270,16 +269,18 @@ unit_test:
 
 .test_docker:
   stage: integration-tests
-  image: docker:23.0.5 # latest ships with docker compose v2.19, which has the following bug: https://github.com/docker/compose/issues/10668
-#  needs:
-#    - unit_test
-#    - docker_build_image_device_base
+  image: docker:latest
+  needs:
+    - unit_test
+    - docker_build_image_device_base
   tags:
-    - privileged
+    - integration_tests
   services:
     - name: docker:dind
   variables:
-    DOCKER_TLS_CERTDIR: "/certs"
+    DOCKER_DRIVER: overlay2
+    DOCKER_TLS_CERTDIR: ""
+    JUMPPAD_HOME: $CI_PROJECT_DIR
   before_script:
     - |
       if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" && -z "$CI_COMMIT_TAG" ]]; then
@@ -290,33 +291,23 @@ unit_test:
         echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag"
       fi
     - apk update
+    - apk add git
     - apk add --update make bash
     - apk add --update bind-tools
-    - apk add --update postgresql14-client gzip
+    - apk add --update postgresql14-client gzip socat
     - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
     - touch /root/.Xauthority
     #    Hack BASH_SOURCE into sourced files, docker its sh shell won't set this
     - export BASH_SOURCE=$(pwd)/setup.sh
-    #    Hack HOSTNAME env variable into host.docker.internal, set in docker-compose
-    - export HOSTNAME=host.docker.internal
     #    source the lofarrc file and mask its non zero exit code
     - . setup.sh || true
     #    TANGO_HOST must be unset our databaseds will be unreachable
     - unset TANGO_HOST
-    #    Do not remove 'bash' or statement will be ignored by primitive docker shell
-    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh pull $tag
     - export TAG="$tag"
-  after_script:
-    #    Collect output of all containers
-    - |
-      mkdir -p log
-      for container in $(docker ps -a --format "{{.Names}}")
-      do
-        echo "Saving log for container $container"
-        docker logs "${container}" >& "log/${container}.log"
-      done
-    #    Collect content of the TangoDB
-    - $CI_PROJECT_DIR/bin/dump_ConfigDb.sh >& log/dump_ConfigDb.log
+    # Forward nomad API port to docker host
+    - socat tcp-listen:4646,reuseaddr,fork tcp:docker:4646 &
+    # Forward consul dns port to docker host
+    - socat udp-listen:8600,reuseaddr,fork udp:docker:8600 &
   artifacts:
     when: always
     paths:
@@ -324,22 +315,24 @@ unit_test:
 
 integration_test_docker:
   extends: .test_docker
+  allow_failure: true # until there is a machine that can properly run them
   script:
     #    Do not remove 'bash' or statement will be ignored by primitive docker shell
-    - bash -e $CI_PROJECT_DIR/sbin/run_integration_test.sh --no-build
+    - bash $CI_PROJECT_DIR/sbin/tag_and_push_docker_image.sh pull $tag
+    #    Do not remove 'bash' or statement will be ignored by primitive docker shell
+    - bash -e $CI_PROJECT_DIR/sbin/run_integration_test.sh --no-build --save-logs
 
 service_test_docker:
   extends: .test_docker
   script:
-    # Fix logstash healthcheck
     - export HOSTNAME=localhost
     #    Do not remove 'bash' or statement will be ignored by primitive docker shell
     - bash -e $CI_PROJECT_DIR/sbin/run_service_test.sh
 
 multi_project_integration_test:
   stage: integration-tests
-#  needs:
-#    - docker_build_image_device_base
+  needs:
+    - docker_build_image_device_base
   variables:
     TANGO_DEFAULT_BRANCH: $CI_DEFAULT_BRANCH
     TANGO_CURRENT_BRANCH: $CI_COMMIT_BRANCH
@@ -382,41 +375,37 @@ release_job:
     tag_name: '$CI_COMMIT_TAG'
     description: '$CI_COMMIT_TAG'
 
-.base_deploy:
+deploy_nomad:
   stage: deploy
-  image: ubuntu:bionic
+  image:
+    name: hashicorp/levant
+    entrypoint: [ "" ]
   when: manual
   rules:
     - if: ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH) || $CI_COMMIT_TAG
-  before_script:
-    - apt-get update
-    - apt-get install ansible openssh-client -y
-    # Use Gitlab protected variable to provide key
-    - echo "$DEPLOY_KEY" > id_rsa
-    - chmod 400 id_rsa
-    - ssh-keygen -y -f id_rsa > id_rsa.pub
-    # Add ssh key to agent
-    - eval $(ssh-agent)
-    - ssh-add id_rsa
-    - ansible --version
-  script:
-    - echo "Deploying version $CI_COMMIT_TAG"
-    - cd deploy
-    # Prevent error of ansible being run in world writeable directory
-    - chmod o-w .
-    # Generate hosts file for deployment
-    - echo "[all]" > hosts
-    - echo "stat ansible_host=$DEPLOY_HOST ansible_user=$DEPLOY_USER" >> hosts
-    # Run deployment with populated variables
-    - ansible-playbook -v deploy.yml --extra-vars station_version=$CI_COMMIT_TAG --extra-vars station_config=$DEPLOY_CONFIG
-.deploy_l2ts_base:
-  extends: .base_deploy
-  variables:
-    DEPLOY_USER: $L2TS_USERNAME
-    DEPLOY_HOST: $L2TS_HOSTNAME
-    DEPLOY_CONFIG: $L2TS_CONFIG
-    DEPLOY_KEY: $L2TS_DEPLOY_KEY
-deploy_l2ts_start:
-  extends: .deploy_l2ts_base
+  parallel:
+    matrix:
+      - STATION:
+          - cs001
+        COMPONENT:
+          - monitoring
+          - logging
+          - tango
+          - object-storage
+          - sdptr
+          - device-server
+          - dsconfig
+          - ec-sim
+          - jupyter
+          - nomad-client
+          - tango-prometheus-exporter
   environment:
-    name: l2ts
+    name: $STATION
+  script:
+    - |
+      levant deploy \
+        -address="http://${STATION}c.control.lofar:4646" \
+        -var-file=infra/env.yaml \
+        -var image_tag="latest" \
+        -var station="${STATION}" \
+        infra/jobs/station/${COMPONENT}.levant.nomad
diff --git a/CDB/README.md b/CDB/README.md
index 794fd25f3ef5080d67eca27676839fdb125ca188..3ec4da94dea9bf4d5751c83ee600bc9f5a62aa93 100644
--- a/CDB/README.md
+++ b/CDB/README.md
@@ -4,7 +4,7 @@ The ConfigDb.json files in this directory are used to populate the Tango configu
 To load a configuration file, use
 
 ```bash
-../sbin/update_ConfigDb.sh file.json
+../sbin/dsconfig.sh --update file.json
 ```
 
 The tool ``tangostationcontrol.toolkit.analyse_dsconfig_hierarchies`` is provided to check the consistency of
@@ -12,13 +12,13 @@ hierarchies defined between the devices in a set of configuration files.
 
 The following files are provided:
 
-| File                                       | Description                                                 | Usage               |
-|--------------------------------------------|-------------------------------------------------------------|---------------------|
-| `LOFAR_ConfigDb.json`                      | Generic base configuration, registering all of the devices. | Always              |
-| `test_environment_ConfigDb.json`           | Base delta for the unit- and integration test suites.       | Tests & development |
-| `stations/simulators_ConfigDb.json`        | A "station" configuration that points to our simulators.    | Tests & development |
-| `stations/dummy_positions_ConfigDb.json`   | An antenna configuration, just to have one (it's CS001).    | Tests & development |
-| `stations/LTS_ConfigDb.json`               | The configuration for the Lab Test Station.                 | Load on LTS         |
-| `stations/DTS_ConfigDb.json`               | The configuration for the Dwingeloo Test Station.           | Load on DTS-lab     |
-| `stations/DTS_Outside_ConfigDb.json`       | The configuration for the Dwingeloo Test Station Outside.   | Load on DTS-outside |
-| `integrations/`                            | Configurations required by the integration test suites.     | Integration tests   |
+| File                                     | Description                                                 | Usage               |
+|------------------------------------------|-------------------------------------------------------------|---------------------|
+| `LOFAR_ConfigDb.json`                    | Generic base configuration, registering all of the devices. | Always              |
+| `test_environment_ConfigDb.json`         | Base delta for the unit- and integration test suites.       | Tests & development |
+| `stations/simulators_ConfigDb.json`      | A "station" configuration that points to our simulators.    | Tests & development |
+| `stations/dummy_positions_ConfigDb.json` | An antenna configuration, just to have one (it's CS001).    | Tests & development |
+| `stations/LTS_ConfigDb.json`             | The configuration for the Lab Test Station.                 | Load on LTS         |
+| `stations/DTS_ConfigDb.json`             | The configuration for the Dwingeloo Test Station.           | Load on DTS-lab     |
+| `stations/DTS_Outside_ConfigDb.json`     | The configuration for the Dwingeloo Test Station Outside.   | Load on DTS-outside |
+| `integrations/`                          | Configurations required by the integration test suites.     | Integration tests   |
diff --git a/CDB/stations/cs001.json b/CDB/stations/cs001.json
index 3d73427ee8ef4ed2733821b85617a1b75c7e2f99..649c41aa8e325d250635c09b65109179bd66b675 100644
--- a/CDB/stations/cs001.json
+++ b/CDB/stations/cs001.json
@@ -34,7 +34,7 @@
               "Station_Name": [
                 "CS001"
               ],
-              "Station_Number":[
+              "Station_Number": [
                 "1"
               ]
             }
@@ -76,7 +76,7 @@
           "STAT/EC/1": {
             "properties": {
               "OPC_Server_Name": [
-                  "ec-sim"
+                "ec-sim.service.consul"
               ]
             }
           }
@@ -437,7 +437,7 @@
                 "SPARSE_ODD",
                 "ALL"
               ],
-              "Antenna_Set_Masks":[
+              "Antenna_Set_Masks": [
                 "111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000",
                 "000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111",
                 "101010101010101010101010101010101010101010101000101010101010101010101010101010101010101010101010",
@@ -543,8 +543,7 @@
                 "2", "95"
               ],
               "Power_to_RECV_mapping": [
-                "1", "0",
-                "1", "2",
+                "1", "0","1", "2",
                 "1", "4",
                 "1", "6",
                 "1", "8",
@@ -641,7 +640,7 @@
                 "2", "94"
               ],
               "Antenna_to_SDP_Mapping": [
-                "0", "0",
+                                "0", "0",
                 "0", "1",
                 "0", "2",
                 "0", "3",
@@ -739,8 +738,8 @@
                 "15", "5"
               ],
               "Antenna_Names": [
-                 "L0",  "L1",  "L2",  "L3",  "L4",  "L5",
-                 "L6",  "L7",  "L8",  "L9", "L10", "L11",
+                "L0",  "L1",  "L2",  "L3",  "L4",  "L5",
+                "L6",  "L7",  "L8",  "L9", "L10", "L11",
                 "L12", "L13", "L14", "L15", "L16", "L17",
                 "L18", "L19", "L20", "L21", "L22", "L23",
                 "L24", "L25", "L26", "L27", "L28", "L29",
@@ -757,7 +756,7 @@
                 "L90", "L91", "L92", "L93", "L94", "L95"
               ],
               "Antenna_Cables": [
-                 "80m",  "80m",  "80m",  "80m",  "80m",  "80m",
+                "80m",  "80m",  "80m",  "80m",  "80m",  "80m",
                  "80m",  "80m",  "80m",  "80m",  "80m",  "80m",
                  "80m", "115m",  "80m",  "80m",  "80m",  "80m",
                  "80m",  "80m",  "80m",  "80m",  "80m",  "80m",
@@ -778,7 +777,7 @@
                 "3826923.503", "460915.488", "5064643.517"
               ],
               "Antenna_Reference_ETRS": [
-                "3826923.942", "460915.117", "5064643.229",
+                                "3826923.942", "460915.117", "5064643.229",
                 "3826921.923", "460914.874", "5064644.767",
                 "3826922.604", "460917.222", "5064644.043",
                 "3826924.742", "460917.480", "5064642.415",
@@ -888,7 +887,7 @@
               "Antenna_Sets": [
                 "ALL"
               ],
-              "Antenna_Set_Masks":[
+              "Antenna_Set_Masks": [
                 "111111111111111111111111"
               ],
               "Control_to_RECV_mapping": [
@@ -918,7 +917,7 @@
                 "1", "47"
               ],
               "Power_to_RECV_mapping": [
-                "1", "0",
+                                "1", "0",
                 "1", "2",
                 "1", "4",
                 "1", "6",
@@ -974,6 +973,7 @@
                  "H6",  "H7",  "H8",  "H9", "H10", "H11",
                 "H12", "H13", "H14", "H15", "H16", "H17",
                 "H18", "H19", "H20", "H21", "H22", "H23"
+
               ],
               "Antenna_Cables": [
                 "115m", "115m", "115m", "115m", "115m", "115m",
@@ -982,7 +982,7 @@
                 "115m", "115m", "115m", "115m", "115m", "115m"
               ],
               "Antenna_Field_Reference_ITRF": [
-                "3826896.192", "460979.502", "5064658.231"
+                                "3826896.192", "460979.502", "5064658.231"
               ],
               "Antenna_Reference_ETRS": [
                 "3826886.142", "460980.772", "5064665.668",
@@ -1028,7 +1028,7 @@
               "Antenna_Sets": [
                 "ALL"
               ],
-              "Antenna_Set_Masks":[
+              "Antenna_Set_Masks": [
                 "111111111111111111111111"
               ],
               "Control_to_RECV_mapping": [
@@ -1084,7 +1084,7 @@
                 "1", "94"
               ],
               "Antenna_to_SDP_Mapping": [
-                "0", "0",
+                                "0", "0",
                 "0", "1",
                 "0", "2",
                 "0", "3",
@@ -1166,10 +1166,10 @@
         }
       }
     },
-    "SDPFirmware":{
+    "SDPFirmware": {
       "STAT": {
         "SDPFirmware": {
-          "STAT/SDPFirmware/LBA":{
+          "STAT/SDPFirmware/LBA": {
             "properties": {
               "Control_Children": [
                 "STAT/SDP/LBA",
@@ -1197,7 +1197,7 @@
               ]
             }
           },
-          "STAT/SDPFirmware/HBA0":{
+          "STAT/SDPFirmware/HBA0": {
             "properties": {
               "Control_Children": [
                 "STAT/SDP/HBA0",
@@ -1225,7 +1225,7 @@
               ]
             }
           },
-          "STAT/SDPFirmware/HBA1":{
+          "STAT/SDPFirmware/HBA1": {
             "properties": {
               "Control_Children": [
                 "STAT/SDP/HBA1",
@@ -1327,222 +1327,6 @@
           }
         }
       }
-    },
-    "BST": {
-      "STAT": {
-        "BST": {
-          "STAT/BST/LBA": {
-            "properties": {
-              "FPGA_bst_offload_hdr_eth_destination_mac_RW_default": [
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7"
-              ]
-            }
-          },
-          "STAT/BST/HBA0": {
-            "properties": {
-              "FPGA_bst_offload_hdr_eth_destination_mac_RW_default": [
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7"
-              ]
-            }
-          },
-          "STAT/BST/HBA1": {
-            "properties": {
-              "FPGA_bst_offload_hdr_eth_destination_mac_RW_default": [
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7"
-              ]
-            }
-          }
-        }
-      }
-    },
-    "SST": {
-      "STAT": {
-        "SST": {
-          "STAT/SST/LBA": {
-            "properties": {
-              "FPGA_sst_offload_hdr_eth_destination_mac_RW_default": [
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7"
-              ]
-            }
-          },
-          "STAT/SST/HBA0": {
-            "properties": {
-              "FPGA_sst_offload_hdr_eth_destination_mac_RW_default": [
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7"
-              ]
-            }
-          },
-          "STAT/SST/HBA1": {
-            "properties": {
-              "FPGA_sst_offload_hdr_eth_destination_mac_RW_default": [
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7"
-              ]
-            }
-          }
-        }
-      }
-    },
-    "XST": {
-      "STAT": {
-        "XST": {
-          "STAT/XST/LBA": {
-            "properties": {
-              "FPGA_xst_offload_hdr_eth_destination_mac_RW_default": [
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7"
-              ]
-            }
-          },
-          "STAT/XST/HBA0": {
-            "properties": {
-              "FPGA_xst_offload_hdr_eth_destination_mac_RW_default": [
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7"
-              ]
-            }
-          },
-          "STAT/XST/HBA1": {
-            "properties": {
-              "FPGA_xst_offload_hdr_eth_destination_mac_RW_default": [
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7",
-                "3c:ec:ef:86:2f:b7"
-              ]
-            }
-          }
-        }
-      }
     }
   }
 }
diff --git a/CDB/stations/hba_core.json b/CDB/stations/hba_core.json
index f03fbc65794fef66842f2fb07e0a9521fff6e8cb..0dd47880d93435fde4e4e4865bf81deaea81b919 100644
--- a/CDB/stations/hba_core.json
+++ b/CDB/stations/hba_core.json
@@ -43,7 +43,7 @@
                 "STAT/SDP/HBA0"
               ],
               "OPC_Server_Name": [
-                "sdptr-hba0"
+                "sdptr-hba0.service.consul"
               ],
               "OPC_Server_Port": [
                 "4842"
@@ -95,7 +95,7 @@
                 "STAT/SDP/HBA1"
               ],
               "OPC_Server_Name": [
-                "sdptr-hba1"
+                "sdptr-hba1.service.consul"
               ],
               "OPC_Server_Port": [
                 "4844"
@@ -162,7 +162,7 @@
                 "STAT/RECVH/H0"
               ],
               "HBAT_single_element_selection_GENERIC_201512": [
-                 "0", "10",  "4",  "3", "14",  "0",
+                "0", "10",  "4",  "3", "14",  "0",
                  "5",  "5",  "3", "13", "10",  "3",
                 "12",  "2",  "7", "15",  "6", "14",
                  "7",  "5",  "7",  "9",  "0", "15"
@@ -265,7 +265,7 @@
                 "STAT/XST/HBA0"
               ],
               "OPC_Server_Name": [
-                "sdptr-hba0"
+                "sdptr-hba0.service.consul"
               ],
               "OPC_Server_Port": [
                 "4842"
@@ -291,7 +291,7 @@
                 "STAT/XST/HBA1"
               ],
               "OPC_Server_Name": [
-                "sdptr-hba1"
+                "sdptr-hba1.service.consul"
               ],
               "OPC_Server_Port": [
                 "4844"
@@ -312,7 +312,7 @@
               "Control_Children": [
               ],
               "OPC_Server_Name": [
-                "sdptr-hba0"
+                "sdptr-hba0.service.consul"
               ],
               "OPC_Server_Port": [
                 "4842"
@@ -435,7 +435,7 @@
               "Control_Children": [
               ],
               "OPC_Server_Name": [
-                "sdptr-hba1"
+                "sdptr-hba1.service.consul"
               ],
               "OPC_Server_Port": [
                 "4844"
@@ -568,7 +568,7 @@
                 "5113"
               ],
               "OPC_Server_Name": [
-                "sdptr-hba0"
+                "sdptr-hba0.service.consul"
               ],
               "OPC_Server_Port": [
                 "4842"
@@ -576,23 +576,41 @@
               "OPC_Time_Out": [
                 "5.0"
               ],
+              "FPGA_bst_offload_hdr_eth_destination_mac_RW_default": [
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56"
+              ],
               "FPGA_bst_offload_hdr_ip_destination_address_RW_default": [
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250"
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1"
               ],
               "FPGA_bst_offload_hdr_udp_destination_port_RW_default": [
                 "5013",
@@ -641,7 +659,7 @@
                 "5123"
               ],
               "OPC_Server_Name": [
-                "sdptr-hba1"
+                "sdptr-hba1.service.consul"
               ],
               "OPC_Server_Port": [
                 "4844"
@@ -649,23 +667,41 @@
               "OPC_Time_Out": [
                 "5.0"
               ],
+              "FPGA_bst_offload_hdr_eth_destination_mac_RW_default": [
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56"
+              ],
               "FPGA_bst_offload_hdr_ip_destination_address_RW_default": [
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250"
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1"
               ],
               "FPGA_bst_offload_hdr_udp_destination_port_RW_default": [
                 "5023",
@@ -720,7 +756,7 @@
                 "5111"
               ],
               "OPC_Server_Name": [
-                "sdptr-hba0"
+                "sdptr-hba0.service.consul"
               ],
               "OPC_Server_Port": [
                 "4842"
@@ -728,23 +764,41 @@
               "OPC_Time_Out": [
                 "5.0"
               ],
+              "FPGA_sst_offload_hdr_eth_destination_mac_RW_default": [
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56"
+              ],
               "FPGA_sst_offload_hdr_ip_destination_address_RW_default": [
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250"
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1"
               ],
               "FPGA_sst_offload_hdr_udp_destination_port_RW_default": [
                 "5011",
@@ -775,7 +829,7 @@
                 "5121"
               ],
               "OPC_Server_Name": [
-                "sdptr-hba1"
+                "sdptr-hba1.service.consul"
               ],
               "OPC_Server_Port": [
                 "4844"
@@ -783,23 +837,41 @@
               "OPC_Time_Out": [
                 "5.0"
               ],
+              "FPGA_sst_offload_hdr_eth_destination_mac_RW_default": [
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56"
+              ],
               "FPGA_sst_offload_hdr_ip_destination_address_RW_default": [
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250"
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1"
               ],
               "FPGA_sst_offload_hdr_udp_destination_port_RW_default": [
                 "5021",
@@ -836,7 +908,7 @@
                 "5112"
               ],
               "OPC_Server_Name": [
-                "sdptr-hba0"
+                "sdptr-hba0.service.consul"
               ],
               "OPC_Server_Port": [
                 "4842"
@@ -844,23 +916,41 @@
               "OPC_Time_Out": [
                 "5.0"
               ],
+              "FPGA_xst_offload_hdr_eth_destination_mac_RW_default": [
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56"
+              ],
               "FPGA_xst_offload_hdr_ip_destination_address_RW_default": [
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250"
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1"
               ],
               "FPGA_xst_offload_hdr_udp_destination_port_RW_default": [
                 "5012",
@@ -909,7 +999,7 @@
                 "5122"
               ],
               "OPC_Server_Name": [
-                "sdptr-hba1"
+                "sdptr-hba1.service.consul"
               ],
               "OPC_Server_Port": [
                 "4844"
@@ -917,23 +1007,41 @@
               "OPC_Time_Out": [
                 "5.0"
               ],
+              "FPGA_xst_offload_hdr_eth_destination_mac_RW_default": [
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56"
+              ],
               "FPGA_xst_offload_hdr_ip_destination_address_RW_default": [
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250"
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1"
               ],
               "FPGA_xst_offload_hdr_udp_destination_port_RW_default": [
                 "5022",
diff --git a/CDB/stations/hba_remote.json b/CDB/stations/hba_remote.json
index dd4e7bdcfbb0ac688f07e8cc36f91b3b9c4d5b17..2f93af0ac571309ca3d36b172a39cc2df6de523d 100644
--- a/CDB/stations/hba_remote.json
+++ b/CDB/stations/hba_remote.json
@@ -342,23 +342,41 @@
               "OPC_Time_Out": [
                 "5.0"
               ],
+              "FPGA_bst_offload_hdr_eth_destination_mac_RW_default": [
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56"
+              ],
               "FPGA_bst_offload_hdr_ip_destination_address_RW_default": [
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250"
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1"
               ],
               "FPGA_bst_offload_hdr_udp_destination_port_RW_default": [
                 "5013",
@@ -421,23 +439,41 @@
               "OPC_Time_Out": [
                 "5.0"
               ],
+              "FPGA_sst_offload_hdr_eth_destination_mac_RW_default": [
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56"
+              ],
               "FPGA_sst_offload_hdr_ip_destination_address_RW_default": [
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250"
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1"
               ],
               "FPGA_sst_offload_hdr_udp_destination_port_RW_default": [
                 "5011",
@@ -482,23 +518,41 @@
               "OPC_Time_Out": [
                 "5.0"
               ],
+              "FPGA_xst_offload_hdr_eth_destination_mac_RW_default": [
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56"
+              ],
               "FPGA_xst_offload_hdr_ip_destination_address_RW_default": [
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250"
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1"
               ],
               "FPGA_xst_offload_hdr_udp_destination_port_RW_default": [
                 "5012",
diff --git a/CDB/stations/lba.json b/CDB/stations/lba.json
index ec0d05e814ec13741fd7144b5700feec72efda7d..626c98b3344f6f0ae3a667c6f747ffcf69f14df2 100644
--- a/CDB/stations/lba.json
+++ b/CDB/stations/lba.json
@@ -22,7 +22,7 @@
                 "STAT/SDP/LBA"
               ],
               "OPC_Server_Name": [
-                "sdptr-lba"
+                "sdptr-lba.service.consul"
               ],
               "OPC_Server_Port": [
                 "4840"
@@ -209,7 +209,7 @@
                 "STAT/XST/LBA"
               ],
               "OPC_Server_Name": [
-                "sdptr-lba"
+                "sdptr-lba.service.consul"
               ],
               "OPC_Server_Port": [
                 "4840"
@@ -228,7 +228,7 @@
           "STAT/SDP/LBA": {
             "properties": {
               "OPC_Server_Name": [
-                "sdptr-lba"
+                "sdptr-lba.service.consul"
               ],
               "OPC_Server_Port": [
                 "4840"
@@ -361,7 +361,7 @@
                 "5103"
               ],
               "OPC_Server_Name": [
-                "sdptr-lba"
+                "sdptr-lba.service.consul"
               ],
               "OPC_Server_Port": [
                 "4840"
@@ -369,23 +369,41 @@
               "OPC_Time_Out": [
                 "5.0"
               ],
+              "FPGA_bst_offload_hdr_eth_destination_mac_RW_default": [
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56"
+              ],
               "FPGA_bst_offload_hdr_ip_destination_address_RW_default": [
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250"
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1"
               ],
               "FPGA_bst_offload_hdr_udp_destination_port_RW_default": [
                 "5003",
@@ -440,7 +458,7 @@
                 "5101"
               ],
               "OPC_Server_Name": [
-                "sdptr-lba"
+                "sdptr-lba.service.consul"
               ],
               "OPC_Server_Port": [
                 "4840"
@@ -448,23 +466,41 @@
               "OPC_Time_Out": [
                 "5.0"
               ],
+              "FPGA_sst_offload_hdr_eth_destination_mac_RW_default": [
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56"
+              ],
               "FPGA_sst_offload_hdr_ip_destination_address_RW_default": [
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250"
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1"
               ],
               "FPGA_sst_offload_hdr_udp_destination_port_RW_default": [
                 "5001",
@@ -501,7 +537,7 @@
                 "5102"
               ],
               "OPC_Server_Name": [
-                "sdptr-lba"
+                "sdptr-lba.service.consul"
               ],
               "OPC_Server_Port": [
                 "4840"
@@ -509,23 +545,41 @@
               "OPC_Time_Out": [
                 "5.0"
               ],
+              "FPGA_xst_offload_hdr_eth_destination_mac_RW_default": [
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56",
+                "52:54:00:12:34:56"
+              ],
               "FPGA_xst_offload_hdr_ip_destination_address_RW_default": [
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250",
-                "10.99.250.250"
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1",
+                "10.99.250.1"
               ],
               "FPGA_xst_offload_hdr_udp_destination_port_RW_default": [
                 "5002",
diff --git a/CDB/stations/testenv_cs001.json.orig b/CDB/stations/testenv_cs001.json.orig
new file mode 100644
index 0000000000000000000000000000000000000000..db183dbea108cf16d064e71e25b6616c4bfed753
--- /dev/null
+++ b/CDB/stations/testenv_cs001.json.orig
@@ -0,0 +1,1186 @@
+{
+  "servers": {
+    "APSCT": {
+      "STAT": {
+        "APSCT": {
+          "STAT/APSCT/L0": {
+            "properties": {
+              "OPC_Server_Name": [
+                "apsct-sim"
+              ],
+              "OPC_Server_Port": [
+                "4843"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "APSCT_On_Off_timeout": [
+                "1"
+              ]
+            }
+          },
+          "STAT/APSCT/L1": {
+            "properties": {
+              "OPC_Server_Name": [
+                "apsct-sim"
+              ],
+              "OPC_Server_Port": [
+                "4843"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "APSCT_On_Off_timeout": [
+                "1"
+              ]
+            }
+          },
+          "STAT/APSCT/H0": {
+            "properties": {
+              "OPC_Server_Name": [
+                "apsct-sim"
+              ],
+              "OPC_Server_Port": [
+                "4843"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "APSCT_On_Off_timeout": [
+                "1"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "CCD": {
+      "STAT": {
+        "CCD": {
+          "STAT/CCD/1": {
+            "properties": {
+              "OPC_Server_Name": [
+                "ccd-sim"
+              ],
+              "OPC_Server_Port": [
+                "4843"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "CCD_On_Off_timeout": [
+                "1"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "EC": {
+      "STAT": {
+        "EC": {
+          "STAT/EC/1": {
+            "properties": {
+              "OPC_Server_Name": [
+                "ec-sim.service.consul"
+              ],
+              "OPC_Server_Port": [
+                "4850"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "OPC_Node_Path_Prefix": [
+                "3:ServerInterfaces",
+                "4:Environmental_Control"
+              ],
+              "OPC_namespace": [
+                "http://Environmental_Control"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "APSPU": {
+      "STAT": {
+        "APSPU": {
+          "STAT/APSPU/L0": {
+            "properties": {
+              "OPC_Server_Name": [
+                "apspu-sim"
+              ],
+              "OPC_Server_Port": [
+                "4842"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ]
+            }
+          },
+          "STAT/APSPU/L1": {
+            "properties": {
+              "OPC_Server_Name": [
+                "apspu-sim"
+              ],
+              "OPC_Server_Port": [
+                "4842"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ]
+            }
+          },
+          "STAT/APSPU/H0": {
+            "properties": {
+              "OPC_Server_Name": [
+                "apspu-sim"
+              ],
+              "OPC_Server_Port": [
+                "4842"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "Beamlet": {
+      "STAT": {
+        "Beamlet": {
+          "STAT/Beamlet/LBA": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "FPGA_beamlet_output_hdr_eth_destination_mac_RW_default": [
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB"
+              ],
+              "FPGA_beamlet_output_hdr_ip_destination_address_RW_default": [
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1"
+              ]
+            }
+          },
+          "STAT/Beamlet/HBA0": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "FPGA_beamlet_output_hdr_eth_destination_mac_RW_default": [
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB"
+              ],
+              "FPGA_beamlet_output_hdr_ip_destination_address_RW_default": [
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1"
+              ]
+            }
+          },
+          "STAT/Beamlet/HBA1": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "FPGA_beamlet_output_hdr_eth_destination_mac_RW_default": [
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB"
+              ],
+              "FPGA_beamlet_output_hdr_ip_destination_address_RW_default": [
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "DigitalBeam": {
+      "STAT": {
+        "DigitalBeam": {
+<<<<<<< HEAD
+          "STAT/DigitalBeam/LBA": {
+            "properties": {
+              "Beam_tracking_interval": [
+                "1.0"
+              ],
+              "Beam_tracking_preparation_period": [
+                "0.5"
+              ]
+=======
+            "STAT": {
+                "DigitalBeam": {
+                    "STAT/DigitalBeam/LBA": {
+                        "properties": {
+                            "Beam_tracking_interval": [
+                                "1.0"
+                            ],
+                            "Beam_tracking_preparation_period": [
+                                "0.5"
+                            ]
+                        }
+                    },
+                    "STAT/DigitalBeam/HBA0": {
+                        "properties": {
+                            "Beam_tracking_interval": [
+                                "1.0"
+                            ],
+                            "Beam_tracking_preparation_period": [
+                                "0.5"
+                            ]
+                        }
+                    },
+                    "STAT/DigitalBeam/HBA1": {
+                        "properties": {
+                            "AntennaField_Device": [
+                                "STAT/AFH/HBA1"
+                            ],
+                            "Beamlet_Device": [
+                                "STAT/Beamlet/HBA1"
+                            ],
+                            "Beam_tracking_interval": [
+                                "1.0"
+                            ],
+                            "Beam_tracking_preparation_period": [
+                                "0.5"
+                            ]
+                        }
+                    }
+                }
+>>>>>>> master
+            }
+          },
+          "STAT/DigitalBeam/HBA0": {
+            "properties": {
+              "Beam_tracking_interval": [
+                "1.0"
+              ],
+              "Beam_tracking_preparation_period": [
+                "0.5"
+              ]
+            }
+          },
+          "STAT/DigitalBeam/HBA1": {
+            "properties": {
+              "AntennaField_Device": [
+                "STAT/AntennaField/HBA1"
+              ],
+              "Beamlet_Device": [
+                "STAT/Beamlet/HBA1"
+              ],
+              "Beam_tracking_interval": [
+                "1.0"
+              ],
+              "Beam_tracking_preparation_period": [
+                "0.5"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "TemperatureManager": {
+      "STAT": {
+        "TemperatureManager": {
+          "STAT/TemperatureManager/1": {
+            "properties": {
+              "Alarm_Error_List": [
+                "APSCT, APSCT_TEMP_error_R",
+                "APSPU, APSPU_TEMP_error_R",
+                "UNB2, UNB2_TEMP_error_R",
+                "RECVH, RECV_TEMP_error_R",
+                "RECVL, RECV_TEMP_error_R"
+              ],
+              "Shutdown_Device_List": [
+                "STAT/SDPFirmware/LBA",
+                "STAT/SDPFirmware/HBA0",
+                "STAT/SDPFirmware/HBA1",
+                "STAT/SDP/LBA",
+                "STAT/SDP/HBA0",
+                "STAT/SDP/HBA1",
+                "STAT/UNB2/L0",
+                "STAT/UNB2/L1",
+                "STAT/UNB2/H0",
+                "STAT/RECVH/H0",
+                "STAT/RECVL/L0",
+                "STAT/RECVL/L1",
+                "STAT/APSCT/L0",
+                "STAT/APSCT/L1",
+                "STAT/APSCT/H0",
+                "STAT/CCD/1",
+                "STAT/APSPU/L0",
+                "STAT/APSPU/L1",
+                "STAT/APSPU/H0"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "PCON": {
+      "STAT": {
+        "PCON": {
+          "STAT/PCON/1": {
+            "properties": {
+              "SNMP_use_simulators": [
+                "True"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "PSOC": {
+      "STAT": {
+        "PSOC": {
+          "STAT/PSOC/1": {
+            "properties": {
+              "SNMP_use_simulators": [
+                "True"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "RECVH": {
+      "STAT": {
+        "RECVH": {
+          "STAT/RECVH/H0": {
+            "properties": {
+              "OPC_Server_Name": [
+                "recvh-sim"
+              ],
+              "OPC_Server_Port": [
+                "4844"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "RCU_On_Off_timeout": [
+                "1"
+              ],
+              "RCU_DTH_On_Off_timeout": [
+                "1"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "RECVL": {
+      "STAT": {
+        "RECVL": {
+          "STAT/RECVL/L0": {
+            "properties": {
+              "OPC_Server_Name": [
+                "recvl-sim"
+              ],
+              "OPC_Server_Port": [
+                "4845"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "RCU_On_Off_timeout": [
+                "1"
+              ],
+              "RCU_DTH_On_Off_timeout": [
+                "1"
+              ]
+            }
+          },
+          "STAT/RECVL/L1": {
+            "properties": {
+              "OPC_Server_Name": [
+                "recvl-sim"
+              ],
+              "OPC_Server_Port": [
+                "4845"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "RCU_On_Off_timeout": [
+                "1"
+              ],
+              "RCU_DTH_On_Off_timeout": [
+                "1"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "SDPFirmware": {
+      "STAT": {
+        "SDPFirmware": {
+          "STAT/SDPFirmware/LBA": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "Firmware_Boot_timeout": [
+                "1.0"
+              ]
+            }
+          },
+          "STAT/SDPFirmware/HBA0": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "Firmware_Boot_timeout": [
+                "1.0"
+              ]
+            }
+          },
+          "STAT/SDPFirmware/HBA1": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "Firmware_Boot_timeout": [
+                "1.0"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "SDP": {
+      "STAT": {
+        "SDP": {
+          "STAT/SDP/LBA": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ]
+            }
+          },
+          "STAT/SDP/HBA0": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ]
+            }
+          },
+          "STAT/SDP/HBA1": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "BST": {
+      "STAT": {
+        "BST": {
+          "STAT/BST/LBA": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "FPGA_bst_offload_hdr_eth_destination_mac_RW_default": [
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB"
+              ],
+              "FPGA_bst_offload_hdr_ip_destination_address_RW_default": [
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1"
+              ]
+            }
+          },
+          "STAT/BST/HBA0": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "FPGA_bst_offload_hdr_eth_destination_mac_RW_default": [
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB"
+              ],
+              "FPGA_bst_offload_hdr_ip_destination_address_RW_default": [
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1"
+              ]
+            }
+          },
+          "STAT/BST/HBA1": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "FPGA_bst_offload_hdr_eth_destination_mac_RW_default": [
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB"
+              ],
+              "FPGA_bst_offload_hdr_ip_destination_address_RW_default": [
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "SST": {
+      "STAT": {
+        "SST": {
+          "STAT/SST/LBA": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "FPGA_sst_offload_hdr_eth_destination_mac_RW_default": [
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB"
+              ],
+              "FPGA_sst_offload_hdr_ip_destination_address_RW_default": [
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1"
+              ]
+            }
+          },
+          "STAT/SST/HBA0": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "FPGA_sst_offload_hdr_eth_destination_mac_RW_default": [
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB"
+              ],
+              "FPGA_sst_offload_hdr_ip_destination_address_RW_default": [
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1"
+              ]
+            }
+          },
+          "STAT/SST/HBA1": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "FPGA_sst_offload_hdr_eth_destination_mac_RW_default": [
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB"
+              ],
+              "FPGA_sst_offload_hdr_ip_destination_address_RW_default": [
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "XST": {
+      "STAT": {
+        "XST": {
+          "STAT/XST/LBA": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "FPGA_xst_offload_hdr_eth_destination_mac_RW_default": [
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB"
+              ],
+              "FPGA_xst_offload_hdr_ip_destination_address_RW_default": [
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1"
+              ]
+            }
+          },
+          "STAT/XST/HBA0": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "FPGA_xst_offload_hdr_eth_destination_mac_RW_default": [
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB"
+              ],
+              "FPGA_xst_offload_hdr_ip_destination_address_RW_default": [
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1"
+              ]
+            }
+          },
+          "STAT/XST/HBA1": {
+            "properties": {
+              "OPC_Server_Name": [
+                "sdptr-sim"
+              ],
+              "OPC_Server_Port": [
+                "4840"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "FPGA_xst_offload_hdr_eth_destination_mac_RW_default": [
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB",
+                "01:23:45:67:89:AB"
+              ],
+              "FPGA_xst_offload_hdr_ip_destination_address_RW_default": [
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1",
+                "127.0.0.1"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "UNB2": {
+      "STAT": {
+        "UNB2": {
+          "STAT/UNB2/L0": {
+            "properties": {
+              "OPC_Server_Name": [
+                "unb2-sim"
+              ],
+              "OPC_Server_Port": [
+                "4841"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "UNB2_On_Off_timeout": [
+                "1"
+              ]
+            }
+          },
+          "STAT/UNB2/L1": {
+            "properties": {
+              "OPC_Server_Name": [
+                "unb2-sim"
+              ],
+              "OPC_Server_Port": [
+                "4841"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "UNB2_On_Off_timeout": [
+                "1"
+              ]
+            }
+          },
+          "STAT/UNB2/H0": {
+            "properties": {
+              "OPC_Server_Name": [
+                "unb2-sim"
+              ],
+              "OPC_Server_Port": [
+                "4841"
+              ],
+              "OPC_Time_Out": [
+                "5.0"
+              ],
+              "UNB2_On_Off_timeout": [
+                "1"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "TileBeam": {
+      "STAT": {
+        "TileBeam": {
+          "STAT/TileBeam/HBA0": {
+            "properties": {
+              "Tracking_enabled_RW_default": [
+                "True"
+              ]
+            }
+          },
+          "STAT/TileBeam/HBA1": {
+            "properties": {
+              "Tracking_enabled_RW_default": [
+                "True"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "StationManager": {
+      "STAT": {
+        "StationManager": {
+          "STAT/StationManager/1": {
+            "properties": {
+              "Suppress_State_Transition_Failures": [
+                "True"
+              ]
+            }
+          }
+        }
+      }
+    },
+    "ObservationControl": {
+      "STAT": {
+        "Observation": {
+          "STAT/Observation/1": {
+          }
+        },
+        "ObservationControl": {
+          "STAT/ObservationControl/1": {
+            "properties": {
+              "Power_Children": [
+                "STAT/Observation/1"
+              ],
+              "Control_Children": [
+                "STAT/Observation/1"
+              ]
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/README.md b/README.md
index dbc58631e3705fbcf066a3f6b0bde00a0125ffbc..1dcb85bc833d894f5d33dea39ffb30be39fdacee 100644
--- a/README.md
+++ b/README.md
@@ -46,6 +46,48 @@ You will also need:
 * docker-compose
 * make
 * bash
+* dig (dnsutils package on ubuntu/debian)
+* wget
+
+## Start dev environment
+
+For local development a dev environment is needed. To setup this environment run
+
+```
+source ./sbin/prepare_dev_env.sh
+```
+
+This will install `jumppad`, if not present yet as well as creating the docker volume needed to simulate the station
+nomad cluster.
+
+Afterwards run
+
+```
+jumppad up infra/dev
+```
+
+to start the dev environment including tango.
+
+Nomad is now available at http://localhost:4646/
+
+## Start dev environment
+
+For local development a dev environment is needed. To setup this environment run
+
+```
+./sbin/prepare_dev_env.sh
+```
+
+This will install `jumppad`, if not present yet as well as creating the docker volume needed to simulate the station
+nomad cluster.
+
+Afterwards run
+
+```
+jumppad up infra/dev
+```
+
+to start the dev environment including tango.
 
 ## Bootstrap
 
@@ -115,6 +157,7 @@ Next change the version in the following places:
 
 # Release Notes
 
+* 0.23.0 Migrate execution environment to nomad
 * 0.22.0 Split `Antennafield` in `AFL` and `AFH` devices in order to separate Low-Band
          and High-Band functionalities
          Removed `Antenna_Type_R` attribute from antennafield devices  
diff --git a/bin/dump_ConfigDb.sh b/bin/dump_ConfigDb.sh
index af76d68da825fbe583156f472cc796f7786b4d5c..b58beff1b37b7b8401bcb937e8167840706201c0 100755
--- a/bin/dump_ConfigDb.sh
+++ b/bin/dump_ConfigDb.sh
@@ -1,9 +1,7 @@
 #!/bin/bash
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
+#
+# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
 # SPDX-License-Identifier: Apache-2.0
+#
 
-# writes the JSON dump to stdout, Do not change -i into -it incompatible with gitlab ci!
-docker exec -i dsconfig bash -c '
-  python -m dsconfig.dump > /tmp/dsconfig-configdb-dump.json
-  /manage_object_properties.py -r > /tmp/dsconfig-objectdb-dump.json
-  /merge_json.py /tmp/dsconfig-objectdb-dump.json /tmp/dsconfig-configdb-dump.json'
+"${LOFAR20_DIR}/sbin/dsconfig.sh" --dump
diff --git a/bin/itango_console.sh b/bin/itango_console.sh
index 20f2c2923da0cb12a6feaca853104418b4ff7026..3e19073ee164a4bf80e7061998d2977a833414cd 100755
--- a/bin/itango_console.sh
+++ b/bin/itango_console.sh
@@ -1,5 +1,49 @@
 #!/bin/bash
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
+#
+# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
 # SPDX-License-Identifier: Apache-2.0
+#
 
-exec docker exec -it itango itango3
+# defaults
+network="station"
+
+# list of arguments expected in the input
+optstring_long="help,network::"
+optstring="hn::"
+
+options=$(getopt -l ${optstring_long} -o ${optstring} -- "$@")
+
+eval set -- "$options"
+
+
+while true; do
+  case ${1} in
+    -h|--help)
+      usage
+      exit 0
+      ;;
+    -n|--network)
+      shift
+      network="$1"
+      ;;
+    --)
+    shift
+    break;;
+  esac
+  shift
+done
+
+if [ -z "$TAG" ]; then
+  TAG="latest"
+fi
+
+echo "Use docker network '$network'"
+docker_args=(run --rm  -e "TANGO_HOST=$TANGO_HOST" --network="$network" -it)
+docker_image="git.astron.nl:5000/lofar2.0/tango/itango:$TAG"
+
+if [[ -n "$DNS" ]]; then
+  echo "Set docker dns to $DNS"
+  docker_args+=(--dns "$DNS")
+fi
+
+docker "${docker_args[@]}" "$docker_image" itango3
diff --git a/bin/itango_shell.sh b/bin/itango_shell.sh
index 8a8d5e38080bb71d884b43645ce5c892fce0a07e..92e92eda3c5cddce689676c0bcac0efe0d0d441a 100755
--- a/bin/itango_shell.sh
+++ b/bin/itango_shell.sh
@@ -1,5 +1,7 @@
 #!/bin/bash
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
+#
+# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
 # SPDX-License-Identifier: Apache-2.0
+#
 
-exec docker exec -it itango /bin/bash
+exec docker exec -e "TANGO_HOST=$TANGO_HOST:$TANGO_PORT" -it itango /bin/bash
diff --git a/deploy/README.md b/deploy/README.md
deleted file mode 100644
index 5f49ffc5c340dacf671e607f55c36f68f4db774a..0000000000000000000000000000000000000000
--- a/deploy/README.md
+++ /dev/null
@@ -1,44 +0,0 @@
-# Deployments
-
-Deploying is achieved through gitlab environments as well as ansible.
-Concretely the following files are involved:
-
-1. [deploy.yml](deploy.yml)
-2. [ansible.cfg](ansible.cfg)
-3. [.gitlab-ci.yml](../.gitlab-ci.yml)
-
-Once a tagged commit hits master a job is created for this tag that needs to
-be manually triggered. You should be able to find this pipeline on the
-[tags overview](https://git.astron.nl/lofar2.0/tango/-/tags).
-
-From here, with prior consent, the deployment can be started for any
-environment.
-
-The gitlab-ci.yml job defines each of the environments in parameterized way.
-The templated `.base_deploy` needs to be extended and have several variables
-defined. These variables need to be fed from
-[protected variables](https://git.astron.nl/lofar2.0/tango/-/settings/ci_cd)
-within gitlab.
-
-Below is a practical example of how to define an environment.
-
-```
-.deploy_example_base:
-  extends: .base_deploy
-  variables:
-    DEPLOY_USER: $EXAMPLE_USERNAME
-    DEPLOY_HOST: $EXAMPLE_HOSTNAME
-    DEPLOY_CONFIG: $EXAMPLE_CONFIG
-    DEPLOY_KEY: $EXAMPLE_KEY
-deploy_l2ts_start:
-  extends: .deploy_example_base
-  environment:
-    name: example
-```
-
-Such an environment will only appear in the
-[environments overview](https://git.astron.nl/lofar2.0/tango/-/environments)
-after the first deployment.
-
-The process of this deployment is handled through ansible as found in
-`deploy.yml`.
diff --git a/deploy/ansible.cfg b/deploy/ansible.cfg
deleted file mode 100644
index 8da5e9439f11320c5e90d51bbe6ad7d7f8d9798c..0000000000000000000000000000000000000000
--- a/deploy/ansible.cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-[defaults]
-host_key_checking = False
-inventory = hosts
diff --git a/deploy/deploy.yml b/deploy/deploy.yml
deleted file mode 100644
index 455c9da59ea78e724dc448e476df8fffa0ff6522..0000000000000000000000000000000000000000
--- a/deploy/deploy.yml
+++ /dev/null
@@ -1,106 +0,0 @@
----
-- name: StationControl Early Deployment
-  hosts: all
-  vars:
-    base_station_config: "LOFAR_ConfigDb.json"
-    install_path: ~/git/tango
-    databaseds_port: 10000
-  tasks:
-     - name: Check make installed
-       include_tasks:
-         file: tasks/check_binary_install.yml
-       vars:
-         program: make
-     - name: Check git installed
-       include_tasks:
-         file: tasks/check_binary_install.yml
-       vars:
-         program: git
-     - name: Register tango directory status
-       shell: cd {{ install_path }}
-       args:
-         chdir: ~
-       changed_when: false
-       failed_when: tango_directory.rc not in [0,1]
-       register: tango_directory
-     - name: Register pending changes
-       shell: "! (git status | grep Changes)"
-       args:
-         chdir: "{{ install_path }}"
-       changed_when: false
-       failed_when: pending_changes.rc not in [0,1]
-       register: pending_changes
-# Several issues with this variable being unset in ansible 2.2
-# where encountered, keep debug in case needed
-     - debug: var=pending_changes
-     - name: Check tango directory status
-       fail:
-         msg: "Tango directory appears to be missing!"
-       when: tango_directory.rc not in [0]
-     - name: Check pending changes
-       fail:
-         msg: "Deployment repository seems to have pending changes!"
-       when: pending_changes.rc not in [0]
-     - name: Stop Current Station
-       changed_when: false
-       shell: "make stop"
-       args:
-         chdir: "{{ install_path }}/docker-compose"
-     - name: Git Fetch All
-       changed_when: false
-       shell: "git fetch --all"
-       args:
-         chdir: "{{ install_path }}"
-     - name: Update Sources
-       changed_when: false
-       shell: "git checkout {{ station_version }}"
-       args:
-         chdir: "{{ install_path }}"
-     - name: Pull Images
-       changed_when: false
-       shell: "make pull"
-       args:
-         chdir: "{{ install_path }}/docker-compose"
-     - name: Build Images
-       changed_when: false
-       shell: "make build"
-       args:
-         chdir: "{{ install_path }}/docker-compose"
-     - name: Start Database
-       changed_when: false
-       shell: "make minimal"
-       args:
-         chdir: "{{ install_path }}/docker-compose"
-     - name: Wait for databaseds
-       ansible.builtin.wait_for:
-         port: "{{ databaseds_port }}"
-         delay: 10
-     - name: Update Base Database Config
-       include_tasks:
-         file: tasks/update_database_config.yml
-       vars:
-         database_config: "{{ base_station_config }}"
-         base_path: "{{ install_path }}"
-     - name: Verify Base Database Config
-       include_tasks:
-         file: tasks/verify_database_config.yml
-       vars:
-         database_config: "{{ base_station_config }}"
-         base_path: "{{ install_path }}"
-     - name: Update Station Database Config
-       include_tasks:
-         file: tasks/update_database_config.yml
-       vars:
-         database_config: "stations/{{ station_config }}"
-         base_path: "{{ install_path }}"
-     - name: Verify Station Database Config
-       include_tasks:
-         file: tasks/verify_database_config.yml
-       vars:
-         database_config: "stations/{{ station_config }}"
-         base_path: "{{ install_path }}"
-     - name: Start Station
-       changed_when: false
-       shell: "make start"
-       args:
-         chdir: "{{ install_path }}/docker-compose"
diff --git a/deploy/tasks/check_binary_install.yml b/deploy/tasks/check_binary_install.yml
deleted file mode 100644
index f114fd87e7b7bd25d7f6b848b25c32aa9324efa2..0000000000000000000000000000000000000000
--- a/deploy/tasks/check_binary_install.yml
+++ /dev/null
@@ -1,9 +0,0 @@
- - name: Register installation status
-   command: which {{ program }}
-   changed_when: false
-   failed_when: installed.rc not in [0,1]
-   register: installed
- - name: Check installation status
-   fail:
-     msg: "{{ program }} does not appear to be installed!"
-   when: installed.rc not in [0]
\ No newline at end of file
diff --git a/deploy/tasks/update_database_config.yml b/deploy/tasks/update_database_config.yml
deleted file mode 100644
index be321c5769d48a6443de18340033c8cdaffd661f..0000000000000000000000000000000000000000
--- a/deploy/tasks/update_database_config.yml
+++ /dev/null
@@ -1,5 +0,0 @@
- - name: Update Database Config
-   changed_when: false
-   shell: "./sbin/load_ConfigDb.sh CDB/{{ database_config }}"
-   args:
-     chdir: "{{ base_path }}"
\ No newline at end of file
diff --git a/deploy/tasks/verify_database_config.yml b/deploy/tasks/verify_database_config.yml
deleted file mode 100644
index 1df3681e4eae0fe7d04cede8f5cd3820aa6cbd67..0000000000000000000000000000000000000000
--- a/deploy/tasks/verify_database_config.yml
+++ /dev/null
@@ -1,9 +0,0 @@
- - name: Get Database Config
-   changed_when: false
-   shell: "./sbin/load_ConfigDb.sh CDB/{{ database_config }} 2>&1"
-   register: dsconfig_result
-   args:
-     chdir: "{{ base_path }}"
- - name: Verify Database Config
-   when: '"No changes needed" in dsconfig_result.stdout'
-   debug: msg="Database changes stored"
\ No newline at end of file
diff --git a/docker-compose/Makefile b/docker-compose/Makefile
index 4929af30299e3eea95c6c03c93f05690e6394258..c8658b65a32ffa53663485ae1d6aeba6ee54e9ca 100644
--- a/docker-compose/Makefile
+++ b/docker-compose/Makefile
@@ -6,19 +6,21 @@ MAKEPATH := $(abspath $(lastword $(MAKEFILE_LIST)))
 BASEDIR := $(notdir $(patsubst %/,%,$(dir $(MAKEPATH))))
 
 TAG ?= latest
+DNS ?= 127.0.0.11
 
 DOCKER_COMPOSE ?= docker compose
 
 DOCKER_COMPOSE_ENV_FILE := $(abspath .env)
 COMPOSE_FILES := $(wildcard *.yml)
-COMPOSE_FILE_ARGS := --env-file $(DOCKER_COMPOSE_ENV_FILE) $(foreach yml,$(COMPOSE_FILES),-f $(yml))
-
 ATTACH_COMPOSE_FILE_ARGS := $(foreach yml,$(filter-out tango.yml,$(COMPOSE_FILES)),-f $(yml))
+BUILD_ONLY_FILE_ARGS := -f tango.yml
+COMPOSE_FILE_ARGS := --env-file $(DOCKER_COMPOSE_ENV_FILE) $(ATTACH_COMPOSE_FILE_ARGS)
+
 
-# The default Docker network mode is tangonet.  The "host" network
+# The default Docker network mode is station.  The "host" network
 # mode will make the tangodb and archiverdb ports clash,
 # But we allow to overwrite it.
-NETWORK_MODE ?= tangonet
+NETWORK_MODE ?= station
 
 # Timeout used to await services to become healthy
 TIMEOUT ?= 300
@@ -97,11 +99,6 @@ endif
 # prevent collisions with jobs from the same project running at the same
 # time.
 #
-ifneq ($(CI_JOB_ID),)
-    NETWORK_MODE := tangonet-$(CI_JOB_ID)
-else
-    $(info Network mode cannot be host for the archiver! It won't work unless you set the env var CI_JOB_ID=local)
-endif
 
 ifeq ($(OS),Windows_NT)
     $(error Sorry, Windows is not supported yet)
@@ -166,7 +163,8 @@ DOCKER_COMPOSE_ARGS := DISPLAY=$(DISPLAY) \
     CONTAINER_EXECUTION_UID=$(shell id -u) \
     DOCKER_GID=$(DOCKER_GID) \
     TEST_MODULE=$(INTEGRATION_MODULE) \
-    TAG=$(TAG)
+    TAG=$(TAG) \
+    DNS=$(DNS)
 
 
 .PHONY: up base base-nocache down minimal context run integration start stop restart build build-nocache status clean pull help await
@@ -181,15 +179,15 @@ ifneq ($(NO_BASE),1)
 endif
 
 base-nocache: context ## Rebuild base lofar device image
-	$(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) build --no-cache --progress=plain lofar-device-base
+	$(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) $(BUILD_ONLY_FILE_ARGS) build --no-cache --progress=plain lofar-device-base
 
 build: base ## build images
 	# docker-compose does not support build dependencies, so manage those here
-	$(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) build --parallel --progress=plain $(SERVICE)
+	$(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) $(BUILD_ONLY_FILE_ARGS) build --parallel --progress=plain $(SERVICE)
 
 build-nocache: base-nocache ## rebuild images from scratch
 	# docker-compose does not support build dependencies, so manage those here
-	$(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) build --no-cache --progress=plain $(SERVICE)
+	$(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) $(BUILD_ONLY_FILE_ARGS) build --no-cache --progress=plain $(SERVICE)
 
 up: base minimal  ## start the base TANGO system and prepare requested services
 	$(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) up --no-start --no-recreate $(SERVICE)
@@ -219,7 +217,7 @@ ifneq ($(NETWORK_MODE),host)
 	docker network inspect 9000-$(NETWORK_MODE) &> /dev/null || ([ $$? -ne 0 ] && docker network create 9000-$(NETWORK_MODE) -o com.docker.network.driver.mtu=9000)
 endif
 
-	$(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) -f tango.yml -f networks.yml up --no-recreate -d
+
 
 context: ## Move and build the necessary files to create minimal docker context
 	rm -rf ./tmp; \
@@ -236,8 +234,8 @@ context: ## Move and build the necessary files to create minimal docker context
 bootstrap: pull build # first start, initialise from scratch
 	$(MAKE) start dsconfig # boot up containers to load configurations
 	sleep 5 # wait for dsconfig container to come up
-	../sbin/update_ConfigDb.sh ../CDB/LOFAR_ConfigDb.json # load default configuration
-	../sbin/update_ConfigDb.sh ../CDB/stations/simulators_ConfigDb.json # by default, use simulators
+	../sbin/dsconfig.sh --update ../CDB/LOFAR_ConfigDb.json # load default configuration
+	../sbin/dsconfig.sh --update ../CDB/stations/simulators_ConfigDb.json # by default, use simulators
 
 start: up ## start a service (usage: make start <servicename>)
 	if [ $(UNAME_S) = Linux ]; then touch ~/.Xauthority; chmod a+r ~/.Xauthority; fi
@@ -266,16 +264,18 @@ await:  ## Await every container with total max timeout of 300, do not reset tim
 		if [ -z "$${service_has_health}" ]; then \
 			continue; \
 		fi; \
+		echo -n "Whait for service $${i} to become healthy .."; \
 		while [ "$$(docker inspect -f '{{.State.Health.Status}}' $${current_service})" != "healthy" ] ; do \
-			sleep 1; \
+			echo -n '.'; \
+			sleep 2; \
 			time=$$(expr $$time + 1); \
 			if [ $${time} -gt $(TIMEOUT) ]; then \
-				echo "Timeout reached waiting for $${i} to become healthy"; \
+				echo "timeout"; \
 				docker logs $${i}; \
 				exit 1; \
 			fi; \
 		done; \
-		echo "Service $${i} is healthy"; \
+		echo ". [ok]"; \
 	done
 
 status:  ## show the container status
diff --git a/docker-compose/README.md b/docker-compose/README.md
index 05882d6ad8bdd4aeafd4554cc07dfbad7bd47f62..528f253acfd2b10d4af91102ce965127932311c1 100644
--- a/docker-compose/README.md
+++ b/docker-compose/README.md
@@ -42,12 +42,9 @@ are used in production.
     - webservers / user interfaces
         - jupyterlab
         - [grafana](grafana/README.md)
-        - [http-json-schemas](http-json-schemas/README.md)
     - logging / monitoring
         - prometheus-node-exporter
-        - [tango-prometheus-exporter](tango-prometheus-exporter/README.md)
-        - logstash (grafana-logstash-output-loki)
-        - loki
+        - [tango-prometheus-exporter](tango-prometheus-exporter/README.md)        - loki
     - tango
         - itango
         - tango-rest
diff --git a/docker-compose/device-afh.yml b/docker-compose/device-afh.yml
index 849463c1c73705e4194ddb9979651e52380f74a5..f116d5e728189e6ed260939bf4baa85670271dc1 100644
--- a/docker-compose/device-afh.yml
+++ b/docker-compose/device-afh.yml
@@ -27,6 +27,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5715:5715" # unique port for this DS
       - "5815:5815" # ZeroMQ event port
@@ -50,7 +51,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-afh AFH STAT -v -ORBendPoint giop:tcp:0:5715 -ORBendPointPublish giop:tcp:${HOSTNAME}:5715
+      - l2ss-ds AFH STAT -v -ORBendPoint giop:tcp:0:5715 -ORBendPointPublish giop:tcp:${HOSTNAME}:5715
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-afl.yml b/docker-compose/device-afl.yml
index 5b47626ec93647070f0db80932baf9b3779e1f03..8909990d47dea92c2eef6c59c0e572c37c935090 100644
--- a/docker-compose/device-afl.yml
+++ b/docker-compose/device-afl.yml
@@ -50,7 +50,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-afl AFL STAT -v -ORBendPoint giop:tcp:0:5730 -ORBendPointPublish giop:tcp:${HOSTNAME}:5730
+      - l2ss-ds AFL STAT -v -ORBendPoint giop:tcp:0:5730 -ORBendPointPublish giop:tcp:${HOSTNAME}:5730
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-aps.yml b/docker-compose/device-aps.yml
index 23690af220e129e34730c6bcd62c95d98520e937..dc06afcb987ab12a8b86235e32039f065a772586 100644
--- a/docker-compose/device-aps.yml
+++ b/docker-compose/device-aps.yml
@@ -18,6 +18,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5728:5728" # unique port for this DS
       - "5828:5828" # ZeroMQ event port
@@ -41,7 +42,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-aps aps STAT -v  -v -ORBendPoint giop:tcp:device-aps:5728 -ORBendPointPublish giop:tcp:${HOSTNAME}:5728
+      - l2ss-ds APS STAT -v  -v -ORBendPoint giop:tcp:device-aps:5728 -ORBendPointPublish giop:tcp:${HOSTNAME}:5728
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-apsct.yml b/docker-compose/device-apsct.yml
index 86c24b6a69b8462d61fe480fb8c39083c982c130..8ebd5c1045f029fd31463d5985579c528849e9ba 100644
--- a/docker-compose/device-apsct.yml
+++ b/docker-compose/device-apsct.yml
@@ -26,6 +26,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5709:5709" # unique port for this DS
       - "5809:5809" # ZeroMQ event port
@@ -49,7 +50,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-apsct Apsct STAT -v  -v -ORBendPoint giop:tcp:device-apsct:5709 -ORBendPointPublish giop:tcp:${HOSTNAME}:5709
+      - l2ss-ds APSCT STAT -v  -v -ORBendPoint giop:tcp:device-apsct:5709 -ORBendPointPublish giop:tcp:${HOSTNAME}:5709
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-apspu.yml b/docker-compose/device-apspu.yml
index ac585e6c480c957546d55b6c95ff7852e026ca2a..5c268b08179fb1931a44bcdb0f055ac5032c4dd6 100644
--- a/docker-compose/device-apspu.yml
+++ b/docker-compose/device-apspu.yml
@@ -26,6 +26,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5710:5710" # unique port for this DS
       - "5810:5810" # ZeroMQ event port
@@ -49,7 +50,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-apspu Apspu STAT -v -ORBendPoint giop:tcp:device-apspu:5710 -ORBendPointPublish giop:tcp:${HOSTNAME}:5710
+      - l2ss-ds APSPU STAT -v -ORBendPoint giop:tcp:device-apspu:5710 -ORBendPointPublish giop:tcp:${HOSTNAME}:5710
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-beamlet.yml b/docker-compose/device-beamlet.yml
index 7c734e0e3ed33c6ec2e25fdeb6df91b3dead4f96..028376efc62e777cbf6bf5d4dc93025957def778 100644
--- a/docker-compose/device-beamlet.yml
+++ b/docker-compose/device-beamlet.yml
@@ -26,6 +26,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5712:5712" # unique port for this DS
       - "5812:5812" # ZeroMQ event port
@@ -49,7 +50,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-beamlet Beamlet STAT -v -ORBendPoint giop:tcp:0:5712 -ORBendPointPublish giop:tcp:${HOSTNAME}:5712
+      - l2ss-ds Beamlet STAT -v -ORBendPoint giop:tcp:0:5712 -ORBendPointPublish giop:tcp:${HOSTNAME}:5712
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-bst.yml b/docker-compose/device-bst.yml
index e1fd186004c2d6016751f08778306d66a530eb3f..23db88c158f9798cd1e98c4c18c4a1f26d1a95c6 100644
--- a/docker-compose/device-bst.yml
+++ b/docker-compose/device-bst.yml
@@ -27,6 +27,7 @@ services:
     networks:
       - control
       - data
+    dns: ${DNS}
     ports:
       - "5003:5003/udp" # port to receive SST UDP packets on (first antennafield)
       - "5103:5103/tcp" # port to emit SST TCP packets on
@@ -56,7 +57,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-bst BST STAT -v -ORBendPoint giop:tcp:0:5717 -ORBendPointPublish giop:tcp:${HOSTNAME}:5717
+      - l2ss-ds BST STAT -v -ORBendPoint giop:tcp:0:5717 -ORBendPointPublish giop:tcp:${HOSTNAME}:5717
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-calibration.yml b/docker-compose/device-calibration.yml
index 387b02286501e90f3087d71abf3b24486ef0913f..495e8ea13cd7c25da02642f3b075eff7a265e3de 100644
--- a/docker-compose/device-calibration.yml
+++ b/docker-compose/device-calibration.yml
@@ -26,6 +26,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5724:5724" # unique port for this DS
       - "5824:5824" # ZeroMQ event port
@@ -51,9 +52,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-calibration Calibration STAT -v -ORBendPoint giop:tcp:0:5724 -ORBendPointPublish giop:tcp:${HOSTNAME}:5724
+      - l2ss-ds Calibration STAT -v -ORBendPoint giop:tcp:0:5724 -ORBendPointPublish giop:tcp:${HOSTNAME}:5724
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
-    depends_on:
-      - object-storage
diff --git a/docker-compose/device-ccd.yml b/docker-compose/device-ccd.yml
index 950800e33372a84d3d1f5204cb8aaea1bf3b8d99..5d4292c347af3eda48d1e530492184cab02d1e3f 100644
--- a/docker-compose/device-ccd.yml
+++ b/docker-compose/device-ccd.yml
@@ -26,6 +26,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5721:5721" # unique port for this DS
       - "5821:5821" # ZeroMQ event port
@@ -49,7 +50,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-ccd Ccd STAT -v -ORBendPoint giop:tcp:device-ccd:5721 -ORBendPointPublish giop:tcp:${HOSTNAME}:5721
+      - l2ss-ds CCD STAT -v -ORBendPoint giop:tcp:device-ccd:5721 -ORBendPointPublish giop:tcp:${HOSTNAME}:5721
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-configuration.yml b/docker-compose/device-configuration.yml
index 1a923f15b33585c8f2c397e7ac0afa5855ac464f..2cbdfb6a5fc9391a7e651c9e74c1635fa34ee730 100644
--- a/docker-compose/device-configuration.yml
+++ b/docker-compose/device-configuration.yml
@@ -26,6 +26,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5722:5722" # unique port for this DS
       - "5822:5822" # ZeroMQ event port
@@ -49,9 +50,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-configuration Configuration STAT -v -ORBendPoint giop:tcp:device-configuration:5722 -ORBendPointPublish giop:tcp:${HOSTNAME}:5722
+      - l2ss-ds Configuration STAT -v -ORBendPoint giop:tcp:device-configuration:5722 -ORBendPointPublish giop:tcp:${HOSTNAME}:5722
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
-    depends_on:
-      - http-json-schemas
diff --git a/docker-compose/device-digitalbeam.yml b/docker-compose/device-digitalbeam.yml
index b093ee63e050f20205b6b2197fbe87f45daf92d6..c47268f284f69bf6d397dee9c4ef5a250d3c7620 100644
--- a/docker-compose/device-digitalbeam.yml
+++ b/docker-compose/device-digitalbeam.yml
@@ -26,6 +26,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5713:5713" # unique port for this DS
       - "5813:5813" # ZeroMQ event port
@@ -49,7 +50,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-digitalbeam DigitalBeam STAT -v -ORBendPoint giop:tcp:0:5713 -ORBendPointPublish giop:tcp:${HOSTNAME}:5713
+      - l2ss-ds DigitalBeam STAT -v -ORBendPoint giop:tcp:0:5713 -ORBendPointPublish giop:tcp:${HOSTNAME}:5713
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-docker.yml b/docker-compose/device-docker.yml
index a9080eeb5263437fe42d22e91dc6c2509f213596..88ef8d8984dd709e9d99cc2f458d4c18972f07da 100644
--- a/docker-compose/device-docker.yml
+++ b/docker-compose/device-docker.yml
@@ -26,6 +26,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5705:5705" # unique port for this DS
       - "5805:5805" # ZeroMQ event port
@@ -51,7 +52,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-docker Docker STAT -v -ORBendPoint giop:tcp:0:5705 -ORBendPointPublish giop:tcp:${HOSTNAME}:5705
+      - l2ss-ds Docker STAT -v -ORBendPoint giop:tcp:0:5705 -ORBendPointPublish giop:tcp:${HOSTNAME}:5705
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-ec.yml b/docker-compose/device-ec.yml
index f944c751acb3ae72cba986a63b4f7e5816d68e04..ac5b62a7cda6283123762a4325520a60dc2d8801 100644
--- a/docker-compose/device-ec.yml
+++ b/docker-compose/device-ec.yml
@@ -21,6 +21,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5729:5729" # unique port for this DS
       - "5829:5829" # ZeroMQ event port
@@ -44,7 +45,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-ec ec STAT -v -ORBendPoint giop:tcp:device-ec:5729 -ORBendPointPublish giop:tcp:${HOSTNAME}:5729
+      - l2ss-ds EC STAT -v -ORBendPoint giop:tcp:device-ec:5729 -ORBendPointPublish giop:tcp:${HOSTNAME}:5729
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-observation-control.yml b/docker-compose/device-observation-control.yml
index b5b7f9fe50defba4487766a576e6213e6fb4ac66..c21733db51c0a6d425114689c5b07e85887a44f3 100644
--- a/docker-compose/device-observation-control.yml
+++ b/docker-compose/device-observation-control.yml
@@ -25,6 +25,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5703:5703" # unique port for this DS
       - "5803:5803" # ZeroMQ event port
@@ -48,9 +49,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-observationcontrol ObservationControl STAT -v -ORBendPoint giop:tcp:0:5703 -ORBendPointPublish giop:tcp:${HOSTNAME}:5703
+      - l2ss-ds ObservationControl STAT -v -ORBendPoint giop:tcp:0:5703 -ORBendPointPublish giop:tcp:${HOSTNAME}:5703
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
-    depends_on:
-      - http-json-schemas
diff --git a/docker-compose/device-pcon.yml b/docker-compose/device-pcon.yml
index e833bb55d4e35eb842cf91a42897dcf1ceb6e2aa..c404f8ad15e9a3031a5ccaffc8ed37613294f5e1 100644
--- a/docker-compose/device-pcon.yml
+++ b/docker-compose/device-pcon.yml
@@ -21,6 +21,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5720:5720" # unique port for this DS
       - "5820:5820" # ZeroMQ event port
@@ -44,7 +45,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-pcon pcon STAT -v -ORBendPoint giop:tcp:device-pcon:5720 -ORBendPointPublish giop:tcp:${HOSTNAME}:5720
+      - l2ss-ds PCON STAT -v -ORBendPoint giop:tcp:device-pcon:5720 -ORBendPointPublish giop:tcp:${HOSTNAME}:5720
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-psoc.yml b/docker-compose/device-psoc.yml
index 998a0ad2fda23aab5232ebd40632392fad353f08..5e19bd580df9633ece4181e69c9e1f01611bf1ca 100644
--- a/docker-compose/device-psoc.yml
+++ b/docker-compose/device-psoc.yml
@@ -21,6 +21,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5719:5719" # unique port for this DS
       - "5819:5819" # ZeroMQ event port
@@ -44,7 +45,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-psoc PSOC STAT -v -ORBendPoint giop:tcp:device-psoc:5719 -ORBendPointPublish giop:tcp:${HOSTNAME}:5719
+      - l2ss-ds PSOC STAT -v -ORBendPoint giop:tcp:device-psoc:5719 -ORBendPointPublish giop:tcp:${HOSTNAME}:5719
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-recvh.yml b/docker-compose/device-recvh.yml
index feb0ce875303bc84aec7c6eec1465d45958e7685..d366e2bad26cbdc7625fa60c79478d624b68126a 100644
--- a/docker-compose/device-recvh.yml
+++ b/docker-compose/device-recvh.yml
@@ -18,6 +18,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5725:5725" # unique port for this DS
       - "5825:5825" # ZeroMQ event port
@@ -41,7 +42,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-recvh RECVH STAT -v -ORBendPoint giop:tcp:device-recvh:5725 -ORBendPointPublish giop:tcp:${HOSTNAME}:5725
+      - l2ss-ds RECVH STAT -v -ORBendPoint giop:tcp:device-recvh:5725 -ORBendPointPublish giop:tcp:${HOSTNAME}:5725
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-recvl.yml b/docker-compose/device-recvl.yml
index 810505463033f32f2afcc4a029a8ecb3f106c0c5..4a2894350281c6573bb37763123fa4d97335b893 100644
--- a/docker-compose/device-recvl.yml
+++ b/docker-compose/device-recvl.yml
@@ -18,6 +18,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5726:5726" # unique port for this DS
       - "5826:5826" # ZeroMQ event port
@@ -41,7 +42,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-recvl RECVL STAT -v -ORBendPoint giop:tcp:device-recvl:5726 -ORBendPointPublish giop:tcp:${HOSTNAME}:5726
+      - l2ss-ds RECVL STAT -v -ORBendPoint giop:tcp:device-recvl:5726 -ORBendPointPublish giop:tcp:${HOSTNAME}:5726
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-sdp.yml b/docker-compose/device-sdp.yml
index 02ce3f99ebc945f93ec340e7da14a2e318f41608..dd47fad159429be378aa1e82679cc127b29f06aa 100644
--- a/docker-compose/device-sdp.yml
+++ b/docker-compose/device-sdp.yml
@@ -26,6 +26,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5701:5701" # unique port for this DS
       - "5801:5801" # ZeroMQ event port
@@ -49,7 +50,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-sdp SDP STAT -v -ORBendPoint giop:tcp:device-sdp:5701 -ORBendPointPublish giop:tcp:${HOSTNAME}:5701
+      - l2ss-ds SDP STAT -v -ORBendPoint giop:tcp:device-sdp:5701 -ORBendPointPublish giop:tcp:${HOSTNAME}:5701
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-sdpfirmware.yml b/docker-compose/device-sdpfirmware.yml
index 5fd34bf0f42bfc3df1ae14e7d50134d1f0faa62b..c61c4a565a13bd242b08adf7de9735aeec6c15f2 100644
--- a/docker-compose/device-sdpfirmware.yml
+++ b/docker-compose/device-sdpfirmware.yml
@@ -26,6 +26,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5727:5727" # unique port for this DS
       - "5827:5827" # ZeroMQ event port
@@ -49,7 +50,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-sdpfirmware SDPFirmware STAT -v -ORBendPoint giop:tcp:device-sdpfirmware:5727 -ORBendPointPublish giop:tcp:${HOSTNAME}:5727
+      - l2ss-ds SDPFirmware STAT -v -ORBendPoint giop:tcp:device-sdpfirmware:5727 -ORBendPointPublish giop:tcp:${HOSTNAME}:5727
     restart: unless-stopped
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-sst.yml b/docker-compose/device-sst.yml
index a6ba4f2b2dc7b32f328cd137e1d8e50159dc58d9..66b1da00a7291fa9041d6edae575f3b4bae78f35 100644
--- a/docker-compose/device-sst.yml
+++ b/docker-compose/device-sst.yml
@@ -27,6 +27,7 @@ services:
     networks:
       - control
       - data
+    dns: ${DNS}
     ports:
       - "5001:5001/udp" # port to receive SST UDP packets on (first antennafield)
       - "5101:5101/tcp" # port to emit SST TCP packets on
@@ -56,7 +57,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-sst SST STAT -v -ORBendPoint giop:tcp:0:5702 -ORBendPointPublish giop:tcp:${HOSTNAME}:5702
+      - l2ss-ds SST STAT -v -ORBendPoint giop:tcp:0:5702 -ORBendPointPublish giop:tcp:${HOSTNAME}:5702
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-station-manager.yml b/docker-compose/device-station-manager.yml
index 044de2d17f7cf878a8e4449cb3029bae71d09197..7570440c6aff559fef865dd2a2b0368600611563 100644
--- a/docker-compose/device-station-manager.yml
+++ b/docker-compose/device-station-manager.yml
@@ -21,6 +21,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5723:5723" # unique port for this DS
       - "5823:5823" # ZeroMQ event port
@@ -44,7 +45,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-station-manager StationManager STAT -v -ORBendPoint giop:tcp:device-station-manager:5723 -ORBendPointPublish giop:tcp:${HOSTNAME}:5723
+      - l2ss-ds StationManager STAT -v -ORBendPoint giop:tcp:device-station-manager:5723 -ORBendPointPublish giop:tcp:${HOSTNAME}:5723
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-temperature-manager.yml b/docker-compose/device-temperature-manager.yml
index 35e2defff2f45a67ddc42f632dc4565536617fe8..316a49301c354a75c664d06d2d8786356db6d800 100644
--- a/docker-compose/device-temperature-manager.yml
+++ b/docker-compose/device-temperature-manager.yml
@@ -21,6 +21,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5716:5716" # unique port for this DS
       - "5816:5816" # ZeroMQ event port
@@ -44,7 +45,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-temperaturemanager TemperatureManager STAT -v -ORBendPoint giop:tcp:0:5716 -ORBendPointPublish giop:tcp:${HOSTNAME}:5716
+      - l2ss-ds TemperatureManager STAT -v -ORBendPoint giop:tcp:0:5716 -ORBendPointPublish giop:tcp:${HOSTNAME}:5716
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-tilebeam.yml b/docker-compose/device-tilebeam.yml
index c67cb289cabfec8f73c0cb586e8633ca401b8489..6c623144d583e65d983f68d1ca96193f5fa8c1a2 100644
--- a/docker-compose/device-tilebeam.yml
+++ b/docker-compose/device-tilebeam.yml
@@ -21,6 +21,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5711:5711" # unique port for this DS
       - "5811:5811" # ZeroMQ event port
@@ -45,7 +46,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-tilebeam TileBeam STAT -v -ORBendPoint giop:tcp:0:5711 -ORBendPointPublish giop:tcp:${HOSTNAME}:5711
+      - l2ss-ds TileBeam STAT -v -ORBendPoint giop:tcp:0:5711 -ORBendPointPublish giop:tcp:${HOSTNAME}:5711
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-unb2.yml b/docker-compose/device-unb2.yml
index 82ef3846066fd78efe7eae2c26f15ddbd2d62d31..8e127bb0400021fb26d33f9a93fc41686c9d8aa1 100644
--- a/docker-compose/device-unb2.yml
+++ b/docker-compose/device-unb2.yml
@@ -26,6 +26,7 @@ services:
         max-file: "10"
     networks:
       - control
+    dns: ${DNS}
     ports:
       - "5704:5704" # unique port for this DS
       - "5804:5804" # ZeroMQ event port
@@ -49,7 +50,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-unb2 UNB2 STAT -v -ORBendPoint giop:tcp:device-unb2:5704 -ORBendPointPublish giop:tcp:${HOSTNAME}:5704
+      - l2ss-ds UNB2 STAT -v -ORBendPoint giop:tcp:device-unb2:5704 -ORBendPointPublish giop:tcp:${HOSTNAME}:5704
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/device-xst.yml b/docker-compose/device-xst.yml
index 0890e91d748ebf14754a51b5b927a38987716084..5c3bc073537060664193f7860a6e14eba46c526d 100644
--- a/docker-compose/device-xst.yml
+++ b/docker-compose/device-xst.yml
@@ -27,6 +27,7 @@ services:
     networks:
       - control
       - data
+    dns: ${DNS}
     ports:
       - "5002:5002/udp" # port to receive XST UDP packets on (first antennafield)
       - "5102:5102/tcp" # port to emit XST TCP packets on
@@ -56,7 +57,7 @@ services:
       - bin/start-ds.sh
       # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
       # can't know about our Docker port forwarding
-      - l2ss-xst XST STAT -v -ORBendPoint giop:tcp:0:5706 -ORBendPointPublish giop:tcp:${HOSTNAME}:5706
+      - l2ss-ds XST STAT -v -ORBendPoint giop:tcp:0:5706 -ORBendPointPublish giop:tcp:${HOSTNAME}:5706
     restart: on-failure
     stop_signal: SIGINT # request a graceful shutdown of Tango
     stop_grace_period: 2s
diff --git a/docker-compose/grafana/Dockerfile b/docker-compose/grafana/Dockerfile
index b8d793c0b7e55839af378d0837da813d56c7d55c..3e753e666e635fa09022345c5235d159d07d0f06 100644
--- a/docker-compose/grafana/Dockerfile
+++ b/docker-compose/grafana/Dockerfile
@@ -1,5 +1,6 @@
 FROM git.astron.nl:5000/lofar2.0/grafana-station-dashboards:latest
 
+COPY datasources/tangodb.yaml /etc/grafana/provisioning/datasources/tangodb.yaml
 COPY stationcontrol-dashboards.yaml /etc/grafana/provisioning/dashboards/
 
 RUN mkdir -p /var/lib/grafana/database
diff --git a/docker-compose/http-json-schemas.yml b/docker-compose/http-json-schemas.yml
deleted file mode 100644
index 46db5c88a5598f7bb8a7c61700dc53c97482f5e4..0000000000000000000000000000000000000000
--- a/docker-compose/http-json-schemas.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
-#
-# Docker compose file that launches a LOFAR2.0 station's
-# ObservationControl device. It also runs the dynamically
-# created Observation devices.
-#
-# Defines:
-#   - http-json-schemas: LOFAR2.0 station
-#
-version: '2.1'
-
-services:
-  http-json-schemas:
-    image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/http-json-schemas:${TAG}
-    build:
-      context: http-json-schemas
-    hostname: http-json-schemas
-    container_name: http-json-schemas
-    networks:
-      - control
-    environment:
-      - NGINX_HOST=http-json-schemas
-      - NGINX_PORT=80
-    ports:
-      - 9999:80
-    logging:
-      driver: "json-file"
-      options:
-        max-size: "100m"
-        max-file: "10"
-    restart: unless-stopped
diff --git a/docker-compose/http-json-schemas/Dockerfile b/docker-compose/http-json-schemas/Dockerfile
deleted file mode 100644
index 5d6aad5cfefdff1ded706a1bb669a70f86795b8f..0000000000000000000000000000000000000000
--- a/docker-compose/http-json-schemas/Dockerfile
+++ /dev/null
@@ -1,2 +0,0 @@
-FROM nginx
-COPY definitions /usr/share/nginx/html
\ No newline at end of file
diff --git a/docker-compose/http-json-schemas/README.md b/docker-compose/http-json-schemas/README.md
deleted file mode 100644
index ba91c82f03d07e024dc9dad77e9f5af2edb1c5a8..0000000000000000000000000000000000000000
--- a/docker-compose/http-json-schemas/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# HTTP JSON Schemas
-
-Service to serve JSON schema files over HTTP. Used by configuration manager device.
diff --git a/docker-compose/integration-test.yml b/docker-compose/integration-test.yml
index 8b2cc0a33f72865ee65c0cd66d5bc1af036fe8f2..d31670e157af42391c12f8e8b8393d608da18f75 100644
--- a/docker-compose/integration-test.yml
+++ b/docker-compose/integration-test.yml
@@ -20,6 +20,7 @@ services:
     container_name: integration-test
     networks:
       - control
+    dns: ${DNS}
     extra_hosts:
       - "host.docker.internal:host-gateway"
     volumes:
diff --git a/docker-compose/jupyter-lab.yml b/docker-compose/jupyter-lab.yml
index 4dfdbd852744acaa8510b640b6a4092cb87376a8..71f695dee8f44be7d251ad7c9997d6d2ba1477c9 100644
--- a/docker-compose/jupyter-lab.yml
+++ b/docker-compose/jupyter-lab.yml
@@ -17,9 +17,6 @@ services:
     image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/jupyter-lab:${TAG}
     build:
       context: jupyter-lab
-      args:
-        CONTAINER_EXECUTION_UID: ${CONTAINER_EXECUTION_UID}
-        SOURCE_IMAGE: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/tango-itango:${TANGO_ITANGO_VERSION}
     hostname: jupyter-lab
     container_name: jupyter-lab
     logging:
@@ -38,9 +35,5 @@ services:
       - TANGO_HOST=${TANGO_HOST}
     ports:
       - "8888:8888"
-    user: ${CONTAINER_EXECUTION_UID}
-    working_dir: /jupyter-notebooks
-    entrypoint:
-      - /opt/lofar/tango/bin/start-ds.sh
-      - jupyter lab --port=8888 --no-browser --ip=0.0.0.0 --allow-root --NotebookApp.token= --NotebookApp.password=
+    command: jupyter lab --port=8888 --no-browser --ip=0.0.0.0 --allow-root --NotebookApp.token= --NotebookApp.password= --Application.log_level=0
     restart: unless-stopped
diff --git a/docker-compose/jupyter-lab/Dockerfile b/docker-compose/jupyter-lab/Dockerfile
index 1ac48fd3656247bedcc573242e7c140eb4c2b05a..74c9bfdfce5228e0dba653b3bcd768d2c9572cf0 100644
--- a/docker-compose/jupyter-lab/Dockerfile
+++ b/docker-compose/jupyter-lab/Dockerfile
@@ -1,76 +1,47 @@
-ARG SOURCE_IMAGE
-FROM ${SOURCE_IMAGE}
-
-# UID if the user that this container will run under. This is needed to give directories
-# that are needed for temporary storage the proper owner and access rights.
-ARG CONTAINER_EXECUTION_UID=1000
-
-# Create new user with uid but only if uid not used
-RUN sudo adduser --disabled-password --system --uid ${CONTAINER_EXECUTION_UID} --no-create-home --home ${HOME} user || exit 0
-RUN sudo chown ${CONTAINER_EXECUTION_UID} -R ${HOME}
-
-# Add compiler to install python packages which come with C++ code
-RUN sudo apt-get update -y && sudo apt-get install -y g++ gcc python3-dev
-
-# Install git to install pip requirements from git
-RUN sudo apt-get update -y && sudo apt-get install -y git
-
-# Install dependencies of our scripts (bin/start-ds.sh)
-RUN sudo apt-get update -y && sudo apt-get install -y rsync
+# needs to stay v 3 until https://github.com/jupyterlab/jupyterlab-git/issues/1245
+FROM jupyter/scipy-notebook:lab-3.6.3
+
+USER root
+RUN --mount=type=cache,target=/var/cache/apt \
+    apt-get update
+RUN --mount=type=cache,target=/var/cache/apt \
+    apt-get upgrade -y
+RUN --mount=type=cache,target=/var/cache/apt \
+    apt-get install -y git
+
+USER ${NB_UID}
+RUN pip install lofar-station-client --index-url https://git.astron.nl/api/v4/projects/395/packages/pypi/simple
+RUN pip install tangostationcontrol --index-url https://git.astron.nl/api/v4/projects/71/packages/pypi/simple
+RUN mamba install --yes jupyterlab-git && \
+        mamba clean --all -f -y && \
+        fix-permissions "${CONDA_DIR}" && \
+        fix-permissions "/home/${NB_USER}"
+
+RUN jupyter server extension enable --py jupyterlab_git
 
 COPY requirements.txt ./
-RUN sudo pip3 install -r requirements.txt
+RUN pip install -r requirements.txt
+RUN rm requirements.txt
 
-# Install tzdata first and supress dialog to select timezone
-RUN echo 'tzdata tzdata/Areas select Europe' | sudo debconf-set-selections
-RUN echo 'tzdata tzdata/Zones/Europe select Amsterdam' | sudo debconf-set-selections
-RUN echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selections
-RUN sudo apt-get update && sudo apt-get install -y tzdata
-# Install some version of the casacore measures tables, to allow basic delay computation analysis in the notebooks
-RUN sudo apt-get update && sudo apt-get install -y casacore-data
+USER root
 
-# see https://github.com/jupyter/nbconvert/issues/1434
-RUN sudo bash -c "echo DEFAULT_ARGS += [\\\"--no-sandbox\\\"] >> /usr/local/lib/python3.10/dist-packages/pyppeteer/launcher.py"
-RUN sudo apt-get update -y && sudo apt-get install -y git gconf-service libasound2 libatk1.0-0 libatk-bridge2.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget libcairo-gobject2 libxinerama1 libgtk2.0-0 libpangoft2-1.0-0 libthai0 libpixman-1-0 libxcb-render0 libharfbuzz0b libdatrie1 libgraphite2-3 libgbm1
-
-# Allow Download as -> PDF via LaTeX
-RUN sudo apt-get update -y && sudo apt-get install -y texlive-xetex texlive-fonts-recommended texlive-latex-recommended cm-super
-
-# Configure jupyter_bokeh
-RUN sudo mkdir -p /usr/share/jupyter /usr/etc
-RUN sudo chmod a+rwx /usr/share/jupyter /usr/etc
-RUN sudo jupyter nbextension install --sys-prefix --symlink --py jupyter_bokeh
-RUN sudo jupyter nbextension enable jupyter_bokeh --py --sys-prefix
+COPY jupyter_notebook_config.py /home/${NB_USER}/.jupyter/
+RUN fix-permissions /home/${NB_USER}/.jupyter/
 
 # Install profiles for ipython & jupyter
 COPY ipython-profiles /opt/ipython-profiles/
-RUN sudo chown ${CONTAINER_EXECUTION_UID} -R /opt/ipython-profiles
+RUN fix-permissions /opt/ipython-profiles
 COPY jupyter-kernels /usr/local/share/jupyter/kernels/
 
-# Install patched jupyter executable
-COPY jupyter-notebook /usr/local/bin/jupyter-notebook
-
 # Install our custom configuration
-COPY user-settings /home/tango/.jupyter/lab/user-settings
+COPY user-settings /home/${NB_USER}/.jupyter/lab/user-settings
+RUN fix-permissions /home/${NB_USER}/.jupyter/lab
 
-# Add Tini. Tini operates as a process subreaper for jupyter. This prevents kernel crashes.
-ENV TINI_VERSION v0.6.0
-ENV JUPYTER_RUNTIME_DIR=/tmp
-ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/bin/tini
-RUN sudo chmod +x /usr/bin/tini
+USER ${NB_UID}
 
-# Needed to perform certain migration actions during startup
-RUN sudo mkdir -p /home/tango/.jupyter/lab/workspaces
-RUN sudo chmod -R 0777 /home/tango/.jupyter
-
-USER ${CONTAINER_EXECUTION_UID}
-# pyppeteer-install installs in the homedir, so run it as the user that will execute the notebook
-RUN pyppeteer-install
+WORKDIR "${HOME}"
 
 # Configure using git from Jupyter Lab. Since the web service is open, we can't know who is
 # committing. We thus tie commits to this machine instead of to the user.
 RUN git config --global user.email "jupyterlab-${HOSTNAME}@lofar.eu"
 RUN git config --global user.name "JupyterLab on ${HOSTNAME}"
-
-# Enable Jupyter lab
-ENV JUPYTER_ENABLE_LAB=yes
diff --git a/docker-compose/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py b/docker-compose/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py
index c76c3e2b4db62cbdc26bff0141be3ebb13511c5f..721c6fba31ed690109d2d17592785de2c95b625c 100644
--- a/docker-compose/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py
+++ b/docker-compose/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py
@@ -37,7 +37,7 @@ sst_l = OptionalDeviceProxy("STAT/SST/LBA")
 xst_l = OptionalDeviceProxy("STAT/XST/LBA")
 beamlet_l = OptionalDeviceProxy("STAT/Beamlet/LBA")
 digitalbeam_l = OptionalDeviceProxy("STAT/DigitalBeam/LBA")
-af_l = OptionalDeviceProxy("STAT/AFL/LBA")
+antennafield_l = af_l = OptionalDeviceProxy("STAT/AFL/LBA")
 
 sdpfirmware_h = OptionalDeviceProxy("STAT/SDPFirmware/HBA")
 sdp_h = OptionalDeviceProxy("STAT/SDP/HBA")
@@ -47,7 +47,7 @@ xst_h = OptionalDeviceProxy("STAT/XST/HBA")
 beamlet_h = OptionalDeviceProxy("STAT/Beamlet/HBA")
 digitalbeam_h = OptionalDeviceProxy("STAT/DigitalBeam/HBA")
 tilebeam_h = OptionalDeviceProxy("STAT/TileBeam/HBA")
-af_h = OptionalDeviceProxy("STAT/AFH/HBA")
+antennafield_h = af_h = OptionalDeviceProxy("STAT/AFH/HBA")
 
 sdpfirmware_h0 = OptionalDeviceProxy("STAT/SDPFirmware/HBA0")
 sdp_h0 = OptionalDeviceProxy("STAT/SDP/HBA0")
@@ -57,7 +57,7 @@ xst_h0 = OptionalDeviceProxy("STAT/XST/HBA0")
 beamlet_h0 = OptionalDeviceProxy("STAT/Beamlet/HBA0")
 digitalbeam_h0 = OptionalDeviceProxy("STAT/DigitalBeam/HBA0")
 tilebeam_h0 = OptionalDeviceProxy("STAT/TileBeam/HBA0")
-af_h0 = OptionalDeviceProxy("STAT/AFH/HBA0")
+antennafield_h0 = af_h0 = OptionalDeviceProxy("STAT/AFH/HBA0")
 
 sdpfirmware_h1 = OptionalDeviceProxy("STAT/SDPFirmware/HBA1")
 sdp_h1 = OptionalDeviceProxy("STAT/SDP/HBA1")
@@ -67,7 +67,7 @@ xst_h1 = OptionalDeviceProxy("STAT/XST/HBA1")
 beamlet_h1 = OptionalDeviceProxy("STAT/Beamlet/HBA1")
 digitalbeam_h1 = OptionalDeviceProxy("STAT/DigitalBeam/HBA1")
 tilebeam_h1 = OptionalDeviceProxy("STAT/TileBeam/HBA1")
-af_h1 = OptionalDeviceProxy("STAT/AFH/HBA1")
+antennafield_h1 = af_h1 = OptionalDeviceProxy("STAT/AFH/HBA1")
 
 stationmanager = OptionalDeviceProxy("STAT/StationManager/1")
 ccd = OptionalDeviceProxy("STAT/CCD/1")
@@ -96,7 +96,7 @@ devices = (
         xst_l,
         beamlet_l,
         digitalbeam_l,
-        antennafield_l,
+        af_l,
         sdpfirmware_h,
         sdp_h,
         bst_h,
@@ -105,7 +105,7 @@ devices = (
         beamlet_h,
         digitalbeam_h,
         tilebeam_h,
-        antennafield_h,
+        af_h,
         sdpfirmware_h0,
         sdp_h0,
         bst_h0,
@@ -114,7 +114,7 @@ devices = (
         beamlet_h0,
         digitalbeam_h0,
         tilebeam_h0,
-        antennafield_h0,
+        af_h0,
         sdpfirmware_h1,
         sdp_h1,
         bst_h1,
@@ -123,7 +123,7 @@ devices = (
         beamlet_h1,
         digitalbeam_h1,
         tilebeam_h1,
-        antennafield_h1,
+        af_h1,
     ]
     + apscts
     + apspus
diff --git a/docker-compose/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/02-stationcontrol.py b/docker-compose/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/02-stationcontrol.py
index d21ed1cf013d73b700cbc72e3d89ef9541efcacc..a9c046560add3bae6d2223810b72a6e7ecf97155 100644
--- a/docker-compose/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/02-stationcontrol.py
+++ b/docker-compose/jupyter-lab/ipython-profiles/stationcontrol-jupyter/startup/02-stationcontrol.py
@@ -1 +1,8 @@
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
+
+# noinspection PyUnresolvedReferences
+import lofar_station_client
+
+# noinspection PyUnresolvedReferences
 import tangostationcontrol
diff --git a/docker-compose/jupyter-lab/jupyter-notebook b/docker-compose/jupyter-lab/jupyter-notebook
deleted file mode 100755
index e594feb90f8ca52db07c83aed402ef76d43d42b5..0000000000000000000000000000000000000000
--- a/docker-compose/jupyter-lab/jupyter-notebook
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/python3
-# -*- coding: utf-8 -*-
-# An adjustment of the `jupyter-notebook' executable patched to:
-#  - log to logstash-loki
-#
-# We go straight for the notebook executable here, as the "jupyter" command
-# execvp's into the requested notebook subcommand, erasing all configuration
-# we set here.
-import re
-import sys
-
-from notebook.notebookapp import main
-
-from logstash_async.handler import AsynchronousLogstashHandler, LogstashFormatter
-import logging
-
-if __name__ == "__main__":
-    # log to the tcp_input of logstash in our logstash-loki container
-    handler = AsynchronousLogstashHandler(
-        "logstash", 5959, database_path="/tmp/pending_log_messages.db"
-    )
-
-    # add to logger of Jupyter traitlets Application. As that logger is configured not to propagate
-    # messages upward, we need to configure it directly.
-    logger = logging.getLogger("NotebookApp")
-    logger.addHandler(handler)
-    logger.setLevel(logging.DEBUG)
-
-    sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
-    sys.exit(main())
diff --git a/docker-compose/jupyter-lab/jupyter_notebook_config.py b/docker-compose/jupyter-lab/jupyter_notebook_config.py
new file mode 100644
index 0000000000000000000000000000000000000000..b927d210bde9d4c63a76c951bac2b4ffe88a3b0d
--- /dev/null
+++ b/docker-compose/jupyter-lab/jupyter_notebook_config.py
@@ -0,0 +1,6 @@
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
+
+NotebookApp.base_url = '/jupyter'
+NotebookApp.password_required = False
+NotebookApp.token = ''
diff --git a/docker-compose/jupyter-lab/requirements.txt b/docker-compose/jupyter-lab/requirements.txt
index c3e82e317c170d8c79d5e14efc8aff8894a87aef..871af8314c883ddea19ae220f430ee695b9fec78 100644
--- a/docker-compose/jupyter-lab/requirements.txt
+++ b/docker-compose/jupyter-lab/requirements.txt
@@ -1,14 +1,9 @@
 # Jupyter & enhancements
-ipython >=7.27.0,!=7.28.0 # BSD
-jupyter
-jupyterlab >=3,<4 # until https://github.com/jupyterlab/jupyterlab-git/issues/1245
-jupyterlab_h5web[full]  # MIT
-jupyterlab-git
-jupyterlab-skip-traceback
 ipykernel
 nbconvert
 notebook-as-pdf
 PyPDF2==2.12.1 # until https://github.com/betatim/notebook-as-pdf/issues/40 hits a notebook-as-pdf release
+jupyterlab-git
 
 # low-level access to station components
 opcua
@@ -23,15 +18,11 @@ etrs-itrs@git+https://github.com/brentjens/etrs-itrs # Apache 2
 
 # plotting
 matplotlib
-jupyter_bokeh
 jupyterplot
 
 # useful LOFAR software
 pabeam@git+https://git.astron.nl/mevius/pabeam # Apache2
-lofar-station-client@git+https://git.astron.nl/lofar2.0/lofar-station-client # Apache2
-attributewrapper@git+https://git.astron.nl/lofar2.0/attributewrapper # Apache2
 
 # user packages
 numpy
-scipy
 astropy
diff --git a/docker-compose/logstash.yml b/docker-compose/logstash.yml
deleted file mode 100644
index d44e0c52b4f6fca74cee7c9b00ceec6e9355da38..0000000000000000000000000000000000000000
--- a/docker-compose/logstash.yml
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
-#
-# Docker compose file that launches Logstash-output-loki
-#
-#
-
-version: '2.1'
-
-services:
-  logstash:
-    image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/logstash:${TAG}
-    build:
-      context: logstash
-      args:
-        SOURCE_IMAGE: grafana/logstash-output-loki:main
-    hostname: logstash
-    container_name: logstash
-    logging:
-      driver: "json-file"
-      options:
-        max-size: "100m"
-        max-file: "10"
-    healthcheck:
-      test: python -c "import socket; s = socket.socket(); s.settimeout(10); s.connect(('$HOSTNAME', 1514));"
-      interval: 1m
-      timeout: 30s
-      retries: 3
-      start_period: 30s
-    networks:
-      - control
-    ports:
-      - "5044:5044"     # logstash beats input
-      - "1514:1514/tcp" # logstash syslog input
-      - "1514:1514/udp" # logstash syslog input
-      - "5959:5959"     # logstash tcp json input
-      - "9600:9600"
-    restart: unless-stopped
-
-  logstash-exporter:
-    image: sequra/logstash_exporter
-    hostname: logstash-exporter
-    container_name: logstash-exporter
-    logging:
-      driver: "json-file"
-      options:
-        max-size: "100m"
-        max-file: "10"
-    networks:
-      - control
-    command: --logstash.endpoint="http://logstash:9600"
-    restart: unless-stopped
diff --git a/docker-compose/logstash/Dockerfile b/docker-compose/logstash/Dockerfile
deleted file mode 100644
index c455b2192eee00a7a293c8f39422ebb54b556f3e..0000000000000000000000000000000000000000
--- a/docker-compose/logstash/Dockerfile
+++ /dev/null
@@ -1,11 +0,0 @@
-ARG SOURCE_IMAGE
-FROM ${SOURCE_IMAGE}
-
-# Disable Elastic Search connection
-ENV ELASTIC_CONTAINER=false
-
-# Provide our logstash config
-COPY logstash.conf /home/logstash/
-COPY logstash.yml /usr/share/logstash/config/logstash.yml
-COPY logstash.conf /usr/share/logstash/pipeline/logstash.conf
-COPY patterns /usr/share/logstash/patterns
diff --git a/docker-compose/logstash/README.md b/docker-compose/logstash/README.md
deleted file mode 100644
index 156aaae85a210ee963a83baadc39ccdadbbef756..0000000000000000000000000000000000000000
--- a/docker-compose/logstash/README.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Logstash
-
-Grafana Loki has a Logstash output plugin called logstash-output-loki that enables shipping logs to a Loki instance
-
-## Usage and configuration
-
-To configure Logstash to forward logs to Loki, simply add the loki output to your Logstash configuration file as documented below:
-
-    output {
-        loki {
-            [url => "" | default = none | required=true]
-
-            [tenant_id => string | default = nil | required=false]
-
-            [message_field => string | default = "message" | required=false]
-            
-            [include_fields => array | default = [] | required=false]
-
-            [batch_wait => number | default = 1(s) | required=false]
-
-            [batch_size => number | default = 102400(bytes) | required=false]
-
-            [min_delay => number | default = 1(s) | required=false]
-
-            [max_delay => number | default = 300(s) | required=false]
-
-            [retries => number | default = 10 | required=false]
-
-            [username => string | default = nil | required=false]
-
-            [password => secret | default = nil | required=false]
-
-            [cert => path | default = nil | required=false]
-
-            [key => path | default = nil| required=false]
-
-            [ca_cert => path | default = nil | required=false]
-
-            [insecure_skip_verify => boolean | default = false | required=false]
-        }
-    }
diff --git a/docker-compose/logstash/logstash.conf b/docker-compose/logstash/logstash.conf
deleted file mode 100644
index 71afa03e94ef6427cdb06223a8f8ad1b452fa435..0000000000000000000000000000000000000000
--- a/docker-compose/logstash/logstash.conf
+++ /dev/null
@@ -1,220 +0,0 @@
-# For syntax, see https://www.elastic.co/guide/en/logstash/current/filter-plugins.html
-
-input {
-  beats {
-    id => "input_beats"
-    port => 5044
-    # TODO (L2SS-748) add SSL encryption
-  }
-}
-
-input {
-  syslog {
-    id => "input_syslog"
-    port => 1514
-  }
-}
-
-input {
-  tcp {
-    id => "input_tcp_json"
-    port => 5959
-    codec => json
-  }
-}
-
-filter {
-  if [type] == "syslog" {
-    grok {
-      id => "grok_syslog"
-      match => { "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{DATA:syslog_program}(?:\[%{POSINT:syslog_pid}\])?: %{GREEDYDATA:syslog_message}" }
-      add_field => [ "received_at", "%{@timestamp}" ]
-      add_field => [ "received_from", "%{host}" ]
-    }
-    syslog_pri { id => "syslog_pri" }
-    date {
-      id => "date_syslog"
-      match => [ "syslog_timestamp", "MMM  d HH:mm:ss", "MMM dd HH:mm:ss" ]
-    }
-  }
-}
-
-filter {
-  if [program] == "grafana" {
-    kv { id => "kv_grafana" }
-    mutate {
-      id => "mutate_grafana"
-      rename => {
-        "t" => "timestamp"
-        "lvl" => "level"
-      }
-      uppercase => [ "level" ]
-    }
-
-    date {
-      id => "date_grafana"
-      match => [ "timestamp", "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS'Z'", "ISO8601" ]
-    }
-  }
-}
-
-filter {
-  if [program] == "prometheus" {
-    kv { id => "kv_prometheus" }
-    mutate {
-      id => "mutate_prometheus"
-      rename => {
-        "ts" => "timestamp"
-      }
-      uppercase => [ "level" ]
-    }
-    date {
-      id => "date_prometheus"
-      match => [ "timestamp", "ISO8601" ]
-    }
-  }
-}
-
-filter {
-  if [program] == "tango-rest" {
-    grok {
-      id => "grok_tango-rest"
-      match => {
-        "message" => "%{TIMESTAMP_ISO8601:timestamp} %{WORD:level} %{GREEDYDATA:message}"
-      }
-      overwrite => [ "timestamp", "level", "message" ]
-    }
-    date {
-      id => "date_tango-rest"
-      match => [ "timestamp", "YYYY-MM-dd HH:mm:ss,SSS" ]
-      timezone => "UTC"
-    }
-  }
-}
-
-filter {
-  # parse tangodb output
-  if [program] == "tangodb" {
-    grok {
-      id => "grok_tangodb"
-      patterns_dir => [ "/usr/share/logstash/patterns/" ]
-      match => {
-        "message" => [
-          "%{TIMESTAMP_ISO8601_MARIADB:timestamp} .%{WORD:level}. %{GREEDYDATA:message}",
-          "%{TIMESTAMP_ISO8601_MARIADB:timestamp} %{NONNEGINT} .%{WORD:level}. %{GREEDYDATA:message}"
-        ]
-      }
-      "overwrite" => [ "timestamp", "level", "message" ]
-    }
-    mutate {
-      id => "mutate_tangodb"
-      gsub => [
-        "level", "Note", "Info"
-      ]
-      uppercase => [ "level" ]
-    }
-    date {
-      id => "date_tangodb"
-      match => [ "timestamp", "YYYY-MM-dd HH:mm:ssZZ", "YYYY-MM-dd HH:mm:ss", "YYYY-MM-dd  H:mm:ss"  ]
-      timezone => "UTC"
-    }
-  }
-}
-
-filter {
-  if [type] == "python-logstash" {
-    # don't archive debug messages
-    if [level] == "DEBUG" {
-      drop { id => "drop_debug_message" }
-    }
-
-    # strip path from program
-    grok {
-      id => "grok_python-logstash-removepath-program"
-      match => {
-        "program" => "(?<program>[^/]+)$"
-      }
-
-      overwrite => [ "program" ]
-      tag_on_failure => [] # be quiet if there are no matches
-    }
-
-    # strip path from source reference
-    grok {
-      id => "grok_python-logstash-removepath-source"
-      match => {
-        "[extra][path]" => "(?<[extra][path]>[^/]+)$"
-      }
-
-      overwrite => [ "[extra][path]" ]
-      tag_on_failure => [] # be quiet if there are no matches
-    }
-
-    # add source reference to each log entry
-    mutate {
-      id => "mutate_python-logstash-sourceref"
-      replace => { "message" => "%{message} [%{[extra][func_name]}() at %{[extra][path]}:%{[extra][line]}]" }
-    }
-
-    # promote some fields out of extra, if present
-    if [extra][device] {
-      mutate {
-        id => "mutate_python-logstash-device"
-        add_field => {
-          "device" => "%{[extra][device]}"
-        }
-      }
-    }
-
-    if [extra][stack_trace] {
-      mutate {
-        id => "mutate_python-logstash-stacktrace"
-        replace => { "message" => "%{message} %{[extra][stack_trace]}" }
-      }
-    }
-  }
-}
-
-# Throttle spam to max 1000/min per program/device.
-# See https://www.elastic.co/guide/en/logstash/current/plugins-filters-throttle.html
-filter {
-  throttle {
-    id => "throttler"
-    before_count => -1
-    after_count => 1000
-    period => 60
-    max_age => 180
-    key => "%{host}-%{program}-%{device}"
-    add_tag => "throttled"
-  }
-
-  if "throttled" in [tags] {
-    drop { id => "drop_throttled" }
-  }
-}
-
-output {
-  loki {
-    id => "output_loki"
-    url => "http://loki:3100/loki/api/v1/push"
-    message_field => "message"
-
-    # Every unique combination of field values creates a separate stream in loki.
-    # This overloads loki, so only send fields that are key and bank on messages
-    # being regex-ed when querying instead.
-    #
-    # See https://grafana.com/blog/2020/08/27/the-concise-guide-to-labels-in-loki/
-    #
-    # So avoid putting in fields that vary, such as:
-    #  * (parts of) timestamps,
-    #  * source file references (file, line, etc).
-    # Instead, f.e. put those in the message field with a replace filter.
-    #
-    # To inspect loki's streams, look at the content of /loki/chunks in the loki container:
-    #    docker exec -it loki ls /loki/chunks
-    include_fields => [ "host", "program", "level", "device", "tags" ]
-  }
-
-  # enable this to see all logs on logstash's stdout. to view, run: docker logs logstash
-  # stdout {}
-}
diff --git a/docker-compose/logstash/logstash.yml b/docker-compose/logstash/logstash.yml
deleted file mode 100644
index a5c9f331919bf8ff4f6f16aff14668cd48c9c345..0000000000000000000000000000000000000000
--- a/docker-compose/logstash/logstash.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-http.host: "0.0.0.0"
-#xpack.monitoring.elasticsearch.hosts: [ "http://loki:3100" ]
-
-pipeline.ecs_compatibility: disabled
diff --git a/docker-compose/logstash/patterns/mariadb.txt b/docker-compose/logstash/patterns/mariadb.txt
deleted file mode 100644
index 42d981acb5fa74d858ea9cb83ad951c87248018a..0000000000000000000000000000000000000000
--- a/docker-compose/logstash/patterns/mariadb.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# MariaDB uses ' 2' instead of '02' for hours. The following format accepts both.
-TIMESTAMP_ISO8601_MARIADB %{YEAR}-%{MONTHNUM}-%{MONTHDAY}[T ] ?%{HOUR}:?%{MINUTE}(?::?%{SECOND})?%{ISO8601_TIMEZONE}?
diff --git a/docker-compose/object-storage.yml b/docker-compose/object-storage.yml
index 03e3e89f0381aaab933228fc0c539d6dd89d34d8..434faf6588729f46c662bac42bbb9a77ca6f3184 100644
--- a/docker-compose/object-storage.yml
+++ b/docker-compose/object-storage.yml
@@ -9,47 +9,19 @@
 version: '2.1'
 
 services:
-  object-storage:
-    image: minio/minio:${MINIO_VERSION}
-    hostname: object-storage
-    container_name: object-storage
-    logging:
-      driver: "json-file"
-      options:
-        max-size: "100m"
-        max-file: "10"
-    networks:
-      - control
-    volumes:
-      - object-storage:/data:rw
-    environment:
-      - MINIO_ROOT_USER=${MINIO_ROOT_USER}
-      - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
-    ports:
-      - "9000:9000"
-      - "9001:9001"
-    command: server --console-address ":9001" /data
-    restart: unless-stopped
-
+  # the object storage is started by nomad/jumppad. This only populates the data for the integration tests
   init-object-storage:
     image: minio/mc:${MINIO_CLIENT_VERSION}
     networks:
       - control
-    depends_on:
-      - object-storage
+    dns: ${DNS}
     volumes:
       - ..:/opt/lofar/tango:rw
     entrypoint: ''
     command: >
-      sh -c "until [ \"$(curl -s -w '%{http_code}' -o /dev/null http://object-storage:9000/minio/health/live)\" -eq \"200\" ]
-             do
-               sleep 1
-             done
-             mc alias set object-storage http://object-storage:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD
+      sh -c "mc alias set object-storage http://s3.service.consul:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD
              mc mb --with-versioning object-storage/caltables
              mc cp --recursive /opt/lofar/tango/docker-compose/object-storage/caltables/ object-storage/caltables/
              date +'%F %T'
              echo 'Initialisation completed'"
 
-volumes:
-  object-storage:
diff --git a/docker-compose/prometheus/prometheus.yml b/docker-compose/prometheus/prometheus.yml
index 673021bc8d828e31e715612536c7fb9a0b59fc7e..09a3067639298ba0f2022b8d99331c0b88991353 100644
--- a/docker-compose/prometheus/prometheus.yml
+++ b/docker-compose/prometheus/prometheus.yml
@@ -6,46 +6,41 @@ global:
 scrape_configs:
   - job_name: tango
     static_configs:
-      - targets: ["tango-prometheus-exporter:8000"]
+      - targets: [ "tango-prometheus-exporter:8000" ]
         labels:
           "host": "localhost"
   - job_name: tango-fast
     scrape_interval: 1s
     static_configs:
-      - targets: ["tango-prometheus-fast-exporter:8000"]
+      - targets: [ "tango-prometheus-fast-exporter:8000" ]
         labels:
           "host": "localhost"
   - job_name: tango-slow
     scrape_interval: 60s
     scrape_timeout: 30s
     static_configs:
-      - targets: ["tango-prometheus-slow-exporter:8000"]
+      - targets: [ "tango-prometheus-slow-exporter:8000" ]
         labels:
           "host": "localhost"
   - job_name: host
     scrape_interval: 60s
     static_configs:
-      - targets: ["host.docker.internal:9100"]
+      - targets: [ "host.docker.internal:9100" ]
         labels:
           "host": "localhost"
   # scrape local services
   - job_name: prometheus
     static_configs:
-      - targets: ["localhost:9090"]
+      - targets: [ "localhost:9090" ]
         labels:
           "host": "localhost"
   - job_name: grafana
     static_configs:
-      - targets: ["grafana:3000"]
-        labels:
-          "host": "localhost"
-  - job_name: logstash
-    static_configs:
-      - targets: ["logstash-exporter:9198"]
+      - targets: [ "grafana:3000" ]
         labels:
           "host": "localhost"
   - job_name: loki
     static_configs:
-      - targets: ["loki:3100"]
+      - targets: [ "loki:3100" ]
         labels:
           "host": "localhost"
diff --git a/docker-compose/tango-prometheus-exporter.yml b/docker-compose/tango-prometheus-exporter.yml
index 14b66f988d36fd6377e894e02108c5ad37573b8d..69e0df046b7ed01625e28376c9516d1da840d146 100644
--- a/docker-compose/tango-prometheus-exporter.yml
+++ b/docker-compose/tango-prometheus-exporter.yml
@@ -28,8 +28,6 @@ services:
       - TANGO_HOST=${TANGO_HOST}
     ports:
       - "8000:8000"
-    depends_on:
-      - databaseds
     restart: unless-stopped
 
   tango-prometheus-fast-exporter:
@@ -54,8 +52,6 @@ services:
       - TANGO_HOST=${TANGO_HOST}
     ports:
       - "8001:8000"
-    depends_on:
-      - databaseds
     restart: unless-stopped
 
   tango-prometheus-slow-exporter:
@@ -80,6 +76,4 @@ services:
       - TANGO_HOST=${TANGO_HOST}
     ports:
       - "8002:8000"
-    depends_on:
-      - databaseds
     restart: unless-stopped
diff --git a/docker-compose/tango-prometheus-exporter/Dockerfile b/docker-compose/tango-prometheus-exporter/Dockerfile
index 372798189e1410f74c676d70b2040f5c23e141ac..6cf1017c0de447d6df4f2d8c9ae3efa631eb7466 100644
--- a/docker-compose/tango-prometheus-exporter/Dockerfile
+++ b/docker-compose/tango-prometheus-exporter/Dockerfile
@@ -10,7 +10,7 @@ USER tango
 COPY code/pip-requirements.txt /tmp/
 RUN pip install -r /tmp/pip-requirements.txt
 
-ADD code /code
+COPY code /code
 COPY lofar2-policy.json /code/
 COPY lofar2-fast-policy.json /code/
 COPY lofar2-slow-policy.json /code/
diff --git a/docker-compose/tango-prometheus-exporter/code/pip-requirements.txt b/docker-compose/tango-prometheus-exporter/code/pip-requirements.txt
index 771b4421928ac8ddc6ac2c328b4827b238b94c06..a0753df38ffe99b79ddc1519c348f987dfa3b4fe 100644
--- a/docker-compose/tango-prometheus-exporter/code/pip-requirements.txt
+++ b/docker-compose/tango-prometheus-exporter/code/pip-requirements.txt
@@ -1,2 +1 @@
 prometheus_client
-python-logstash-async
diff --git a/docker-compose/tango-prometheus-exporter/code/tango-prometheus-client.py b/docker-compose/tango-prometheus-exporter/code/tango-prometheus-client.py
index 751cafde6dc7ca78931177faf6aff3543d0ab04d..fdb80e699e4bd6bbb2b453ac16ebdfae4bcf0baa 100644
--- a/docker-compose/tango-prometheus-exporter/code/tango-prometheus-client.py
+++ b/docker-compose/tango-prometheus-exporter/code/tango-prometheus-client.py
@@ -1,7 +1,15 @@
-import time
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
+
 import argparse
-from prometheus_client.core import GaugeMetricFamily, REGISTRY, CounterMetricFamily
+import fnmatch
+import json
+import logging
+import time
+from functools import lru_cache
+
 from prometheus_client import start_http_server
+from prometheus_client.core import GaugeMetricFamily, REGISTRY
 from tango import (
     Database,
     DeviceProxy,
@@ -10,23 +18,10 @@ from tango import (
     DevState,
     DevFailed,
 )
-import logging
-import json
-import fnmatch
-from functools import lru_cache
-from logstash_async.handler import AsynchronousLogstashHandler, LogstashFormatter
 
 logger = logging.getLogger()
 logging.basicConfig(format="%(asctime)s %(levelname)s %(message)s", level=logging.DEBUG)
 
-# log to memory only, with a limit of 600 seconds. this container is not important enough to keep
-# all logs around for, and as a scraper must be robust against overloading on all sides
-handler = AsynchronousLogstashHandler(
-    "logstash", 5959, database_path=None, event_ttl=600
-)
-handler.setLevel(logging.INFO)
-logger.addHandler(handler)
-
 # time to wait before reconnecting after encountering connection issues
 reconnect_timeout_time = 1.0
 
@@ -193,12 +188,12 @@ class CustomCollector(object):
                 the array as [x][y] instead of [y][x]."""
 
                 new_metric = self._to_metric(
-                    dev,
-                    attr_info,
-                    y,
-                    x,
-                    y * attr_value.dim_x + x,
-                    attr_value.value[y][x],
+                        dev,
+                        attr_info,
+                        y,
+                        x,
+                        y * attr_value.dim_x + x,
+                        attr_value.value[y][x],
                 )
                 metrics.append(new_metric) if new_metric else None
 
@@ -247,7 +242,7 @@ class CustomCollector(object):
             DevState.DISABLE,
         ]:
             logger.warning(
-                f"Error processing device {device_name}: it is in state {dev.state()}"
+                    f"Error processing device {device_name}: it is in state {dev.state()}"
             )
 
             # at least log state & status
@@ -268,7 +263,7 @@ class CustomCollector(object):
             except DevFailed as e:
                 reason = e.args[0].desc.replace("\n", " ")
                 logger.warning(
-                    f"Error processing device {device_name} attribute {attr_name}: {reason}"
+                        f"Error processing device {device_name} attribute {attr_name}: {reason}"
                 )
 
                 if "The last connection request was done less than" in e.args[0].desc:
@@ -276,7 +271,7 @@ class CustomCollector(object):
                     break
             except Exception as e:
                 logger.exception(
-                    f"Error processing device {device_name} attribute {attr_name}"
+                        f"Error processing device {device_name} attribute {attr_name}"
                 )
 
         return metrics
@@ -288,12 +283,14 @@ class CustomCollector(object):
         scrape_begin = time.time()
 
         attribute_metrics = GaugeMetricFamily(
-            "device_attribute",
-            "Device attribute value",
-            labels=["station", "device", "name", "str_value", "type", "x", "y", "idx"],
+                "device_attribute",
+                "Device attribute value",
+                labels=["station", "device", "name", "str_value", "type", "x", "y",
+                        "idx"],
         )
         scraping_metrics = GaugeMetricFamily(
-            "device_scraping", "Device scraping duration", labels=["station", "device"]
+                "device_scraping", "Device scraping duration",
+                labels=["station", "device"]
         )
 
         for device_name in self.policy.devices():
@@ -314,11 +311,11 @@ class CustomCollector(object):
                 dev_scrape_end = time.time()
 
             logger.debug(
-                f"Done processing device {device_name}. Took {dev_scrape_end - dev_scrape_begin} seconds."
+                    f"Done processing device {device_name}. Took {dev_scrape_end - dev_scrape_begin} seconds."
             )
 
             scraping_metrics.add_metric(
-                [self.station, device_name], dev_scrape_end - dev_scrape_begin
+                    [self.station, device_name], dev_scrape_end - dev_scrape_begin
             )
 
         scrape_end = time.time()
@@ -335,23 +332,23 @@ if __name__ == "__main__":
 
     parser = argparse.ArgumentParser()
     parser.add_argument(
-        "-c", "--config", type=str, required=True, help="configuration file"
+            "-c", "--config", type=str, required=True, help="configuration file"
     )
     parser.add_argument(
-        "-t",
-        "--timeout",
-        type=int,
-        required=False,
-        default=250,
-        help="device proxy timeout (ms)",
+            "-t",
+            "--timeout",
+            type=int,
+            required=False,
+            default=250,
+            help="device proxy timeout (ms)",
     )
     parser.add_argument(
-        "-p",
-        "--port",
-        type=int,
-        required=False,
-        default=8000,
-        help="HTTP server port to open",
+            "-p",
+            "--port",
+            type=int,
+            required=False,
+            default=8000,
+            help="HTTP server port to open",
     )
     args = parser.parse_args()
 
diff --git a/docker-compose/tango.yml b/docker-compose/tango.yml
index 9de7d9e1dc6d594b21ea967266c383886e82970e..c4a2827d97d6597df9f0a55b38c90c6dfe361cc4 100644
--- a/docker-compose/tango.yml
+++ b/docker-compose/tango.yml
@@ -75,6 +75,7 @@ services:
     restart: unless-stopped
 
   dsconfig:
+    image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/dsconfig:${TAG}
     build:
       context: dsconfig
       args:
@@ -83,8 +84,6 @@ services:
     container_name: dsconfig
     networks:
       - control
-    depends_on:
-      - databaseds
     environment:
       - TANGO_HOST=${TANGO_HOST}
     command: >
diff --git a/infra/dev/jobs/.keep b/infra/dev/jobs/.keep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/infra/dev/jobs/station/.keep b/infra/dev/jobs/station/.keep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/infra/dev/main.hcl b/infra/dev/main.hcl
new file mode 100644
index 0000000000000000000000000000000000000000..8122fe07273c96ea36fac8e4bdd53c0f2b5cbb28
--- /dev/null
+++ b/infra/dev/main.hcl
@@ -0,0 +1,32 @@
+variable "host_volume" {
+  default = "dev_nomad_station"
+}
+
+module "nomad" {
+  source    = "./nomad"
+  variables = {
+    host_volume = "${variable.host_volume}"
+  }
+}
+
+resource "nomad_job" "tango" {
+  cluster = module.nomad.output.nomad_cluster
+
+  paths = ["./jobs/station/tango.nomad"]
+
+  health_check {
+    timeout = "300s"
+    jobs    = ["tango"]
+  }
+}
+
+resource "nomad_job" "object-storage" {
+  cluster = module.nomad.output.nomad_cluster
+
+  paths = ["./jobs/station/object-storage.nomad"]
+
+  health_check {
+    timeout = "300s"
+    jobs    = ["object-storage"]
+  }
+}
diff --git a/infra/dev/nomad/config/nomad/client.hcl b/infra/dev/nomad/config/nomad/client.hcl
new file mode 100644
index 0000000000000000000000000000000000000000..5312a79f7aef599628103a691df44ae26f606b17
--- /dev/null
+++ b/infra/dev/nomad/config/nomad/client.hcl
@@ -0,0 +1,55 @@
+plugin "docker" {
+  config {
+    allow_privileged = true
+    volumes {
+      enabled      = true
+      selinuxlabel = "z"
+    }
+  }
+}
+
+client {
+  host_volume "monitoring-postgresql-data" {
+    path = "/localdata/volumes/monitoring-postgresql-data"
+  }
+
+  host_volume "monitoring-loki-data" {
+    path = "/localdata/volumes/monitoring-loki-data"
+  }
+
+  host_volume "monitoring-prometheus-data" {
+    path = "/localdata/volumes/monitoring-prometheus-data"
+  }
+
+  host_volume "tango-database" {
+    path = "/localdata/volumes/tango-database"
+  }
+
+  host_volume "jupyter-notebooks" {
+    path = "/localdata/volumes/jupyter-notebooks"
+  }
+
+  host_volume "object-storage-data" {
+    path = "/localdata/volumes/object-storage-data"
+  }
+
+  options = {
+    "driver.allowlist" = "docker,exec"
+  }
+}
+consul {
+  address              = "localhost:8500"
+  server_service_name  = "nomad"
+  client_service_name  = "nomad-client"
+  auto_advertise       = true
+  server_auto_join     = true
+  client_auto_join     = true
+  checks_use_advertise = true
+}
+
+advertise {
+
+  http = "{{ GetInterfaceIP \"eth0\" }}"
+  rpc  = "{{ GetInterfaceIP \"eth0\" }}"
+  serf = "{{ GetInterfaceIP \"eth0\" }}"
+}
diff --git a/infra/dev/nomad/config/nomad/consul.hcl b/infra/dev/nomad/config/nomad/consul.hcl
new file mode 100644
index 0000000000000000000000000000000000000000..512ae3b7188bf969d7e1541f2836da3fc3e7628d
--- /dev/null
+++ b/infra/dev/nomad/config/nomad/consul.hcl
@@ -0,0 +1,21 @@
+data_dir  = "/tmp/"
+log_level = "trace"
+
+server     = false
+datacenter = "dev-stat"
+
+bind_addr      = "0.0.0.0"
+advertise_addr = "{{GetInterfaceIP \"eth0\"}}"
+
+ports {
+  grpc     = 8502
+  grpc_tls = 8503
+}
+
+connect {
+  enabled = true
+}
+
+retry_join              = ["192.168.123.100"]
+skip_leave_on_interrupt = true
+leave_on_terminate      = false
diff --git a/infra/dev/nomad/nomad.hcl b/infra/dev/nomad/nomad.hcl
new file mode 100644
index 0000000000000000000000000000000000000000..897d6d08dfc8ecf216690ae3b5596cbdf478128f
--- /dev/null
+++ b/infra/dev/nomad/nomad.hcl
@@ -0,0 +1,105 @@
+resource "network" "station" {
+  subnet = "192.168.123.0/24"
+}
+
+resource "template" "consul_config" {
+
+  source = <<-EOF
+  data_dir  = "/consul/data/"
+  log_level = "trace"
+
+
+  datacenter = "dev-stat"
+
+  server = true
+
+  bootstrap_expect = 1
+  ui = true
+
+  bind_addr = "0.0.0.0"
+  client_addr = "127.0.0.1 {{GetInterfaceIP \"eth0\"}}"
+  advertise_addr = "{{GetInterfaceIP \"eth0\"}}"
+
+  ports {
+    grpc = 8502
+    grpc_tls = 8503
+    dns = 53
+  }
+  connect {
+    enabled = true
+  }
+
+  auto_encrypt {
+    allow_tls = true
+  }
+  skip_leave_on_interrupt = true
+  leave_on_terminate = false
+  recursors  = ["127.0.0.11"]
+  EOF
+
+  destination = "./tmp/consul.hcl"
+}
+
+
+resource "container" "consul" {
+  depends_on = ["resource.template.consul_config"]
+  network {
+    id         = resource.network.station.id
+    ip_address = "192.168.123.100"
+  }
+
+  image {
+    name = "hashicorp/consul:latest"
+  }
+
+  command = [
+    "consul",
+    "agent",
+    "-config-file=/consul/config/config.hcl"
+  ]
+
+  environment = {
+    CONSUL_HTTP_ADDR = "http://localhost:8500"
+  }
+
+  volume {
+    source      = resource.template.consul_config.destination
+    destination = "/consul/config/config.hcl"
+    type        = "bind"
+  }
+
+  port {
+    local  = 8500
+    remote = 8500
+    host   = 18500
+  }
+
+  port {
+    local    = 53
+    host     = 8600
+    remote   = 53
+    protocol = "udp"
+  }
+  privileged = false
+}
+
+resource "nomad_cluster" "station" {
+  depends_on    = ["resource.container.consul"]
+  client_nodes  = 1
+  client_config = "./config/nomad/client.hcl"
+  consul_config = "./config/nomad/consul.hcl"
+  datacenter    = "stat"
+
+  network {
+    id = resource.network.station.id
+  }
+  environment = {
+    NO_PROXY = "git.astron.nl:5000"
+  }
+
+  volume {
+    source      = "${variable.host_volume}"
+    destination = "/localdata"
+    type        = "volume"
+  }
+}
diff --git a/infra/dev/nomad/tmp/.keep b/infra/dev/nomad/tmp/.keep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/infra/dev/nomad/variables.hcl b/infra/dev/nomad/variables.hcl
new file mode 100644
index 0000000000000000000000000000000000000000..6deffdedc1a4c8782220efd5f4b60e1ae628619b
--- /dev/null
+++ b/infra/dev/nomad/variables.hcl
@@ -0,0 +1,7 @@
+variable "host_volume" {
+  default = "dev_nomad_station"
+}
+
+output "nomad_cluster" {
+  value = resource.nomad_cluster.station
+}
diff --git a/infra/dev/services.hcl b/infra/dev/services.hcl
new file mode 100644
index 0000000000000000000000000000000000000000..fc7d8e0b9ff44c57c38bf1ecaf11f3030b736bea
--- /dev/null
+++ b/infra/dev/services.hcl
@@ -0,0 +1,21 @@
+variable "host_volume" {
+  default = "dev_nomad_station"
+}
+
+module "nomad" {
+  source    = "./nomad"
+  variables = {
+    host_volume = "${variable.host_volume}"
+  }
+}
+
+resource "nomad_job" "monitoring" {
+  cluster = module.nomad.output.nomad_cluster
+
+  paths = ["./jobs/station/monitoring.nomad"]
+
+  health_check {
+    timeout = "300s"
+    jobs    = ["monitoring"]
+  }
+}
diff --git a/infra/env.yaml b/infra/env.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..5d7d6fcac7736c8d48d46e4ae400b2b6caaf5a51
--- /dev/null
+++ b/infra/env.yaml
@@ -0,0 +1,72 @@
+tango:
+  db:
+    version: 11.0.2
+  databaseds:
+    version: 5.22.0
+  rest:
+    version: 1.14.8
+
+monitoring:
+  db:
+    version: 15.4
+
+object_storage:
+  minio:
+    version: RELEASE.2023-09-23T03-47-50Z
+  user:
+    name: minioadmin
+    pass: minioadmin
+
+sdptr:
+  lba:
+    port: 4840
+    first_gn: 0
+    fpgas: 16
+  hba0:
+    port: 4842
+    first_gn: 16
+    fpgas: 4
+  hba1:
+    port: 4844
+    first_gn: 20
+    fpgas: 4
+
+devices:
+  - class: AFH
+  - class: AFL
+  - class: APS
+  - class: APSCT
+  - class: APSPU
+  - class: Beamlet
+  - class: BST
+    ports:
+      lba: 5003
+      hba0: 5013
+      hba1: 5023
+  - class: Calibration
+  - class: CCD
+  - class: Configuration
+  - class: DigitalBeam
+  - class: Docker
+  - class: EC
+  - class: ObservationControl
+  - class: PCON
+  - class: PSOC
+  - class: RECVH
+  - class: RECVL
+  - class: SDP
+  - class: SDPFirmware
+  - class: SST
+    ports:
+      lba: 5001
+      hba0: 5011
+      hba1: 5021
+  - class: StationManager
+  - class: TemperatureManager
+  - class: TileBeam
+  - class: UNB2
+  - class: XST
+    ports:
+      lba: 5002
+      hba0: 5012
+      hba1: 5022
diff --git a/infra/imds/meta-data b/infra/imds/meta-data
deleted file mode 100644
index 88f01c27494507b5bfc4bc505afc40df5eb1a0a4..0000000000000000000000000000000000000000
--- a/infra/imds/meta-data
+++ /dev/null
@@ -1,3 +0,0 @@
-instance-id: omeid/somehostname
-local-hostname: ds001c
-region: ds001
diff --git a/infra/imds/user-data b/infra/imds/user-data
deleted file mode 100644
index 38aebe14eec4813ecc0f090ee7c94db72bab1083..0000000000000000000000000000000000000000
--- a/infra/imds/user-data
+++ /dev/null
@@ -1,205 +0,0 @@
-## template: jinja
-#cloud-config
-system_info:
-  default_user:
-    name: ghost
-    home: /home/ghost
-
-password: password
-chpasswd:
-  expire: False
-
-write_files:
-- content: |
-    datacenter = "{{ds.meta_data.region}}"
-    data_dir  = "/localdata/consul"
-    node_name = "lcu"
-    server = true
-    bootstrap = true
-    bind_addr = "0.0.0.0"
-
-    # ui
-    ui_config{
-      enabled = true
-    }
-
-
-    ports {
-      grpc = 8502
-    }
-
-    connect {
-      enabled = true
-    }
-  owner: consul:consul
-  path: /etc/consul.d/consul.hcl
-  defer: true
-- content: |
-    bind_addr = "0.0.0.0"
-    name      = "lcu"
-    region    = "{{ds.meta_data.region}}"
-    data_dir  = "/localdata/nomad/data"
-    datacenter = "stat"
-
-    server {
-      # license_path is required for Nomad Enterprise as of Nomad v1.1.1+
-      #license_path = "/etc/nomad.d/license.hclic"
-      enabled          = true
-      bootstrap_expect = 1
-    }
-
-    client {
-      enabled = true
-      servers = ["127.0.0.1"]
-      cni_path = "/localdata/cni/bin"
-
-      host_volume "monitoring-postgresql-data" {
-        path = "/localdata/volumes/monitoring-postgresql-data"
-      }
-
-      host_volume "monitoring-loki-data" {
-        path = "/localdata/volumes/monitoring-loki-data"
-      }
-
-      host_volume "monitoring-prometheus-data" {
-        path = "/localdata/volumes/monitoring-prometheus-data"
-      }
-    }
-
-    consul {
-      address             = "127.0.0.1:8500"
-      server_service_name = "nomad"
-      client_service_name = "nomad-client"
-      auto_advertise      = true
-      server_auto_join    = true
-      client_auto_join    = true
-    }
-
-    plugin "docker" {
-      config {
-        volumes {
-          enabled = true
-        }
-        allow_caps = ["all"]
-      }
-    }
-  path: /etc/nomad.d/nomad.hcl
-  defer: true
-
-- content: |
-    net.bridge.bridge-nf-call-arptables = 1
-    net.bridge.bridge-nf-call-ip6tables = 1
-    net.bridge.bridge-nf-call-iptables = 1
-  path: /etc/sysctl.d/10-bridge.conf
-- content: |
-    deb [signed-by=/usr/share/keyrings/docker.asc] https://apt.releases.hashicorp.com $RELEASE main
-  path: /etc/apt/sources.list.d/hashicorp.list
-- path: /usr/share/keyrings/hashicorp.asc
-  owner: root:root
-  permissions: '0644'
-  content: |
-    -----BEGIN PGP PUBLIC KEY BLOCK-----
-
-    mQINBGO9u+MBEADmE9i8rpt8xhRqxbzlBG06z3qe+e1DI+SyjscyVVRcGDrEfo+J
-    W5UWw0+afey7HFkaKqKqOHVVGSjmh6HO3MskxcpRm/pxRzfni/OcBBuJU2DcGXnG
-    nuRZ+ltqBncOuONi6Wf00McTWviLKHRrP6oWwWww7sYF/RbZp5xGmMJ2vnsNhtp3
-    8LIMOmY2xv9LeKMh++WcxQDpIeRohmSJyknbjJ0MNlhnezTIPajrs1laLh/IVKVz
-    7/Z73UWX+rWI/5g+6yBSEtj368N7iyq+hUvQ/bL00eyg1Gs8nE1xiCmRHdNjMBLX
-    lHi0V9fYgg3KVGo6Hi/Is2gUtmip4ZPnThVmB5fD5LzS7Y5joYVjHpwUtMD0V3s1
-    HiHAUbTH+OY2JqxZDO9iW8Gl0rCLkfaFDBS2EVLPjo/kq9Sn7vfp2WHffWs1fzeB
-    HI6iUl2AjCCotK61nyMR33rNuNcbPbp+17NkDEy80YPDRbABdgb+hQe0o8htEB2t
-    CDA3Ev9t2g9IC3VD/jgncCRnPtKP3vhEhlhMo3fUCnJI7XETgbuGntLRHhmGJpTj
-    ydudopoMWZAU/H9KxJvwlVXiNoBYFvdoxhV7/N+OBQDLMevB8XtPXNQ8ZOEHl22G
-    hbL8I1c2SqjEPCa27OIccXwNY+s0A41BseBr44dmu9GoQVhI7TsetpR+qwARAQAB
-    tFFIYXNoaUNvcnAgU2VjdXJpdHkgKEhhc2hpQ29ycCBQYWNrYWdlIFNpZ25pbmcp
-    IDxzZWN1cml0eStwYWNrYWdpbmdAaGFzaGljb3JwLmNvbT6JAlQEEwEIAD4CGwMF
-    CwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQR5iuxlTlwVQoyOQu6qFvy8piHnAQUC
-    Y728PQUJCWYB2gAKCRCqFvy8piHnAd16EADeBtTgkdVEvct40TH/9HKkR/Lc/ohM
-    rer6FFHdKmceJ6Ma8/Qm4nCO5C7c4+EPjsUXdhK5w8DSdC5VbKLJDY1EnDlmU5B1
-    wSFkGoYKoB8lUn30E77E33MTu2kfrSuF605vetq269CyBwIJV7oNN6311dW8iQ6z
-    IytTtlJbVr4YZ7Vst40/uR4myumk9bVBGEd6JhFAPmr/um+BZFhRf9/8xtOryOyB
-    GF2d+bc9IoAugpxwv0IowHEqkI4RpK2U9hvxG80sTOcmerOuFbmNyPwnEgtJ6CM1
-    bc8WAmObJiQcRSLbcgF+a7+2wqrUbCqRE7QoS2wjd1HpUVPmSdJN925c2uaua2A4
-    QCbTEg8kV2HiP0HGXypVNhZJt5ouo0YgR6BSbMlsMHniDQaSIP1LgmEz5xD4UAxO
-    Y/GRR3LWojGzVzBb0T98jpDgPtOu/NpKx3jhSpE2U9h/VRDiL/Pf7gvEIxPUTKuV
-    5D8VqAiXovlk4wSH13Q05d9dIAjuinSlxb4DVr8IL0lmx9DyHehticmJVooHDyJl
-    HoA2q2tFnlBBAFbN92662q8Pqi9HbljVRTD1vUjof6ohaoM+5K1C043dmcwZZMTc
-    7gV1rbCuxh69rILpjwM1stqgI1ONUIkurKVGZHM6N2AatNKqtBRdGEroQo1aL4+4
-    u+DKFrMxOqa5b7kCDQRjvbwTARAA0ut7iKLj9sOcp5kRG/5V+T0Ak2k2GSus7w8e
-    kFh468SVCNUgLJpLzc5hBiXACQX6PEnyhLZa8RAG+ehBfPt03GbxW6cK9nx7HRFQ
-    GA79H5B4AP3XdEdT1gIL2eaHdQot0mpF2b07GNfADgj99MhpxMCtTdVbBqHY8YEQ
-    Uq7+E9UCNNs45w5ddq07EDk+o6C3xdJ42fvS2x44uNH6Z6sdApPXLrybeun74C1Z
-    Oo4Ypre4+xkcw2q2WIhy0Qzeuw+9tn4CYjrhw/+fvvPGUAhtYlFGF6bSebmyua8Q
-    MTKhwqHqwJxpjftM3ARdgFkhlH1H+PcmpnVutgTNKGcy+9b/lu/Rjq/47JZ+5VkK
-    ZtYT/zO1oW5zRklHvB6R/OcSlXGdC0mfReIBcNvuNlLhNcBA9frNdOk3hpJgYDzg
-    f8Ykkc+4z8SZ9gA3g0JmDHY1X3SnSadSPyMas3zH5W+16rq9E+MZztR0RWwmpDtg
-    Ff1XGMmvc+FVEB8dRLKFWSt/E1eIhsK2CRnaR8uotKW/A/gosao0E3mnIygcyLB4
-    fnOM3mnTF3CcRumxJvnTEmSDcoKSOpv0xbFgQkRAnVSn/gHkcbVw/ZnvZbXvvseh
-    7dstp2ljCs0queKU+Zo22TCzZqXX/AINs/j9Ll67NyIJev445l3+0TWB0kego5Fi
-    UVuSWkMAEQEAAYkEcgQYAQgAJhYhBHmK7GVOXBVCjI5C7qoW/LymIecBBQJjvbwT
-    AhsCBQkJZgGAAkAJEKoW/LymIecBwXQgBBkBCAAdFiEE6wr14plJaVlvmYc+cG5m
-    g2nAhekFAmO9vBMACgkQcG5mg2nAhenPURAAimI0EBZbqpyHpwpbeYq3Pygg1bdo
-    IlBQUVoutaN1lR7kqGXwYH+BP6G40x79LwVy/fWV8gO7cDX6D1yeKLNbhnJHPBus
-    FJDmzDPbjTlyWlDqJoWMiPqfAOc1A1cHodsUJDUlA01j1rPTho0S9iALX5R50Wa9
-    sIenpfe7RVunDwW5gw6y8me7ncl5trD0LM2HURw6nYnLrxePiTAF1MF90jrAhJDV
-    +krYqd6IFq5RHKveRtCuTvpL7DlgVCtntmbXLbVC/Fbv6w1xY3A7rXko/03nswAi
-    AXHKMP14UutVEcLYDBXbDrvgpb2p2ZUJnujs6cNyx9cOPeuxnke8+ACWvpnWxwjL
-    M5u8OckiqzRRobNxQZ1vLxzdovYTwTlUAG7QjIXVvOk9VNp/ERhh0eviZK+1/ezk
-    Z8nnPjx+elThQ+r16EM7hD0RDXtOR1VZ0R3OL64AlZYDZz1jEA3lrGhvbjSIfBQk
-    T6mxKUsCy3YbElcOyuohmPRgT1iVDIZ/1iPL0Q0HGm4+EsWCdH6fAPB7TlHD8z2D
-    7JCFLihFDWs5lrZyuWMO9nryZiVjJrOLPcStgJYVd/MhRHR4hC6g09bgo25RMJ6f
-    gyzL4vlEB7aSUih7yjgL9s5DKXP2J71dAhIlF8nnM403R2xEeHyivnyeR/9Ifn7M
-    PJvUMUuoG+ZANSMkrw//XA31o//TVk9WsLD1Edxt5XZCoR+fS+Vz8ScLwP1d/vQE
-    OW/EWzeMRG15C0td1lfHvwPKvf2MN+WLenp9TGZ7A1kEHIpjKvY51AIkX2kW5QLu
-    Y3LBb+HGiZ6j7AaU4uYR3kS1+L79v4kyvhhBOgx/8V+b3+2pQIsVOp79ySGvVwpL
-    FJ2QUgO15hnlQJrFLRYa0PISKrSWf35KXAy04mjqCYqIGkLsz2qQCY2lGcD5k05z
-    bBC4TvxwVxv0ftl2C5Bd0ydl/2YM7GfLrmZmTijK067t4OO+2SROT2oYPDsMtZ6S
-    E8vUXvoGpQ8tf5Nkrn2t0zDG3UDtgZY5UVYnZI+xT7WHsCz//8fY3QMvPXAuc33T
-    vVdiSfP0aBnZXj6oGs/4Vl1Dmm62XLr13+SMoepMWg2Vt7C8jqKOmhFmSOWyOmRH
-    UZJR7nKvTpFnL8atSyFDa4o1bk2U3alOscWS8u8xJ/iMcoONEBhItft6olpMVdzP
-    CTrnCAqMjTSPlQU/9EGtp21KQBed2KdAsJBYuPgwaQeyNIvQEOXmINavl58VD72Y
-    2T4TFEY8dUiExAYpSodbwBL2fr8DJxOX68WH6e3fF7HwX8LRBjZq0XUwh0KxgHN+
-    b9gGXBvgWnJr4NSQGGPiSQVNNHt2ZcBAClYhm+9eC5/VwB+Etg4+1wDmggztiqE=
-    =FdUF
-    -----END PGP PUBLIC KEY BLOCK-----
-
-
-yum_repos:
-  hashicorp:
-    name=Hashicorp Stable - $basearch
-    baseurl=https://rpm.releases.hashicorp.com/RHEL/$releasever/$basearch/stable
-    enabled=1
-    gpgcheck=1
-    gpgkey=https://rpm.releases.hashicorp.com/gpg
-
-apt:
-  preserve_sources_list: true
-  sources:
-    hashicorp:
-      source: deb [trusted=yes] https://apt.releases.hashicorp.com $RELEASE main
-
-package_update: true
-package_upgrade: true
-packages:
-  - docker
-  - nomad
-  - consul
-  - curl
-
-runcmd:
-  - mkdir -p /localdata/volumes/monitoring-postgresql-data
-  - mkdir -p /localdata/volumes/monitoring-prometheus-data
-  - mkdir -p /localdata/volumes/monitoring-loki-data
-  - chmod 0777 /localdata/volumes/monitoring-*
-  - [apt-get, update]
-  - [apt-get, -y, upgrade]
-  - [mkdir, -p, /localdata/consul]
-  - [chown, consul:consul, /localdata/consul]
-  - [mkdir, -p, /localdata/nomad/data]
-  - curl -L -o cni-plugins.tgz "https://github.com/containernetworking/plugins/releases/download/v1.0.0/cni-plugins-linux-$( [ $(uname -m) = aarch64 ] && echo arm64 || echo amd64)"-v1.0.0.tgz
-  - mkdir -p /localdata/cni/bin
-  - tar -C /localdata/cni/bin -xzf cni-plugins.tgz
-  - [systemctl, enable, consul.service]
-  - [systemctl, enable, nomad.service]
-  - [systemctl, start, consul.service]
-  - [systemctl, start, nomad.service]
diff --git a/infra/install-station.sh b/infra/install-station.sh
deleted file mode 100644
index 49ef0b65885134640c5f2acfa2291239f07213a3..0000000000000000000000000000000000000000
--- a/infra/install-station.sh
+++ /dev/null
@@ -1,159 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
-#
-
-
-REGION="nl-cs-001"
-
-sudo yum install -y yum-utils
-sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
-
-### CONSUL ###
-sudo yum -y install consul
-
-cat << EOF > /etc/consul.d/consul.hcl
-datacenter = "$REGION"
-data_dir = "/localdata/consul"
-node_name = "lcu"
-server = true
-bootstrap = true
-bind_addr = "0.0.0.0"
-client_addr = "127.0.0.1"
-advertise_addr = "$IP"
-
-# ui
-ui_config{
-  enabled = true
-}
-
-tls {
-  defaults {
-    ca_path = "/etc/consul.d"
-    ca_file = "/etc/consul.d/consul-agent-ca.pem"
-    cert_file = "/etc/consul.d/$REGION-server-consul-0.pem"
-    key_file = "/etc/consul.d/$REGION-server-consul-0-key.pem"
-
-    verify_incoming = true
-    verify_outgoing = true
-  }
-  internal_rpc {
-    verify_server_hostname = true
-  }
-}
-
-ports {
-  grpc = 8502
-  grpc_tls = 8503
-}
-
-connect {
-  enabled = true
-}
-
-auto_encrypt {
-  allow_tls = true
-}
-
-performance {
-  raft_multiplier = 1
-}
-
-encrypt = "..."
-
-EOF
-sudo mkdir -p /localdata/consul
-sudo chown consul:consul /localdata/consul
-
-# Create on the machine where the CA file is located and copy them afterwards
-#sudo consul tls cert create -server -dc "$REGION"
-sudo chown -R consul:consul /etc/consul.d
-
-systemctrl enable consul.service
-systemctrl start consul.service
-
-consul join -wan cs001c.control.lofar
-
-
-### NOMAD ###
-sudo yum -y install nomad
-
-cat << EOF > /etc/nomad.d/nomad.hcl
-# Full configuration options can be found at https://www.nomadproject.io/docs/configuration
-
-data_dir  = "/localdata/nomad/data"
-bind_addr = "0.0.0.0"
-name      = "lcu"
-region    = "$REGION"
-datacenter = "stat"
-
-server {
-  # license_path is required for Nomad Enterprise as of Nomad v1.1.1+
-  #license_path = "/etc/nomad.d/license.hclic"
-  enabled          = true
-  bootstrap_expect = 1
-  encrypt          = "..."
-}
-
-client {
-  enabled = true
-  servers = ["127.0.0.1"]
-  cni_path = "/localdata/cni/bin"
-
-  host_volume "monitoring-postgresql-data" {
-    path = "/localdata/volumes/monitoring-postgresql-data"
-  }
-
-  host_volume "monitoring-loki-data" {
-    path = "/localdata/volumes/monitoring-loki-data"
-  }
-
-  host_volume "monitoring-prometheus-data" {
-    path = "/localdata/volumes/monitoring-prometheus-data"
-  }
-
-}
-
-consul {
-  address             = "127.0.0.1:8500"
-  server_service_name = "nomad"
-  client_service_name = "nomad-client"
-  auto_advertise      = true
-  server_auto_join    = true
-  client_auto_join    = true
-}
-
-plugin "docker" {
-  config {
-    volumes {
-      enabled = true
-    }
-    allow_caps = ["all"]
-  }
-}
-EOF
-
-mkdir -p /localdata/volumes/monitoring-postgresql-data
-mkdir -p /localdata/volumes/monitoring-prometheus-data
-mkdir -p /localdata/volumes/monitoring-loki-data
-chmod 0777 /localdata/volumes/monitoring-*
-
-curl -L -o cni-plugins.tgz "https://github.com/containernetworking/plugins/releases/download/v1.0.0/cni-plugins-linux-$( [ "$(uname -m)" = aarch64 ] && echo arm64 || echo amd64)"-v1.0.0.tgz
-mkdir -p /localdata/cni/bin && \
-tar -C /localdata/cni/bin -xzf cni-plugins.tgz
-
-echo 1 | tee /proc/sys/net/bridge/bridge-nf-call-arptables
-echo 1 | tee /proc/sys/net/bridge/bridge-nf-call-ip6tables
-echo 1 | tee /proc/sys/net/bridge/bridge-nf-call-iptables
-
-cat << "EOF" > /etc/sysctl.d/10-bridge.conf
-net.bridge.bridge-nf-call-arptables = 1
-net.bridge.bridge-nf-call-ip6tables = 1
-net.bridge.bridge-nf-call-iptables = 1
-EOF
-
-systemctrl enable nomad.service
-systemctrl start nomad.service
-
-nomad server join cs001c.control.lofar:4648
diff --git a/infra/jobs/Makefile b/infra/jobs/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..9300046e1e52f20d73aaf809a99ad9b740c00b9a
--- /dev/null
+++ b/infra/jobs/Makefile
@@ -0,0 +1,12 @@
+
+
+DIR_OUT ?= .
+
+SUBDIRS := $(wildcard */.)
+
+.PHONY: render $(SUBDIRS)
+
+render: $(SUBDIRS)
+
+$(SUBDIRS):
+	$(MAKE) -C $@
diff --git a/infra/jobs/station/Makefile b/infra/jobs/station/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..15bcff97bce249bc9ef432c2043b42d3e3bef57f
--- /dev/null
+++ b/infra/jobs/station/Makefile
@@ -0,0 +1,18 @@
+
+TAG ?= latest
+STATION ?= dev
+DIR_OUT ?= .
+DIR_SRC += .
+SRC_JOBS += $(wildcard $(addsuffix /*.levant.nomad, $(DIR_SRC)))
+JOBS := $(patsubst %.levant.nomad,%.nomad, $(SRC_JOBS))
+ENV ?= ../../env.yaml
+
+.PHONY: render
+
+render: pull $(JOBS) $(DEVICES)
+
+pull:
+	docker pull -q hashicorp/levant
+
+%.nomad: %.levant.nomad
+	docker run --rm -v $(realpath $(ENV)):/env.yaml -v $(realpath $(DIR_SRC)):/in -v $(realpath $(DIR_OUT)):/out hashicorp/levant render -var-file=/env.yaml -var image_tag="$(TAG)" -var station="$(STATION)" -out=/out/$@ /in/$<
diff --git a/infra/jobs/station/device-server.levant.nomad b/infra/jobs/station/device-server.levant.nomad
new file mode 100644
index 0000000000000000000000000000000000000000..4b313fa582a29a3ced6fedbfa358080e51d27813
--- /dev/null
+++ b/infra/jobs/station/device-server.levant.nomad
@@ -0,0 +1,89 @@
+job "device-servers" {
+    datacenters = ["stat"]
+    type        = "service"
+
+    reschedule {
+      unlimited = true
+      delay = "30s"
+      delay_function = "constant"
+    }
+
+    [[ range $device := $.devices ]]
+    [[ with $device ]]
+    [[ $class := .class ]]
+    [[ $name := .class | toLower ]]
+    group "device-[[ $name ]]" {
+        count = 1
+
+        network {
+            mode = "cni/station-control"
+            port "device_server" {}
+            port "zmq_event" {}
+            port "zmq_heartbeat" {}
+            port "metrics" {
+                to = "8000"
+            }
+
+            [[ range $port_name, $port := .ports]]
+              port "[[ $port_name ]]" {
+                  static = "[[ $port ]]"
+              }
+            [[ end ]]
+        }
+
+        service {
+            tags = ["scrape"]
+            name = "device-[[ $name ]]"
+            port = "device_server"
+            meta {
+                metrics_address = "${NOMAD_ADDR_metrics}"
+                metrics_path = "/"
+            }
+        }
+
+        task "device-[[ $name ]]" {
+            driver = "docker"
+
+            config {
+                image = "git.astron.nl:5000/lofar2.0/tango/lofar-device-base:[[ $.image_tag ]]"
+                ports = [
+                    [[ range $port_name, $port := .ports]]
+                      "[[ $port_name ]]",
+                    [[ end ]]
+
+                    "device_server",
+                    "zmq_event",
+                    "zmq_heartbeat",
+                    "metrics"
+                ]
+
+                entrypoint = [
+                    "l2ss-ds",
+                    "[[ $class ]]",
+                    "STAT",
+                    "-v",
+                    "-ORBendPoint",
+                    "giop:tcp:0:${NOMAD_PORT_device_server}",
+                    "-ORBendPointPublish",
+                    "giop:tcp:device-[[ $name ]].service.consul:${NOMAD_PORT_device_server}"
+                ]
+            }
+
+            env {
+                TANGO_HOST               = "tango.service.consul:10000"
+                TANGO_ZMQ_EVENT_PORT     = "${NOMAD_PORT_zmq_event}"
+                TANGO_ZMQ_HEARTBEAT_PORT = "${NOMAD_PORT_zmq_heartbeat}"
+                MINIO_ROOT_USER = "minioadmin"
+                MINIO_ROOT_PASSWORD = "minioadmin"
+            }
+
+
+            resources {
+                cpu    = 250
+                memory = 512
+            }
+        }
+    }
+    [[ end ]]
+    [[ end ]]
+}
diff --git a/infra/jobs/station/dsconfig.levant.nomad b/infra/jobs/station/dsconfig.levant.nomad
new file mode 100644
index 0000000000000000000000000000000000000000..cd5eac570d88ecd36dbef909bbc6f6e061669e1b
--- /dev/null
+++ b/infra/jobs/station/dsconfig.levant.nomad
@@ -0,0 +1,52 @@
+job "dsconfig" {
+  parameterized {
+    payload = "required"
+  }
+
+  datacenters = ["stat"]
+  type        = "batch"
+
+  group "dsconfig" {
+    count = 1
+
+    task "dsconfig" {
+      driver = "docker"
+
+      config {
+        image = "git.astron.nl:5000/lofar2.0/tango/dsconfig:[[ .image_tag ]]"
+        mount {
+          type   = "bind"
+          source = "local/dsconfig-update-settings.json"
+          target = "/tmp/dsconfig-update-settings.json"
+        }
+        mount {
+          type   = "bind"
+          source = "local/run.sh"
+          target = "/run.sh"
+        }
+        entrypoint = ["bash", "/run.sh"]
+
+      }
+
+      dispatch_payload {
+        file = "dsconfig-update-settings.json"
+      }
+      template {
+        data        = <<EOH
+        #!/bin/bash
+        /manage_object_properties.py --write < /tmp/dsconfig-update-settings.json
+        json2tango --write --update /tmp/dsconfig-update-settings.json
+        EOH
+        destination = "local/run.sh"
+      }
+      resources {
+        cpu    = 100
+        memory = 100
+      }
+      env {
+        TANGO_HOST = "tango.service.consul:10000"
+      }
+    }
+
+  }
+}
diff --git a/infra/jobs/station/ec-sim.levant.nomad b/infra/jobs/station/ec-sim.levant.nomad
new file mode 100644
index 0000000000000000000000000000000000000000..1880d16d280f3e2cd04b15a68f7ba9c840e8f5ea
--- /dev/null
+++ b/infra/jobs/station/ec-sim.levant.nomad
@@ -0,0 +1,35 @@
+job "simulators" {
+  datacenters = ["stat"]
+  type        = "service"
+
+  group "ec-sim" {
+    count = 1
+
+    network {
+      mode = "bridge"
+      port "opcua" {
+        static = 4850
+        to     = 4840
+      }
+    }
+
+    service {
+      name = "ec-sim"
+      port = "opcua"
+    }
+
+    task "sim" {
+      driver = "docker"
+
+      config {
+        image = "git.astron.nl:5000/lofar2.0/tango/ec-sim:latest"
+        ports = ["opcua"]
+      }
+      resources {
+        cpu    = 100
+        memory = 100
+      }
+    }
+
+  }
+}
diff --git a/infra/jobs/station/jupyter.levant.nomad b/infra/jobs/station/jupyter.levant.nomad
new file mode 100644
index 0000000000000000000000000000000000000000..3475f0dbbb830f4a50fcb98b4f5bc8ea06ce2a45
--- /dev/null
+++ b/infra/jobs/station/jupyter.levant.nomad
@@ -0,0 +1,69 @@
+job "jupyter" {
+  datacenters = ["stat"]
+  type        = "service"
+
+  group "jupyter-lab" {
+    count = 1
+
+    network {
+      mode = "bridge"
+      port "jupyter" {
+        to = 8888
+      }
+    }
+
+    volume "notebooks" {
+      type      = "host"
+      read_only = false
+      source    = "jupyter-notebooks"
+    }
+
+    service {
+      name = "jupyter"
+      port = "jupyter"
+      task = "jupyter"
+    }
+
+    task "jupyter-lab" {
+      driver = "docker"
+
+      volume_mount {
+        volume      = "notebooks"
+        destination = "/home/jovyan/notebooks"
+        read_only   = false
+      }
+
+      config {
+        image = "git.astron.nl:5000/lofar2.0/tango/jupyter-lab:[[.image_tag]]"
+        ports = ["jupyter"]
+        mount {
+          type   = "bind"
+          source = "local/jupyter_notebook_config.py"
+          target = "/home/jovyan/.jupyter/jupyter_notebook_config.py"
+        }
+      }
+
+      env {
+        TANGO_HOST = "tango.service.consul:10000"
+      }
+
+      resources {
+        cpu        = 1024
+        memory     = 1024
+        memory_max = 8192
+      }
+      template {
+        data = <<EOH
+c.NotebookApp.base_url = '/jupyter'
+c.NotebookApp.password_required = False
+c.NotebookApp.token = ''
+c.NotebookApp.trust_xheaders = True
+c.NotebookApp.allow_origin = '*'
+
+        EOH
+
+        destination = "local/jupyter_notebook_config.py"
+      }
+    }
+  }
+}
diff --git a/infra/jobs/station/logging.levant.nomad b/infra/jobs/station/logging.levant.nomad
new file mode 100644
index 0000000000000000000000000000000000000000..a3aca67de78a979ef11c45194f8435ab64f4161d
--- /dev/null
+++ b/infra/jobs/station/logging.levant.nomad
@@ -0,0 +1,155 @@
+job "log-scraping" {
+    datacenters = ["stat"]
+    type        = "system"
+
+    update {
+        min_healthy_time  = "10s"
+        healthy_deadline  = "5m"
+        progress_deadline = "10m"
+        auto_revert       = true
+    }
+
+    group "vector" {
+        count = 1
+        restart {
+            attempts = 3
+            interval = "10m"
+            delay    = "30s"
+            mode     = "fail"
+        }
+        network {
+            mode = "bridge"
+            port "api" {
+                to = 8686
+            }
+        }
+
+        volume "docker-sock" {
+            type      = "host"
+            source    = "docker-sock-ro"
+            read_only = true
+        }
+
+        ephemeral_disk {
+            size   = 500
+            sticky = true
+        }
+
+        service {
+            connect {
+                sidecar_service {
+                    proxy {
+                        upstreams {
+                            destination_name = "prometheus"
+                            local_bind_port  = 9090
+                        }
+                        upstreams {
+                            destination_name = "loki"
+                            local_bind_port  = 3100
+                        }
+                    }
+                }
+            }
+        }
+
+        task "vector" {
+            driver = "docker"
+            config {
+                image = "timberio/vector:0.32.1.custom.989ad14-distroless-static"
+                ports = ["api"]
+            }
+            # docker socket volume mount
+            volume_mount {
+                volume      = "docker-sock"
+                destination = "/var/run/docker.sock"
+                read_only   = true
+            }
+            # Vector won't start unless the sinks(backends) configured are healthy
+            env {
+                VECTOR_CONFIG          = "local/vector.toml"
+                VECTOR_REQUIRE_HEALTHY = "true"
+            }
+            # resource limits are a good idea because you don't want your log collection to consume all resources available
+            resources {
+                cpu    = 50 # 500 MHz
+                memory = 100 # 256MB
+            }
+            # template with Vector's configuration
+            template {
+                destination     = "local/vector.toml"
+                change_mode     = "signal"
+                change_signal   = "SIGHUP"
+                left_delimiter  = "(("
+                right_delimiter = "))"
+                data            = <<EOF
+data_dir                     = "alloc/data/vector/"
+healthchecks.require_healthy = true
+
+[api]
+  enabled              = true
+  address              = "0.0.0.0:8686"
+  playground           = false
+
+[sources.docker-local]
+  type                 = "docker_logs"
+  docker_host          = "/var/run/docker.sock"
+  exclude_containers   = ["vector-"]
+[transforms.nomad-flags]
+  inputs = ["docker-local"]
+  type   = "remap"
+  source = '''
+    structured =
+      parse_syslog(.message) ??
+      parse_json(.message) ??
+      parse_common_log(.message) ??
+      parse_key_value!(.message)
+    .                = merge!(., structured)
+    .nomad.job       = .label."com.hashicorp.nomad.job_name"
+    .nomad.task      = .label."com.hashicorp.nomad.task_name"
+    .nomad.group     = .label."com.hashicorp.nomad.task_group_name"
+    .nomad.namespace = .label."com.hashicorp.nomad.namespace"
+    .nomad.node      = .label."com.hashicorp.nomad.node_name"
+    .nomad.job_id    = .label."com.hashicorp.nomad.job_id"
+    .nomad.node_id   = .label."com.hashicorp.nomad.node_id"
+  '''
+
+[sinks.loki]
+  type                 = "loki"
+  inputs               = [ "nomad-flags" ]
+  endpoint             = "http://localhost:3100"
+  encoding.codec       = "json"
+  healthcheck.enabled  = true
+  remove_label_fields  = true
+[sinks.loki.labels]
+  "nomad_*"            = '{{ nomad }}'
+
+[sources.host_metrics]
+  type                 = "host_metrics"
+  scrape_interval_secs = 10
+[sources.nomad_metrics]
+  type                 = "prometheus_scrape"
+  scrape_interval_secs = 10
+  endpoints            = [ "http://(( env "attr.unique.network.ip-address" )):4646/v1/metrics?format=prometheus" ]
+[sources.vector_metrics]
+  type                 = "internal_metrics"
+  scrape_interval_secs = 10
+[sinks.prometheus_remote_write]
+  type                 = "prometheus_remote_write"
+  inputs               = [ "host_metrics", "nomad_metrics", "internal_metrics" ]
+  endpoint             = "http://localhost:9090/api/v1/write"
+  healthcheck.enabled  = false
+EOF
+            }
+            service {
+                check {
+                    port     = "api"
+                    type     = "http"
+                    path     = "/health"
+                    interval = "30s"
+                    timeout  = "5s"
+                }
+            }
+            kill_timeout = "30s"
+        }
+    }
+}
diff --git a/infra/jobs/station/monitoring.levant.nomad b/infra/jobs/station/monitoring.levant.nomad
new file mode 100644
index 0000000000000000000000000000000000000000..a9010f7477ff2ca104252385bd1196504d0a35af
--- /dev/null
+++ b/infra/jobs/station/monitoring.levant.nomad
@@ -0,0 +1,395 @@
+job "monitoring" {
+  datacenters = ["stat"]
+  type        = "service"
+
+  group "postgres" {
+    count = 1
+
+    network {
+      mode = "bridge"
+      port "postgres" {
+        to = 5432
+      }
+    }
+
+    volume "postgresql" {
+      type      = "host"
+      read_only = false
+      source    = "monitoring-postgresql-data"
+    }
+
+    service {
+      name         = "postgres"
+      port         = "postgres"
+      task         = "postgres"
+      address_mode = "alloc"
+
+      connect {
+        sidecar_service {}
+      }
+    }
+
+    task "postgres" {
+      driver = "docker"
+
+      volume_mount {
+        volume      = "postgresql"
+        destination = "/var/lib/postgresql/data"
+        read_only   = false
+      }
+
+      config {
+        image = "postgres:[[.monitoring.db.version]]"
+        ports = ["postgres"]
+      }
+
+      env {
+        POSTGRES_DB       = "grafana"
+        POSTGRES_USER     = "postgres"
+        POSTGRES_PASSWORD = "password"
+      }
+
+      resources {
+        cpu    = 250
+        memory = 512
+      }
+    }
+  }
+
+  group "grafana" {
+    network {
+      mode = "bridge"
+      port "http" {
+        to = 3000
+      }
+    }
+
+    service {
+      tags = ["haproxy", "scrape"]
+      name = "grafana"
+      port = "http"
+
+      connect {
+        sidecar_service {
+          proxy {
+            upstreams {
+              destination_name = "postgres"
+              local_bind_port  = 5432
+            }
+            upstreams {
+              destination_name = "prometheus"
+              local_bind_port  = 9090
+            }
+            upstreams {
+              destination_name = "loki"
+              local_bind_port  = 3100
+            }
+          }
+        }
+      }
+    }
+
+    task "wait-for-db" {
+      lifecycle {
+        hook    = "prestart"
+        sidecar = false
+      }
+      driver = "docker"
+
+      config {
+        image   = "postgres:[[.monitoring.db.version]]"
+        command = "sh"
+        args    = ["-c", "while ! pg_isready -h localhost; do sleep 1; done"]
+      }
+      env {
+        PGPASSFILE = "local/pgpass.txt"
+      }
+
+      template {
+        data        = <<EOH
+{{ range service "postgres" }}
+localhost:5432:grafana:postgres:password
+{{ end }}
+                EOH
+        destination = "local/pgpass.txt"
+      }
+    }
+
+    task "grafana" {
+      driver = "docker"
+      config {
+        image = "git.astron.nl:5000/lofar2.0/tango/grafana:[[.image_tag]]"
+        ports = ["http"]
+        mount {
+          type   = "bind"
+          source = "local/datasource-prometheus.yaml"
+          target = "/etc/grafana/provisioning/datasources/prometheus.yaml"
+        }
+        mount {
+          type   = "bind"
+          source = "local/datasource-loki.yaml"
+          target = "/etc/grafana/provisioning/datasources/loki.yaml"
+        }
+      }
+
+      env {
+        GF_SERVER_DOMAIN              = "[[.station]]c.control.lofar"
+        GF_SERVER_ROOT_URL            = "%(protocol)s://%(domain)s:%(http_port)s/grafana/"
+        GF_SERVER_SERVE_FROM_SUB_PATH = "true"
+        GF_DATABASE_TYPE              = "postgres"
+        GF_DATABASE_HOST              = "localhost:5432"
+        GF_DATABASE_NAME              = "grafana"
+        GF_DATABASE_USER              = "postgres"
+        GF_DATABASE_PASSWORD          = "password"
+        GF_AUTH_ANONYMOUS_ENABLED     = "true"
+        GF_AUTH_ANONYMOUS_ORG_ROLE    = "Admin"
+        GF_AUTH_DISABLE_LOGIN_FORM    = "true"
+      }
+
+      template {
+        data        = <<EOH
+        apiVersion: 1
+
+        datasources:
+          - name: Prometheus
+            type: prometheus
+            access: proxy
+            orgId: 1
+            uid: prometheus
+            url: http://localhost:9090
+            isDefault: true
+
+        EOH
+        destination = "local/datasource-prometheus.yaml"
+      }
+      template {
+        data        = <<EOH
+        apiVersion: 1
+
+        datasources:
+          - name: Loki
+            type: loki
+            access: proxy
+            orgId: 1
+            uid: loki
+            url: http://localhost:3100
+            jsonData:
+              esVersion:  7.10.0
+              includeFrozen: false
+              logLevelField:
+              logMessageField:
+              maxConcurrentShardRequests: 5
+              timeField: "@timestamp"
+
+        EOH
+        destination = "local/datasource-loki.yaml"
+      }
+      resources {
+        cpu        = 250
+        memory     = 1024
+        memory_max = 8192
+      }
+    }
+  }
+  group "prometheus" {
+
+    network {
+      mode = "bridge"
+
+      port "prometheus" {
+        to = 9090
+      }
+    }
+
+    volume "prometheus" {
+      type      = "host"
+      read_only = false
+      source    = "monitoring-prometheus-data"
+    }
+
+    service {
+      tags         = ["haproxy", "scrape"]
+      name         = "prometheus"
+      port         = "prometheus"
+      address_mode = "alloc"
+
+      connect {
+        sidecar_service {}
+      }
+
+      check {
+        type     = "http"
+        name     = "prometheus_health"
+        port     = "prometheus"
+        path     = "/-/healthy"
+        interval = "20s"
+        timeout  = "30s"
+      }
+    }
+
+    task "prometheus" {
+      driver = "docker"
+
+      volume_mount {
+        volume      = "prometheus"
+        destination = "/prometheus"
+        read_only   = false
+      }
+
+      config {
+        image = "git.astron.nl:5000/lofar2.0/tango/prometheus:[[.image_tag]]"
+        ports = ["prometheus"]
+        args  = [
+          "--config.file=/etc/prometheus/prometheus.yml",
+          "--web.enable-remote-write-receiver"
+        ]
+        mount {
+          type   = "bind"
+          source = "local/prometheus.yaml"
+          target = "/etc/prometheus/prometheus.yml"
+        }
+      }
+
+      template {
+        data          = <<EOH
+        global:
+          evaluation_interval: 10s
+          scrape_interval: 10s
+          scrape_timeout: 10s
+
+        scrape_configs:
+          - job_name: tango
+            metrics_path: /
+            consul_sd_configs:
+            - server: '{{ with node "cs001c@lofar-cs-001" }}{{ .Node.Address }}:8500{{ end }}'
+              services: ['tango-prometheus-exporter']
+            relabel_configs:
+              - target_label: host
+                replacement: localhost
+
+          - job_name: tango-fast
+            scrape_interval: 1s
+            consul_sd_configs:
+            - server: '{{ with node "cs001c@lofar-cs-001" }}{{ .Node.Address }}:8500{{ end }}'
+              services: ['tango-prometheus-fast-exporter']
+            relabel_configs:
+              - target_label: host
+                replacement: localhost
+
+          - job_name: tango-slow
+            scrape_interval: 60s
+            scrape_timeout: 30s
+            consul_sd_configs:
+            - server: '{{ with node "cs001c@lofar-cs-001" }}{{ .Node.Address }}:8500{{ end }}'
+              services: ['tango-prometheus-slow-exporter']
+            relabel_configs:
+              - target_label: host
+                replacement: localhost
+
+          - job_name: 'consul'
+            metrics_path: '/v1/agent/metrics'
+            params:
+              format: ['prometheus']
+            consul_sd_configs:
+            - server: '{{ with node "cs001c@lofar-cs-001" }}{{ .Node.Address }}:8500{{ end }}'
+              services: ['consul']
+            relabel_configs:
+              - target_label: host
+
+          - job_name: 'nomad_metrics'
+            consul_sd_configs:
+            - server: '{{ with node "cs001c@lofar-cs-001" }}{{ .Node.Address }}:8500{{ end }}'
+              services: ['nomad-client', 'nomad']
+            relabel_configs:
+            - source_labels: ['__meta_consul_tags']
+              regex: '(.*)http(.*)'
+              action: keep
+            scrape_interval: 5s
+            metrics_path: /v1/metrics
+            params:
+              format: ['prometheus']
+
+          - job_name: "consul_services"
+            consul_sd_configs:
+              - server: '{{ with node "cs001c@lofar-cs-001" }}{{ .Node.Address }}:8500{{ end }}'
+                services:
+          {{range services}}{{if in .Tags "scrape"}}{{ if .Name | regexMatch "(.+)-sidecar-proxy$" }}{{ else }}
+                  - '{{.Name}}'
+          {{end}}{{end}}{{end}}
+            relabel_configs:
+              - target_label: host
+                replacement: localhost
+              - source_labels: [__meta_consul_service_metadata_metrics_path]
+                regex:  '(/.*)'            # capture '/...' part
+                target_label: __metrics_path__  # change metrics path
+              - source_labels: [__meta_consul_service_metadata_metrics_address]
+                regex:  '(.+)'
+                target_label: __address__  # change address
+        EOH
+        change_mode   = "signal"
+        change_signal = "SIGHUP"
+        destination   = "local/prometheus.yaml"
+      }
+
+      resources {
+        cpu        = 500
+        memory     = 2048
+        memory_max = 8192
+      }
+    }
+  }
+
+  group "loki" {
+
+    ephemeral_disk {
+      size = 101
+    }
+    network {
+      mode = "bridge"
+
+      port "loki" {
+        to = 3100
+        # should be activated when fully replaces docker-compose setup
+        #static = 3100
+      }
+    }
+
+    volume "loki" {
+      type      = "host"
+      read_only = false
+      source    = "monitoring-loki-data"
+    }
+
+    service {
+      tags         = ["haproxy", "scrape"]
+      name         = "loki"
+      port         = "loki"
+      address_mode = "alloc"
+
+      connect {
+        sidecar_service {}
+      }
+    }
+
+    task "loki" {
+      driver = "docker"
+
+      volume_mount {
+        volume      = "loki"
+        destination = "/loki"
+        read_only   = false
+      }
+
+      config {
+        image = "git.astron.nl:5000/lofar2.0/tango/loki:[[.image_tag]]"
+        ports = ["prometheus"]
+      }
+
+      resources {
+        cpu        = 250
+        memory     = 768
+        memory_max = 8192
+      }
+    }
+  }
+}
diff --git a/infra/jobs/station/nomad-client.nomad b/infra/jobs/station/nomad-client.nomad
new file mode 100644
index 0000000000000000000000000000000000000000..80869738b76c62a3ffd1699668b0bab466b5d356
--- /dev/null
+++ b/infra/jobs/station/nomad-client.nomad
@@ -0,0 +1,391 @@
+job "nomad-client" {
+  datacenters = ["stat"]
+  type        = "service"
+
+  group "qemu-vm" {
+    count = 1
+
+    volume "images" {
+      type      = "host"
+      read_only = false
+      source    = "images"
+    }
+
+    network {
+      port "http" {}
+    }
+
+    task "imds" {
+      lifecycle {
+        hook    = "prestart"
+        sidecar = true
+      }
+
+      driver = "exec"
+
+      config {
+        command = "python3"
+        args    = [
+          "-m", "http.server", "${NOMAD_PORT_http}",
+          "--directory", "local/"
+        ]
+      }
+      template {
+        data = <<EOH
+        instance-id: ${NOMAD_SHORT_ALLOC_ID}
+        local-hostname: ${NOMAD_SHORT_ALLOC_ID}-client
+        EOH
+
+        destination = "local/meta-data"
+      }
+      template {
+        data = <<EOH
+        #cloud-config
+        password: password
+        chpasswd:
+          expire: False
+        ssh_authorized_keys:
+          - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPuqXIY4ATAkSfk3+nKIoyvFh/AUbhASgKTqUM0SfXvg root@cs001c
+
+        bootcmd:
+          - mkdir /localdata
+          - DEBIAN_FRONTEND=noninteractive apt-get -yq update
+          - DEBIAN_FRONTEND=noninteractive apt-get -yq install gnupg
+
+        mounts:
+          - [ vdb, /localdata ]
+          - [ vdc, /var/lib/docker ]
+
+        runcmd:
+          - ip link set dev ens3 mtu 9000
+          - [systemctl, restart, systemd-resolved]
+          - [sysctl ,-w ,fs.inotify.max_user_instances=256]
+          - curl -L -o cni-plugins.tgz "https://github.com/containernetworking/plugins/releases/download/v1.3.0/cni-plugins-linux-$( [ $(uname -m) = aarch64 ] && echo arm64 || echo amd64)"-v1.3.0.tgz
+          - mkdir -p /opt/cni/bin
+          - tar -C /opt/cni/bin -xzf cni-plugins.tgz
+          - mkdir -p /localdata/nomad/data
+          - mkdir -p /localdata/volumes/monitoring-postgresql-data
+          - mkdir -p /localdata/volumes/monitoring-prometheus-data
+          - mkdir -p /localdata/volumes/monitoring-loki-data
+          - mkdir -p /localdata/volumes/tango-database
+          - mkdir -p /localdata/volumes/object-storage-data
+          - chmod 0777 /localdata/volumes/*
+          - [systemctl, enable, consul.service]
+          - [systemctl, start, consul.service]
+          - [systemctl, enable, nomad.service]
+          - [systemctl, start, nomad.service]
+          - [systemctl, enable, docker.service]
+          - [systemctl, start, docker.service]
+          - iptables -A CNI-BR9000 -d 172.31.0.0/16 -j ACCEPT
+
+        package_update: true
+        package_upgrade: true
+        packages:
+          - docker.io
+          - nomad
+          - curl
+          - consul
+
+        write_files:
+        - content: |
+            {
+                "cniVersion": "0.4.0",
+                "name": "station-control",
+                "plugins": [
+                {
+                  "type": "bridge",
+                  "bridge": "br9000",
+                  "isDefaultGateway": true,
+                  "forceAddress": false,
+                  "ipMasq": true,
+                  "hairpinMode": true,
+                  "mtu": 9000,
+                  "ipam": {
+                    "type": "host-local",
+                    "subnet": "172.31.0.0/16"
+                  }
+                },
+                {
+                  "type": "firewall",
+                  "backend": "iptables",
+                  "iptablesAdminChainName": "CNI-BR9000"
+                },
+                {
+                  "type": "portmap",
+                  "capabilities": { "portMappings": true },
+                  "snat": true
+                }
+                ]
+            }
+          path: /opt/cni/config/station-control.conflist
+          defer: true
+
+        - content: |
+            bind_addr = "0.0.0.0"
+            name      = "{{ env "NOMAD_SHORT_ALLOC_ID" }}-client"
+            region    = "{{ env "node.region" }}"
+            data_dir  = "/localdata/nomad/data"
+            datacenter = "stat"
+
+            client {
+              enabled = true
+              servers = ["{{ env "attr.unique.network.ip-address" }}"]
+              cni_path = "/opt/cni/bin"
+
+              host_volume "docker-sock-ro" {
+                path      = "/var/run/docker.sock"
+                read_only = true
+              }
+
+              host_volume "monitoring-postgresql-data" {
+                path = "/localdata/volumes/monitoring-postgresql-data"
+              }
+
+              host_volume "monitoring-loki-data" {
+                path = "/localdata/volumes/monitoring-loki-data"
+              }
+
+              host_volume "monitoring-prometheus-data" {
+                path = "/localdata/volumes/monitoring-prometheus-data"
+              }
+
+              host_volume "tango-database" {
+                path = "/localdata/volumes/tango-database"
+              }
+
+              host_volume "object-storage-data" {
+                path = "/localdata/volumes/object-storage-data"
+              }
+
+              host_volume "jupyter-notebooks" {
+                path = "/localdata/volumes/jupyter-notebooks"
+              }
+
+              options = {
+                "driver.allowlist" = "docker,exec"
+              }
+            }
+
+            consul {
+              address             = "localhost:8500"
+              server_service_name = "nomad"
+              client_service_name = "nomad-client"
+              auto_advertise      = true
+              server_auto_join    = true
+              client_auto_join    = true
+            }
+
+            telemetry {
+              collection_interval        = "1s"
+              disable_hostname           = true
+              prometheus_metrics         = true
+              publish_allocation_metrics = true
+              publish_node_metrics       = true
+            }
+
+            plugin "docker" {
+              config {
+                volumes {
+                  enabled = true
+                }
+                allow_caps = ["all"]
+                extra_labels = ["job_name", "job_id", "task_group_name", "task_name", "namespace", "node_name", "node_id"]
+              }
+            }
+          path: /etc/nomad.d/nomad.hcl
+          defer: true
+        - content: |
+            datacenter = "{{ env "attr.consul.datacenter" }}"
+            data_dir = "/opt/consul"
+            bind_addr = "{{"{{"}} GetInterfaceIP \"ens3\" {{"}}"}}"
+            encrypt = "{{ with nomadVar "nomad/jobs/nomad-client/qemu-vm/imds" }}{{ .consul_encrypt }}{{ end }}"
+            retry_join = ["10.99.250.250"]
+            server = false
+            ports {
+              grpc = 8502
+            }
+          path: /etc/consul.d/consul.hcl
+          defer: true
+
+        apt:
+          preserve_source_list: true
+          sources:
+            hashicorp:
+              source: 'deb https://apt.releases.hashicorp.com $RELEASE main'
+              key: |
+                -----BEGIN PGP PUBLIC KEY BLOCK-----
+
+                mQINBGO9u+MBEADmE9i8rpt8xhRqxbzlBG06z3qe+e1DI+SyjscyVVRcGDrEfo+J
+                W5UWw0+afey7HFkaKqKqOHVVGSjmh6HO3MskxcpRm/pxRzfni/OcBBuJU2DcGXnG
+                nuRZ+ltqBncOuONi6Wf00McTWviLKHRrP6oWwWww7sYF/RbZp5xGmMJ2vnsNhtp3
+                8LIMOmY2xv9LeKMh++WcxQDpIeRohmSJyknbjJ0MNlhnezTIPajrs1laLh/IVKVz
+                7/Z73UWX+rWI/5g+6yBSEtj368N7iyq+hUvQ/bL00eyg1Gs8nE1xiCmRHdNjMBLX
+                lHi0V9fYgg3KVGo6Hi/Is2gUtmip4ZPnThVmB5fD5LzS7Y5joYVjHpwUtMD0V3s1
+                HiHAUbTH+OY2JqxZDO9iW8Gl0rCLkfaFDBS2EVLPjo/kq9Sn7vfp2WHffWs1fzeB
+                HI6iUl2AjCCotK61nyMR33rNuNcbPbp+17NkDEy80YPDRbABdgb+hQe0o8htEB2t
+                CDA3Ev9t2g9IC3VD/jgncCRnPtKP3vhEhlhMo3fUCnJI7XETgbuGntLRHhmGJpTj
+                ydudopoMWZAU/H9KxJvwlVXiNoBYFvdoxhV7/N+OBQDLMevB8XtPXNQ8ZOEHl22G
+                hbL8I1c2SqjEPCa27OIccXwNY+s0A41BseBr44dmu9GoQVhI7TsetpR+qwARAQAB
+                tFFIYXNoaUNvcnAgU2VjdXJpdHkgKEhhc2hpQ29ycCBQYWNrYWdlIFNpZ25pbmcp
+                IDxzZWN1cml0eStwYWNrYWdpbmdAaGFzaGljb3JwLmNvbT6JAlQEEwEIAD4CGwMF
+                CwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQR5iuxlTlwVQoyOQu6qFvy8piHnAQUC
+                Y728PQUJCWYB2gAKCRCqFvy8piHnAd16EADeBtTgkdVEvct40TH/9HKkR/Lc/ohM
+                rer6FFHdKmceJ6Ma8/Qm4nCO5C7c4+EPjsUXdhK5w8DSdC5VbKLJDY1EnDlmU5B1
+                wSFkGoYKoB8lUn30E77E33MTu2kfrSuF605vetq269CyBwIJV7oNN6311dW8iQ6z
+                IytTtlJbVr4YZ7Vst40/uR4myumk9bVBGEd6JhFAPmr/um+BZFhRf9/8xtOryOyB
+                GF2d+bc9IoAugpxwv0IowHEqkI4RpK2U9hvxG80sTOcmerOuFbmNyPwnEgtJ6CM1
+                bc8WAmObJiQcRSLbcgF+a7+2wqrUbCqRE7QoS2wjd1HpUVPmSdJN925c2uaua2A4
+                QCbTEg8kV2HiP0HGXypVNhZJt5ouo0YgR6BSbMlsMHniDQaSIP1LgmEz5xD4UAxO
+                Y/GRR3LWojGzVzBb0T98jpDgPtOu/NpKx3jhSpE2U9h/VRDiL/Pf7gvEIxPUTKuV
+                5D8VqAiXovlk4wSH13Q05d9dIAjuinSlxb4DVr8IL0lmx9DyHehticmJVooHDyJl
+                HoA2q2tFnlBBAFbN92662q8Pqi9HbljVRTD1vUjof6ohaoM+5K1C043dmcwZZMTc
+                7gV1rbCuxh69rILpjwM1stqgI1ONUIkurKVGZHM6N2AatNKqtBRdGEroQo1aL4+4
+                u+DKFrMxOqa5b7kCDQRjvbwTARAA0ut7iKLj9sOcp5kRG/5V+T0Ak2k2GSus7w8e
+                kFh468SVCNUgLJpLzc5hBiXACQX6PEnyhLZa8RAG+ehBfPt03GbxW6cK9nx7HRFQ
+                GA79H5B4AP3XdEdT1gIL2eaHdQot0mpF2b07GNfADgj99MhpxMCtTdVbBqHY8YEQ
+                Uq7+E9UCNNs45w5ddq07EDk+o6C3xdJ42fvS2x44uNH6Z6sdApPXLrybeun74C1Z
+                Oo4Ypre4+xkcw2q2WIhy0Qzeuw+9tn4CYjrhw/+fvvPGUAhtYlFGF6bSebmyua8Q
+                MTKhwqHqwJxpjftM3ARdgFkhlH1H+PcmpnVutgTNKGcy+9b/lu/Rjq/47JZ+5VkK
+                ZtYT/zO1oW5zRklHvB6R/OcSlXGdC0mfReIBcNvuNlLhNcBA9frNdOk3hpJgYDzg
+                f8Ykkc+4z8SZ9gA3g0JmDHY1X3SnSadSPyMas3zH5W+16rq9E+MZztR0RWwmpDtg
+                Ff1XGMmvc+FVEB8dRLKFWSt/E1eIhsK2CRnaR8uotKW/A/gosao0E3mnIygcyLB4
+                fnOM3mnTF3CcRumxJvnTEmSDcoKSOpv0xbFgQkRAnVSn/gHkcbVw/ZnvZbXvvseh
+                7dstp2ljCs0queKU+Zo22TCzZqXX/AINs/j9Ll67NyIJev445l3+0TWB0kego5Fi
+                UVuSWkMAEQEAAYkEcgQYAQgAJhYhBHmK7GVOXBVCjI5C7qoW/LymIecBBQJjvbwT
+                AhsCBQkJZgGAAkAJEKoW/LymIecBwXQgBBkBCAAdFiEE6wr14plJaVlvmYc+cG5m
+                g2nAhekFAmO9vBMACgkQcG5mg2nAhenPURAAimI0EBZbqpyHpwpbeYq3Pygg1bdo
+                IlBQUVoutaN1lR7kqGXwYH+BP6G40x79LwVy/fWV8gO7cDX6D1yeKLNbhnJHPBus
+                FJDmzDPbjTlyWlDqJoWMiPqfAOc1A1cHodsUJDUlA01j1rPTho0S9iALX5R50Wa9
+                sIenpfe7RVunDwW5gw6y8me7ncl5trD0LM2HURw6nYnLrxePiTAF1MF90jrAhJDV
+                +krYqd6IFq5RHKveRtCuTvpL7DlgVCtntmbXLbVC/Fbv6w1xY3A7rXko/03nswAi
+                AXHKMP14UutVEcLYDBXbDrvgpb2p2ZUJnujs6cNyx9cOPeuxnke8+ACWvpnWxwjL
+                M5u8OckiqzRRobNxQZ1vLxzdovYTwTlUAG7QjIXVvOk9VNp/ERhh0eviZK+1/ezk
+                Z8nnPjx+elThQ+r16EM7hD0RDXtOR1VZ0R3OL64AlZYDZz1jEA3lrGhvbjSIfBQk
+                T6mxKUsCy3YbElcOyuohmPRgT1iVDIZ/1iPL0Q0HGm4+EsWCdH6fAPB7TlHD8z2D
+                7JCFLihFDWs5lrZyuWMO9nryZiVjJrOLPcStgJYVd/MhRHR4hC6g09bgo25RMJ6f
+                gyzL4vlEB7aSUih7yjgL9s5DKXP2J71dAhIlF8nnM403R2xEeHyivnyeR/9Ifn7M
+                PJvUMUuoG+ZANSMkrw//XA31o//TVk9WsLD1Edxt5XZCoR+fS+Vz8ScLwP1d/vQE
+                OW/EWzeMRG15C0td1lfHvwPKvf2MN+WLenp9TGZ7A1kEHIpjKvY51AIkX2kW5QLu
+                Y3LBb+HGiZ6j7AaU4uYR3kS1+L79v4kyvhhBOgx/8V+b3+2pQIsVOp79ySGvVwpL
+                FJ2QUgO15hnlQJrFLRYa0PISKrSWf35KXAy04mjqCYqIGkLsz2qQCY2lGcD5k05z
+                bBC4TvxwVxv0ftl2C5Bd0ydl/2YM7GfLrmZmTijK067t4OO+2SROT2oYPDsMtZ6S
+                E8vUXvoGpQ8tf5Nkrn2t0zDG3UDtgZY5UVYnZI+xT7WHsCz//8fY3QMvPXAuc33T
+                vVdiSfP0aBnZXj6oGs/4Vl1Dmm62XLr13+SMoepMWg2Vt7C8jqKOmhFmSOWyOmRH
+                UZJR7nKvTpFnL8atSyFDa4o1bk2U3alOscWS8u8xJ/iMcoONEBhItft6olpMVdzP
+                CTrnCAqMjTSPlQU/9EGtp21KQBed2KdAsJBYuPgwaQeyNIvQEOXmINavl58VD72Y
+                2T4TFEY8dUiExAYpSodbwBL2fr8DJxOX68WH6e3fF7HwX8LRBjZq0XUwh0KxgHN+
+                b9gGXBvgWnJr4NSQGGPiSQVNNHt2ZcBAClYhm+9eC5/VwB+Etg4+1wDmggztiqE=
+                =FdUF
+                -----END PGP PUBLIC KEY BLOCK-----
+        EOH
+
+        destination = "local/user-data"
+      }
+      template {
+        data = <<EOH
+
+        EOH
+
+        destination = "local/vendor-data"
+      }
+
+
+      #  artifact {
+      #    source      = "https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2"
+      #    destination = "alloc/data"
+      #  }
+    }
+
+    task "prepare-image" {
+
+      driver = "exec"
+      lifecycle {
+        hook = "prestart"
+      }
+
+      config {
+        command = "/bin/bash"
+        args    = ["local/run.sh"]
+      }
+
+      template {
+        data        = <<EOH
+        #!/bin/bash
+        qemu-img resize local/disk-{{ env "NOMAD_SHORT_ALLOC_ID" }}.qcow2 +10G
+        mv local/disk-{{ env "NOMAD_SHORT_ALLOC_ID" }}.qcow2 images/disk-{{ env "NOMAD_SHORT_ALLOC_ID" }}.qcow2
+        EOH
+        destination = "local/run.sh"
+      }
+
+      volume_mount {
+        volume           = "images"
+        destination      = "images"
+        propagation_mode = "host-to-task"
+      }
+
+      artifact {
+        source      = "https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2"
+        mode        = "file"
+        destination = "local/disk-${NOMAD_SHORT_ALLOC_ID}.qcow2"
+      }
+    }
+
+    task "debian" {
+
+      driver = "qemu"
+
+      config {
+        image_path        = "/opt/nomad/images/disk-${NOMAD_SHORT_ALLOC_ID}.qcow2"
+        #image_path        = "/opt/images/debian-12-genericcloud-amd64.qcow2"
+        drive_interface   = "virtio"
+        graceful_shutdown = true
+        accelerator       = "kvm"
+        args              = [
+          "-nographic",
+          #"-snapshot",
+          #"-drive", "file=debian-12-genericcloud-amd64.qcow2,if=virtio",
+          "-net", "nic,model=virtio,macaddr=52:54:00:12:34:56",
+          "-net", "tap,script=/etc/ovs-ifup,downscript=/etc/ovs-ifdown",
+          "-drive", "file=/dev/vg0/station_data,format=raw,if=virtio",
+          "-drive", "file=/dev/vg0/docker,format=raw,if=virtio",
+          #"-hdb", "fat:./local/imds",
+          "-smbios",
+          "type=1,serial=ds=nocloud-net;i=${NOMAD_SHORT_ALLOC_ID};h=${NOMAD_SHORT_ALLOC_ID}-client;s=http://${NOMAD_ADDR_http}/",
+          "-cpu", "host",
+          "-smp", "4",
+          #"-serial", "telnet:localhost:4321,server,nowait"
+          "-overcommit", "mem-lock=on",
+          "-overcommit", "cpu-pm=on",
+          #"-chardev", "socket,id=char0,path=/tmp/vhostqemu",
+          #"-device", "vhost-user-fs-pci,queue-size=1024,chardev=char0,tag=docker_volumes",
+          #"-object", "memory-backend-file,id=mem,size=60000,mem-path=/dev/shm,share=on",
+          #"-numa", "node,memdev=mem"
+
+        ]
+      }
+
+      resources {
+        memory = 60000
+        cpu    = 18000
+      }
+    }
+    task "cleanup-image" {
+      lifecycle {
+        hook = "poststop"
+      }
+
+      driver = "exec"
+
+      config {
+        command = "rm"
+        args    = ["images/disk-${NOMAD_SHORT_ALLOC_ID}.qcow2"]
+      }
+
+      volume_mount {
+        volume           = "images"
+        destination      = "images"
+        propagation_mode = "host-to-task"
+      }
+    }
+  }
+}
diff --git a/infra/jobs/station/object-storage.levant.nomad b/infra/jobs/station/object-storage.levant.nomad
new file mode 100644
index 0000000000000000000000000000000000000000..00cd9701f4e92d2813cc6326b3b66888d37184ea
--- /dev/null
+++ b/infra/jobs/station/object-storage.levant.nomad
@@ -0,0 +1,115 @@
+job "object-storage" {
+  datacenters = ["stat"]
+  type        = "service"
+
+  group "minio" {
+    count = 1
+
+    network {
+      mode = "bridge"
+      port "s3" {
+        static = 9000
+        to     = 9000
+      }
+      port "console" {
+        static = 9001
+        to     = 9001
+      }
+      port "metrics" {
+        to = 9598
+      }
+    }
+
+    volume "minio" {
+      type      = "host"
+      read_only = false
+      source    = "object-storage-data"
+    }
+
+    service {
+      tags = ["scrape"]
+      name = "s3"
+      port = "s3"
+      task = "minio"
+
+      meta {
+        metrics_address = "${NOMAD_ADDR_metrics}"
+      }
+    }
+    service {
+      name = "s3-console"
+      port = "console"
+      task = "minio"
+    }
+
+    task "minio" {
+      driver = "docker"
+
+      volume_mount {
+        volume      = "minio"
+        destination = "/data"
+        read_only   = false
+      }
+
+      config {
+        image   = "minio/minio:[[.object_storage.minio.version]]"
+        ports   = ["s3", "console"]
+        command = "server"
+        args    = ["--console-address", ":9001", "/data"]
+      }
+
+      env {
+        MINIO_ROOT_USER            = "[[.object_storage.user.name]]"
+        MINIO_ROOT_PASSWORD        = "[[.object_storage.user.pass]]"
+        MINIO_PROMETHEUS_AUTH_TYPE = "public"
+        #MINIO_BROWSER_REDIRECT_URL = "http://[[.station]]c.control.lofar/minio"
+      }
+
+      resources {
+        cpu    = 250
+        memory = 512
+      }
+    }
+
+    task "vector" {
+      driver = "docker"
+      config {
+        image = "timberio/vector:0.32.1.custom.989ad14-distroless-static"
+        ports = ["metrics"]
+      }
+      # Vector won't start unless the sinks(backends) configured are healthy
+      env {
+        VECTOR_CONFIG          = "local/vector.toml"
+        VECTOR_REQUIRE_HEALTHY = "true"
+      }
+      # resource limits are a good idea because you don't want your log collection to consume all resources available
+      resources {
+        cpu    = 10 # 500 MHz
+        memory = 50 # 256MB
+      }
+      # template with Vector's configuration
+      template {
+        destination     = "local/vector.toml"
+        change_mode     = "signal"
+        change_signal   = "SIGHUP"
+        left_delimiter  = "(("
+        right_delimiter = "))"
+        data            = <<EOF
+data_dir                     = "alloc/data/vector/"
+healthchecks.require_healthy = true
+[sources.cluster_metrics]
+  type                 = "prometheus_scrape"
+  scrape_interval_secs = 60
+  endpoints            = [ "http://localhost:9000/minio/v2/metrics/cluster" ]
+[sources.bucket_metrics]
+  type                 = "prometheus_scrape"
+  scrape_interval_secs = 60
+  endpoints            = [ "http://localhost:9000/minio/v2/metrics/bucket" ]
+[sinks.prometheus_exporter]
+  type                 = "prometheus_exporter"
+  inputs               = [ "cluster_metrics", "bucket_metrics" ]
+EOF
+      }
+    }
+  }
+}
diff --git a/infra/jobs/station/sdptr.levant.nomad b/infra/jobs/station/sdptr.levant.nomad
new file mode 100644
index 0000000000000000000000000000000000000000..127d390d44d448eb679c05ed49400d55a681e858
--- /dev/null
+++ b/infra/jobs/station/sdptr.levant.nomad
@@ -0,0 +1,43 @@
+job "sdptr" {
+    datacenters = ["stat"]
+    type        = "service"
+
+    [[ range $name, $tr := $.sdptr ]]
+        group "[[ $name ]]" {
+            count = 1
+            network {
+                mode = "bridge"
+                port "sdptr" {
+                    static = [[ $tr.port ]]
+                    to     = [[ $tr.port ]]
+                }
+            }
+
+            service {
+                name = "sdptr-[[ $name ]]"
+                port = "sdptr"
+            }
+
+            task "translator" {
+                driver = "docker"
+
+
+                config {
+                    image   = "git.astron.nl:5000/lofar2.0/sdptr:latest"
+                    ports   = ["sdptr"]
+                    command = "/usr/local/bin/sdptr-[[ $name ]]"
+                    args    = [
+                        "--port", "[[ $tr.port ]]",
+                        "--first_gn", "[[ $tr.first_gn ]]",
+                        "--fpgas", "[[ $tr.fpgas ]]", "--nodaemon"
+                    ]
+                }
+            }
+            resources {
+                cpu    = 500
+                memory = 100
+            }
+
+        }
+    [[ end ]]
+}
diff --git a/infra/jobs/station/tango-prometheus-exporter.levant.nomad b/infra/jobs/station/tango-prometheus-exporter.levant.nomad
new file mode 100644
index 0000000000000000000000000000000000000000..6f65141cb3cf10f141bc945d245451d6ccc27986
--- /dev/null
+++ b/infra/jobs/station/tango-prometheus-exporter.levant.nomad
@@ -0,0 +1,103 @@
+job "tango-prometheus-exporter" {
+  datacenters = ["stat"]
+  type        = "service"
+
+  group "tango-prometheus-exporter" {
+    count = 1
+
+    network {
+      mode = "bridge"
+      port "http" {
+        to = 8000
+      }
+    }
+
+    service {
+      name = "tango-prometheus-exporter"
+      port = "http"
+    }
+
+    task "exporter" {
+      driver = "docker"
+
+      config {
+        image   = "git.astron.nl:5000/lofar2.0/tango/tango-prometheus-exporter:[[.image_tag]]"
+        ports   = ["http"]
+        command = "--config=/code/lofar2-policy.json"
+      }
+      resources {
+        cpu    = 100
+        memory = 100
+      }
+      env {
+        TANGO_HOST = "tango.service.consul:10000"
+      }
+    }
+  }
+
+  group "tango-prometheus-fast-exporter" {
+    count = 1
+
+    network {
+      mode = "bridge"
+      port "http" {
+        to = 8000
+      }
+    }
+
+    service {
+      name = "tango-prometheus-fast-exporter"
+      port = "http"
+    }
+
+    task "exporter" {
+      driver = "docker"
+
+      config {
+        image   = "git.astron.nl:5000/lofar2.0/tango/tango-prometheus-exporter:[[.image_tag]]"
+        ports   = ["http"]
+        command = "--config=/code/lofar2-fast-policy.json"
+      }
+      resources {
+        cpu    = 100
+        memory = 100
+      }
+      env {
+        TANGO_HOST = "tango.service.consul:10000"
+      }
+    }
+  }
+
+  group "tango-prometheus-slow-exporter" {
+    count = 1
+
+    network {
+      mode = "bridge"
+      port "http" {
+        to = 8000
+      }
+    }
+
+    service {
+      name = "tango-prometheus-slow-exporter"
+      port = "http"
+    }
+
+    task "exporter" {
+      driver = "docker"
+
+      config {
+        image   = "git.astron.nl:5000/lofar2.0/tango/tango-prometheus-exporter:[[.image_tag]]"
+        ports   = ["http"]
+        command = "--config=/code/lofar2-slow-policy.json"
+      }
+      resources {
+        cpu    = 100
+        memory = 100
+      }
+      env {
+        TANGO_HOST = "tango.service.consul:10000"
+      }
+    }
+  }
+}
diff --git a/infra/jobs/station/tango.levant.nomad b/infra/jobs/station/tango.levant.nomad
new file mode 100644
index 0000000000000000000000000000000000000000..0003c4106306725bbe706fa22974d7aab20339c7
--- /dev/null
+++ b/infra/jobs/station/tango.levant.nomad
@@ -0,0 +1,164 @@
+job "tango" {
+  datacenters = ["stat"]
+  type        = "service"
+
+  group "database" {
+    count = 1
+
+    restart {
+      attempts = 10
+      interval = "5m"
+      delay    = "25s"
+      mode     = "delay"
+    }
+
+    network {
+      mode = "bridge"
+      port "mysql" {
+        # should be migrated to port 3000 when fully replaces docker-compose setup
+        static = 3306
+        to     = 3306
+      }
+    }
+
+    volume "database" {
+      type      = "host"
+      read_only = false
+      source    = "tango-database"
+    }
+
+    service {
+      name = "tangodb"
+      port = "mysql"
+
+      check {
+        type     = "tcp"
+        interval = "10s"
+        timeout  = "2s"
+      }
+    }
+
+    task "database" {
+      driver = "docker"
+
+      volume_mount {
+        volume      = "database"
+        destination = "/var/lib/mysql"
+        read_only   = false
+      }
+
+      config {
+        image = "git.astron.nl:5000/lofar2.0/tango/tango-db:[[.tango.db.version]]"
+        ports = ["mysql"]
+      }
+
+      env {
+        MYSQL_ROOT_PASSWORD = "secret"
+        MYSQL_DATABASE      = "tango"
+        MYSQL_USER          = "tango"
+        MYSQL_PASSWORD      = "tango"
+      }
+
+      resources {
+        cpu    = 1024
+        memory = 512
+      }
+    }
+  }
+
+  group "device-server" {
+    count = 1
+
+    restart {
+      attempts = 10
+      interval = "10m"
+      delay    = "60s"
+      mode     = "delay"
+    }
+
+    network {
+      mode = "bridge"
+      port "tango" {
+        static = 10000
+        to     = 10000
+      }
+    }
+
+    service {
+      name = "tango"
+      port = "tango"
+
+      check {
+        type     = "tcp"
+        interval = "10s"
+        timeout  = "20s"
+      }
+    }
+
+    task "wait-for-db" {
+      lifecycle {
+        hook    = "prestart"
+        sidecar = false
+      }
+      driver = "docker"
+
+      config {
+        image   = "busybox"
+        command = "sh"
+        args    = ["-c", "while ! nc -z $MYSQL_HOST $MYSQL_PORT; do sleep 1; done"]
+      }
+
+      template {
+        data        = <<EOH
+            {{ range service "tangodb" }}
+            MYSQL_HOST     = "{{ .Address }}"
+            MYSQL_PORT     = "{{ .Port }}"
+            {{ end }}
+            EOH
+        destination = "local/env.txt"
+        env         = true
+      }
+    }
+
+    task "database-ds" {
+      driver = "docker"
+
+
+      config {
+        image      = "git.astron.nl:5000/lofar2.0/tango/tango-databaseds:[[.tango.databaseds.version]]"
+        ports      = ["tango"]
+        entrypoint = [
+          "/usr/local/bin/DataBaseds",
+          "2",
+          "-ORBendPoint",
+          "giop:tcp:0.0.0.0:10000",
+          "-ORBendPointPublish",
+          "giop:tcp:tango.service.consul:10000"
+        ]
+      }
+
+      env {
+        MYSQL_DATABASE = "tango"
+        MYSQL_USER     = "tango"
+        MYSQL_PASSWORD = "tango"
+        TANGO_HOST     = "tango.service.consul:10000"
+      }
+
+      template {
+        data        = <<EOH
+        {{ range service "tangodb" }}
+        MYSQL_HOST     = "{{ .Address }}:{{ .Port }}"
+        {{ end }}
+        EOH
+        destination = "local/env.txt"
+        env         = true
+      }
+
+
+      resources {
+        cpu    = 1024
+        memory = 512
+      }
+    }
+  }
+}
diff --git a/infra/qemu-test.pkr.hcl b/infra/qemu-test.pkr.hcl
deleted file mode 100644
index 9ee48e24fdef94ac6d67b88dfb47fbd738a38bb6..0000000000000000000000000000000000000000
--- a/infra/qemu-test.pkr.hcl
+++ /dev/null
@@ -1,459 +0,0 @@
-packer {
-  required_version = ">= 1.7.0, < 2.0.0"
-
-  required_plugins {
-    qemu = {
-      source  = "github.com/hashicorp/qemu"
-      version = ">= 1.0.0, < 2.0.0"
-    }
-  }
-}
-
-variable "apt_cache_url" {
-  type    = string
-  default = "http://myserver:3142"
-}
-
-variable "boot_wait" {
-  type    = string
-  default = "3s"
-}
-
-variable "bundle_iso" {
-  type    = string
-  default = "false"
-}
-
-variable "communicator" {
-  type    = string
-  default = "ssh"
-}
-
-variable "country" {
-  type    = string
-  default = "CA"
-}
-
-variable "cpus" {
-  type    = string
-  default = "1"
-}
-
-variable "description" {
-  type    = string
-  default = "Base box for x86_64 Debian Bookworm 12.x"
-}
-
-variable "disk_size" {
-  type    = string
-  default = "7500"
-}
-
-variable "domain" {
-  type    = string
-  default = ""
-}
-
-variable "guest_os_type" {
-  type    = string
-  default = "Debian_64"
-}
-
-variable "headless" {
-  type    = string
-  default = "false"
-}
-
-variable "host_port_max" {
-  type    = string
-  default = "4444"
-}
-
-variable "host_port_min" {
-  type    = string
-  default = "2222"
-}
-
-variable "http_port_max" {
-  type    = string
-  default = "9000"
-}
-
-variable "http_port_min" {
-  type    = string
-  default = "8000"
-}
-
-variable "iso_checksum" {
-  type    = string
-  default = "sha512:b462643a7a1b51222cd4a569dad6051f897e815d10aa7e42b68adc8d340932d861744b5ea14794daa5cc0ccfa48c51d248eda63f150f8845e8055d0a5d7e58e6"
-  # default = "file:http://cdimage.debian.org/cdimage/release/current/amd64/iso-cd/SHA512SUMS"
-}
-
-variable "iso_file" {
-  type    = string
-  default = "debian-12.0.0-amd64-netinst.iso"
-}
-
-variable "iso_path_external" {
-  type    = string
-  default = "http://cdimage.debian.org/cdimage/release/current/amd64/iso-cd"
-}
-
-variable "iso_path_internal" {
-  type    = string
-  default = "http://myserver:8080/debian"
-}
-
-variable "keep_registered" {
-  type    = string
-  default = "false"
-}
-
-variable "keyboard" {
-  type    = string
-  default = "us"
-}
-
-variable "language" {
-  type    = string
-  default = "en"
-}
-
-variable "locale" {
-  type    = string
-  default = "en_CA.UTF-8"
-}
-
-variable "memory" {
-  type    = string
-  default = "1024"
-}
-
-variable "mirror" {
-  type    = string
-  default = "ftp.ca.debian.org"
-}
-
-variable "packer_cache_dir" {
-  type    = string
-  default = "${env("PACKER_CACHE_DIR")}"
-}
-
-variable "preseed_file" {
-  type    = string
-  default = "base.preseed"
-}
-
-variable "qemu_binary" {
-  type    = string
-  default = "qemu-system-x86_64"
-}
-
-variable "shutdown_timeout" {
-  type    = string
-  default = "5m"
-}
-
-variable "skip_export" {
-  type    = string
-  default = "false"
-}
-
-variable "ssh_agent_auth" {
-  type    = string
-  default = "false"
-}
-
-variable "ssh_clear_authorized_keys" {
-  type    = string
-  default = "false"
-}
-
-variable "ssh_disable_agent_forwarding" {
-  type    = string
-  default = "false"
-}
-
-variable "ssh_file_transfer_method" {
-  type    = string
-  default = "scp"
-}
-
-variable "ssh_fullname" {
-  type    = string
-  default = "Ghost Writer"
-}
-
-variable "ssh_handshake_attempts" {
-  type    = string
-  default = "10"
-}
-
-variable "ssh_keep_alive_interval" {
-  type    = string
-  default = "5s"
-}
-
-variable "ssh_password" {
-  type    = string
-  default = "1ma63b0rk3d"
-}
-
-variable "ssh_port" {
-  type    = string
-  default = "22"
-}
-
-variable "ssh_pty" {
-  type    = string
-  default = "false"
-}
-
-variable "ssh_timeout" {
-  type    = string
-  default = "60m"
-}
-
-variable "ssh_username" {
-  type    = string
-  default = "ghost"
-}
-
-variable "start_retry_timeout" {
-  type    = string
-  default = "5m"
-}
-
-variable "system_clock_in_utc" {
-  type    = string
-  default = "true"
-}
-
-variable "timezone" {
-  type    = string
-  default = "UTC"
-}
-
-variable "vm_name" {
-  type    = string
-  default = "base-bookworm"
-}
-
-variable "vnc_vrdp_bind_address" {
-  type    = string
-  default = "127.0.0.1"
-}
-
-variable "vnc_vrdp_port_max" {
-  type    = string
-  default = "6000"
-}
-
-variable "vnc_vrdp_port_min" {
-  type    = string
-  default = "5900"
-}
-
-# The "legacy_isotime" function has been provided for backwards compatability,
-# but we recommend switching to the timestamp and formatdate functions.
-
-locals {
-  #output_directory = "build/${legacy_isotime("2006-01-02-15-04-05")}"
-  output_directory = "build/station"
-}
-
-source "qemu" "iso" {
-  accelerator  = "kvm"
-  boot_command = [
-    "<wait><wait><wait><esc><wait><wait><wait>",
-    "/install.amd/vmlinuz ",
-    "initrd=/install.amd/initrd.gz ",
-    "auto=true ",
-    "url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/${var.preseed_file} ",
-    "hostname=${var.vm_name} ",
-    "domain=${var.domain} ",
-    "interface=auto ",
-    "vga=788 noprompt quiet --<enter>"
-  ]
-  boot_wait            = var.boot_wait
-  communicator         = var.communicator
-  cpus                 = var.cpus
-  disk_cache           = "writeback"
-  disk_compression     = false
-  disk_discard         = "ignore"
-  disk_image           = false
-  disk_interface       = "virtio-scsi"
-  disk_size            = var.disk_size
-  format               = "raw"
-  headless             = var.headless
-  host_port_max        = var.host_port_max
-  host_port_min        = var.host_port_min
-  http_content         = { "/${var.preseed_file}" = templatefile(var.preseed_file, { var = var }) }
-  http_port_max        = var.http_port_max
-  http_port_min        = var.http_port_min
-  iso_checksum         = var.iso_checksum
-  iso_skip_cache       = false
-  iso_target_extension = "iso"
-  iso_target_path      = "${regex_replace(var.packer_cache_dir, "^$", "/tmp")}/${var.iso_file}"
-  iso_urls             = [
-    "${var.iso_path_internal}/${var.iso_file}",
-    "${var.iso_path_external}/${var.iso_file}"
-  ]
-  machine_type                 = "pc"
-  memory                       = var.memory
-  net_device                   = "virtio-net"
-  output_directory             = "build/debian-bookworm-amd64-qemu-iso-output"
-  qemu_binary                  = var.qemu_binary
-  shutdown_command             = "echo '${var.ssh_password}' | sudo -E -S poweroff"
-  shutdown_timeout             = var.shutdown_timeout
-  skip_compaction              = true
-  skip_nat_mapping             = false
-  ssh_agent_auth               = var.ssh_agent_auth
-  ssh_clear_authorized_keys    = var.ssh_clear_authorized_keys
-  ssh_disable_agent_forwarding = var.ssh_disable_agent_forwarding
-  ssh_file_transfer_method     = var.ssh_file_transfer_method
-  ssh_handshake_attempts       = var.ssh_handshake_attempts
-  ssh_keep_alive_interval      = var.ssh_keep_alive_interval
-  ssh_password                 = var.ssh_password
-  ssh_port                     = var.ssh_port
-  ssh_pty                      = var.ssh_pty
-  ssh_timeout                  = var.ssh_timeout
-  ssh_username                 = var.ssh_username
-  use_default_display          = false
-  vm_name                      = var.vm_name
-  vnc_bind_address             = var.vnc_vrdp_bind_address
-  vnc_port_max                 = var.vnc_vrdp_port_max
-  vnc_port_min                 = var.vnc_vrdp_port_min
-}
-source "qemu" "img" {
-  accelerator      = "kvm"
-  boot_wait        = var.boot_wait
-  communicator     = var.communicator
-  cpus             = var.cpus
-  disk_cache       = "writeback"
-  disk_compression = true
-  disk_discard     = "ignore"
-  disk_image       = true
-  disk_interface   = "virtio-scsi"
-  disk_size        = var.disk_size
-  format           = "qcow2"
-  headless         = var.headless
-  host_port_max    = var.host_port_max
-  host_port_min    = var.host_port_min
-  http_content     = { "/${var.preseed_file}" = templatefile(var.preseed_file, { var = var }) }
-  http_port_max    = var.http_port_max
-  http_port_min    = var.http_port_min
-  iso_checksum     = "none"
-  iso_urls         = [
-    "./build/debian-bookworm-amd64-qemu-iso-output/base-bookworm"
-  ]
-  machine_type                 = "pc"
-  memory                       = var.memory
-  net_device                   = "virtio-net"
-  output_directory             = local.output_directory
-  qemu_binary                  = var.qemu_binary
-  shutdown_command             = "echo '${var.ssh_password}' | sudo -E -S poweroff"
-  shutdown_timeout             = var.shutdown_timeout
-  skip_compaction              = true
-  skip_nat_mapping             = false
-  ssh_agent_auth               = var.ssh_agent_auth
-  ssh_clear_authorized_keys    = var.ssh_clear_authorized_keys
-  ssh_disable_agent_forwarding = var.ssh_disable_agent_forwarding
-  ssh_file_transfer_method     = var.ssh_file_transfer_method
-  ssh_handshake_attempts       = var.ssh_handshake_attempts
-  ssh_keep_alive_interval      = var.ssh_keep_alive_interval
-  ssh_password                 = var.ssh_password
-  ssh_port                     = var.ssh_port
-  ssh_pty                      = var.ssh_pty
-  ssh_timeout                  = var.ssh_timeout
-  ssh_username                 = var.ssh_username
-  use_default_display          = false
-  vm_name                      = var.vm_name
-  vnc_bind_address             = var.vnc_vrdp_bind_address
-  vnc_port_max                 = var.vnc_vrdp_port_max
-  vnc_port_min                 = var.vnc_vrdp_port_min
-}
-
-build {
-  name = "iso"
-
-  sources = ["source.qemu.iso"]
-}
-
-build {
-  name        = "img"
-  description = "Can't use variables here yet!"
-
-  sources = ["source.qemu.img"]
-
-  provisioner "shell" {
-    execute_command   = "echo '${var.ssh_password}' | sudo -S -E bash '{{ .Path }}'"
-    expect_disconnect = true
-    inline            = [
-      "echo Adding file to Docker Container",
-      "echo '${var.ssh_username} ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers.d/99${var.ssh_username}",
-      "chmod 0440 /etc/sudoers.d/99${var.ssh_username}"
-    ]
-  }
-
-  provisioner "shell" {
-    inline = [
-      "sudo apt-get update",
-      "sudo apt-get --yes dist-upgrade",
-      "sudo apt-get clean"
-    ]
-    inline_shebang = "/bin/sh -e"
-  }
-
-  provisioner "shell" {
-    inline = [
-      "sudo apt-get update && sudo apt-get install -y wget gpg coreutils lsb-release",
-      "wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg",
-      "echo \"deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main\" | sudo tee /etc/apt/sources.list.d/hashicorp.list",
-      "sudo apt-get update && sudo apt-get install -y nomad consul",
-      "sudo apt-get clean"
-    ]
-    inline_shebang = "/bin/sh -e"
-  }
-
-  provisioner "shell" {
-    inline = [
-      "sudo apt-get install -y cloud-init",
-      "sudo apt-get clean"
-    ]
-    inline_shebang = "/bin/sh -e"
-  }
-
-  provisioner "shell" {
-    inline = [
-      "echo \"policy: enabled\" | sudo tee /etc/cloud/ds-identity.cfg",
-      "echo \"datasource_list: [ ConfigDrive, NoCloud, None ]\ndatasource:\n  ConfigDrive:\n    dsmode: local\n\" | sudo tee /etc/cloud/cloud.cfg.d/00-sources.cfg"
-    ]
-    inline_shebang = "/bin/sh -e"
-  }
-
-  provisioner "shell" {
-    inline = [
-      "sudo systemctl enable cloud-init-local.service",
-      "sudo systemctl start cloud-init-local.service",
-      "sudo systemctl enable cloud-init.service",
-      "sudo systemctl start cloud-init.service",
-      "sudo systemctl enable cloud-config.service",
-      "sudo systemctl start cloud-config.service",
-      "sudo systemctl enable cloud-final.service",
-      "sudo systemctl start cloud-final.service",
-      "sudo cloud-init init --local",
-      "sudo rm -rf /var/lib/cloud"
-    ]
-    inline_shebang = "/bin/sh -e"
-  }
-
-  post-processor "compress" {
-    compression_level   = 6
-    format              = ".gz"
-    keep_input_artifact = true
-    only                = ["qemu"]
-    output              = "${local.output_directory}/${var.vm_name}.raw.gz"
-  }
-}
diff --git a/infra/start-cloud-init.sh b/infra/start-cloud-init.sh
deleted file mode 100755
index 56a81672e755ebf272a391d4859624492a6fd58e..0000000000000000000000000000000000000000
--- a/infra/start-cloud-init.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-#
-# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
-#
-
-(
-  cd imds || exit
-  cloud-localds my-seed.img user-data meta-data
-)
-
-qemu-img create -f qcow2 -b ./debian-12-genericcloud-amd64.qcow2 -F qcow2 boot-disk.img
-qemu-system-x86_64 -machine accel=kvm:tcg -cpu host -m 1G -drive file=boot-disk.img,if=virtio -netdev user,id=n0 -nic user,model=virtio-net-pci -drive file=./imds/my-seed.img,if=virtio,format=raw -smbios type=1,serial=ds='nocloud'
diff --git a/infra/station-config/consul.hcl b/infra/station-config/consul.hcl
deleted file mode 100644
index 60653b0021cb748b33a9c7d5f65fbc406b1b5b8e..0000000000000000000000000000000000000000
--- a/infra/station-config/consul.hcl
+++ /dev/null
@@ -1,57 +0,0 @@
-# Full configuration options can be found at https://www.consul.io/docs/agent/config
-
-datacenter = "nl-cs-001"
-data_dir   = "/localdata/consul"
-node_name  = "lcu"
-server     = true
-bootstrap  = true
-
-bind_addr = "0.0.0.0"
-
-client_addr = "127.0.0.1 10.151.255.1"
-
-advertise_addr = "10.151.255.1"
-
-# ui
-ui_config {
-  enabled = true
-}
-
-tls {
-  defaults {
-    ca_path   = "/etc/consul.d"
-    ca_file   = "/etc/consul.d/consul-agent-ca.pem"
-    cert_file = "/etc/consul.d/nl-cs-001-server-consul-0.pem"
-    key_file  = "/etc/consul.d/nl-cs-001-server-consul-0-key.pem"
-
-    verify_incoming = true
-    verify_outgoing = true
-  }
-  internal_rpc {
-    verify_server_hostname = true
-  }
-}
-
-telemetry {
-  prometheus_retention_time = "24h"
-}
-
-ports {
-  grpc     = 8502
-  grpc_tls = 8503
-}
-
-connect {
-  enabled = true
-}
-
-auto_encrypt {
-  allow_tls = true
-}
-performance {
-  raft_multiplier = 1
-}
-
-bootstrap_expect = 1
-
-encrypt = "..."
diff --git a/infra/station-config/nomad.hcl b/infra/station-config/nomad.hcl
deleted file mode 100644
index 709db4bf0ce3117ea3b336784befc63f5f9f7407..0000000000000000000000000000000000000000
--- a/infra/station-config/nomad.hcl
+++ /dev/null
@@ -1,59 +0,0 @@
-# Full configuration options can be found at https://www.nomadproject.io/docs/configuration
-
-data_dir   = "/localdata/nomad/data"
-bind_addr  = "0.0.0.0"
-name       = "lcu"
-region     = "nl-cs-001"
-datacenter = "stat"
-
-server {
-  # license_path is required for Nomad Enterprise as of Nomad v1.1.1+
-  #license_path = "/etc/nomad.d/license.hclic"
-  enabled          = true
-  bootstrap_expect = 1
-  encrypt          = "..."
-}
-
-client {
-  enabled  = true
-  servers  = ["127.0.0.1"]
-  cni_path = "/localdata/cni/bin"
-
-  host_volume "monitoring-postgresql-data" {
-    path = "/localdata/volumes/monitoring-postgresql-data"
-  }
-
-  host_volume "monitoring-loki-data" {
-    path = "/localdata/volumes/monitoring-loki-data"
-  }
-
-  host_volume "monitoring-prometheus-data" {
-    path = "/localdata/volumes/monitoring-prometheus-data"
-  }
-}
-
-telemetry {
-  collection_interval        = "1s"
-  disable_hostname           = true
-  prometheus_metrics         = true
-  publish_allocation_metrics = true
-  publish_node_metrics       = true
-}
-
-consul {
-  address             = "127.0.0.1:8500"
-  server_service_name = "nomad"
-  client_service_name = "nomad-client"
-  auto_advertise      = true
-  server_auto_join    = true
-  client_auto_join    = true
-}
-
-plugin "docker" {
-  config {
-    volumes {
-      enabled = true
-    }
-    allow_caps = ["all"]
-  }
-}
diff --git a/sbin/dsconfig.sh b/sbin/dsconfig.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a51b6c61351ea78b968a0c8e882ac44dd107955c
--- /dev/null
+++ b/sbin/dsconfig.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+#
+# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# defaults
+network="station"
+
+# list of arguments expected in the input
+optstring_long="help,load,update,dump,file,network::"
+optstring="hludf:n::"
+
+options=$(getopt -l ${optstring_long} -o ${optstring} -- "$@")
+
+eval set -- "$options"
+
+
+while true; do
+  case ${1} in
+    -h|--help)
+      usage
+      exit 0
+      ;;
+    -l|--load)
+      echo "Load config"
+      action="load"
+      ;;
+    -u|--update)
+      echo "Update config"
+      action="update"
+      ;;
+    -d|--dump)
+      echo "Dump config"
+      action="dump"
+      ;;
+    -n|--network)
+      shift
+      network="$1"
+      ;;
+    --)
+    shift
+    break;;
+  esac
+  shift
+done
+
+if [ -z "$TAG" ]; then
+  TAG="latest"
+fi
+
+echo "Use docker network '$network'"
+docker_args=(run --rm  -e "TANGO_HOST=$TANGO_HOST" --network="$network" -i)
+docker_image="git.astron.nl:5000/lofar2.0/tango/dsconfig:$TAG"
+
+if [[ -n "$DNS" ]]; then
+  echo "Set docker dns to $DNS"
+  docker_args+=(--dns "$DNS")
+fi
+
+if [[ "$action" != "dump" ]]; then
+
+  if [ ${#} -eq 1 ]; then
+      file=$(realpath "${1}")
+  else
+      echo "A file name must be provided."
+      exit 1
+  fi
+
+  cmd_args=(--write)
+  docker_args+=(-v "${file}":/tmp/dsconfig-update-settings.json)
+
+  if [[ "$action" == "update" ]]; then
+    "${LOFAR20_DIR}/sbin/dsconfig.sh" --dump > "${LOFAR20_DIR}"/CDB/dump_"$(date "+%Y.%m.%d_%H.%M.%S")".json
+
+    # update settings, Do not change -i into -it this will break integration tests in gitlab ci!
+    docker "${docker_args[@]}" "$docker_image" \
+      sh -c '/manage_object_properties.py --write < /tmp/dsconfig-update-settings.json'
+    cmd_args+=(--update)
+  fi
+
+
+  # update settings, Do not change -i into -it this will break integration tests in gitlab ci!
+  docker "${docker_args[@]}" "$docker_image" \
+    json2tango "${cmd_args[@]}" /tmp/dsconfig-update-settings.json
+
+  # somehow json2tango does not return 0 on success
+  exit 0
+fi
+
+# writes the JSON dump to stdout, Do not change -i into -it incompatible with gitlab ci!
+docker "${docker_args[@]}" "$docker_image" \
+  bash -c '
+  python -m dsconfig.dump > /tmp/dsconfig-configdb-dump.json
+  /manage_object_properties.py -r > /tmp/dsconfig-objectdb-dump.json
+  /merge_json.py /tmp/dsconfig-objectdb-dump.json /tmp/dsconfig-configdb-dump.json'
diff --git a/sbin/load_ConfigDb.sh b/sbin/load_ConfigDb.sh
index 707eee508b7dd0019775037e95b6a31e0a41d00b..cc7c8b254f5cd4f56e9bf71fa2fa116b703c405c 100755
--- a/sbin/load_ConfigDb.sh
+++ b/sbin/load_ConfigDb.sh
@@ -1,20 +1,7 @@
 #!/bin/bash
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
+#
+# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
 # SPDX-License-Identifier: Apache-2.0
+#
 
-if [ ${#} -eq 1 ]; then
-    file=${1}
-else
-    echo "A file name must be provided."
-    exit 1
-fi
-
-# copy file into container to read it from container, as the file's location
-# in the container won't be the same as on the host.
-docker cp "${file}" dsconfig:/tmp/dsconfig-update-settings.json || exit 1
-
-# update settings, Do not change -i into -it this will break integration tests in gitlab ci!
-docker exec -i dsconfig json2tango --write /tmp/dsconfig-update-settings.json
-
-# somehow json2tango does not return 0 on success
-exit 0
+"${LOFAR20_DIR}/sbin/dsconfig.sh" --load "${1}"
diff --git a/sbin/prepare_dev_env.sh b/sbin/prepare_dev_env.sh
new file mode 100755
index 0000000000000000000000000000000000000000..080ceb4725570870313225300b5730364c74bee4
--- /dev/null
+++ b/sbin/prepare_dev_env.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+#
+# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Url to the jumppad download location
+jumppad_download="https://git.astron.nl/lofar2.0/tango/-/package_files/37347/download"
+
+if [ -z "$LOFAR20_DIR" ]; then
+    # We assume we aren't in the PATH, so we can derive our path.
+    # We need our parent directory.
+    LOFAR20_DIR_RELATIVE=$(dirname "$0")/..
+
+    # As an absolute path
+    LOFAR20_DIR=$(readlink -f "${LOFAR20_DIR_RELATIVE}")
+fi
+
+echo 'Check if jumppad is installed'
+mkdir -p ./.bin
+bin_dir=$(realpath ./.bin)
+export PATH="$PATH:$bin_dir"
+
+if ! [ -x "$(command -v jumppad)" ]; then
+  echo 'jumppad is not installed, installing'
+  mkdir -p ./.bin
+  wget "${jumppad_download}" -O ./.bin/jumppad
+  chmod +x ./.bin/jumppad
+fi
+
+make -C infra/jobs/station DIR_OUT="$( realpath "infra/dev/jobs/station")" render
+
+docker_volume="dev_nomad_station"
+
+# list of arguments expected in the input
+optstring_long="help,volume::"
+optstring="hv::"
+
+options=$(getopt -l ${optstring_long} -o ${optstring} -- "$@")
+
+eval set -- "$options"
+
+while true; do
+  case ${1} in
+    -h|--help)
+      usage
+      exit 0
+      ;;
+    -v|--volume)
+      shift
+      docker_volume="$1"
+      ;;
+    --)
+    shift
+    break;;
+  esac
+  shift
+done
+
+if [ "$(docker volume list | grep -c "$docker_volume")" = "0" ]; then
+  echo "Create volume $docker_volume"
+  docker volume create "$docker_volume"
+fi
+
+docker pull -q bash
+docker run --rm -i -v "$docker_volume":/mnt bash bash  <<- EOM
+  mkdir -p /mnt/volumes/tango-database
+  mkdir -p /mnt/volumes/monitoring-postgresql-data
+  mkdir -p /mnt/volumes/monitoring-loki-data
+  mkdir -p /mnt/volumes/monitoring-prometheus-data
+  mkdir -p /mnt/volumes/object-storage-data
+  mkdir -p /mnt/volumes/jupyter-notebooks
+  chmod 0777 -R /mnt/volumes
+EOM
diff --git a/sbin/run_integration_test.sh b/sbin/run_integration_test.sh
index c6ea4a4d4bcd84568928e167cc9638cf7c79bf3a..621d69b2f3165172f36e7e23f713771dd08a426e 100755
--- a/sbin/run_integration_test.sh
+++ b/sbin/run_integration_test.sh
@@ -4,6 +4,9 @@
 # SPDX-License-Identifier: Apache-2.0
 #
 
+export HOSTNAME=192.168.123.1
+export DNS=192.168.123.100
+
 # Usage function explains how parameters are parsed
 function usage {
     echo "./$(basename "$0")
@@ -26,24 +29,24 @@ function integration_test {
   IFS=" " read -r -a configs <<< "${3}"
   for config in "${configs[@]}"; do
     echo "Updating config ${config} ..."
-    bash "${LOFAR20_DIR}"/sbin/update_ConfigDb.sh "${config}"
+    bash "${LOFAR20_DIR}"/sbin/dsconfig.sh --update "${config}"
   done
   if [ -n "${2+x}" ]; then
     # shellcheck disable=SC2145
     echo "make restart ${restarts[@]} ..."
-    make restart "${restarts[@]}"
+    DNS="$DNS" make restart "${restarts[@]}"
     # shellcheck disable=SC2145
     echo "make await ${restarts[@]} ..."
     make await "${restarts[@]}"
   fi
 
   echo "make integration ${1} ..."
-  make integration "${1}"
+  DNS="$DNS" make integration "${1}"
 }
 
 # list of arguments expected in the input
-optstring_long="help,no-build,skip-tests"
-optstring="hnb"
+optstring_long="help,no-build,skip-tests,preserve,save-logs"
+optstring="h"
 
 options=$(getopt -l ${optstring_long} -o ${optstring} -- "$@")
 
@@ -64,6 +67,15 @@ while true; do
       echo "Only setup and configure environment don't run any tests"
       export no_tests=1
       ;;
+    --preserve)
+      echo "Preserve test environment"
+      export preserve=1
+      ;;
+    --save-logs)
+      echo "Save logs after execution"
+      export save_logs=1
+      ;;
+
     --)
     shift
     break;;
@@ -82,23 +94,73 @@ fi
 
 export TANGO_SKIP_BUILD=1
 
-cd "$LOFAR20_DIR/docker-compose" || exit 1
+if [ -z "$TAG" ]; then
+  export TAG="latest"
+fi
 
-# Start the database server first, `-z ${y+x}` is the inverse of `-n ${y}`
+# Build dsconfig first, `-z ${y+x}` is the inverse of `-n ${y}`
 if [ -z "${no_build+x}" ]; then
     rm -rf "${LOFAR20_DIR}/tangostationcontrol/dist"
     cd "${LOFAR20_DIR}/tangostationcontrol" || exit 1
     tox -e build
     cd "$LOFAR20_DIR/docker-compose" || exit 1
-    make build databaseds dsconfig
+    make build dsconfig
+    cd "$LOFAR20_DIR"
 fi
 
-make start databaseds dsconfig
+docker network rm station || true
+
+# prepare a docker volume for nomad
+tmp_volume="test_$(hexdump -n 16 -v -e '/1 "%02X"' /dev/urandom)"
+
+function cleanup {
+  cd "$LOFAR20_DIR"
+  if [ -n "${save_logs}" ]; then
+    mkdir -p log
+    for container in $(docker ps -a --format "{{.Names}}")
+    do
+      echo "Saving log for container $container"
+      docker logs "${container}" >& "log/${container}.log"
+    done
+    bash "${LOFAR20_DIR}"/sbin/dsconfig.sh --dump >& log/dump_ConfigDb.log
+  fi
+  if [ -z "${preserve}" ]; then
+    make stop > /dev/null 2>&1
+    HOME="$JUMPPAD_HOME" jumppad down infra/dev
+    docker volume rm "$tmp_volume" || true
+  fi
+}
+
+trap cleanup EXIT
 
-# Give dsconfig and databaseds time to start
-sleep 5 # dsconfig container must be up and running...
-# shellcheck disable=SC2016
-echo '/usr/local/bin/wait-for-it.sh ${TANGO_HOST} --strict --timeout=300 -- true' | make run dsconfig bash -
+cd "$LOFAR20_DIR" || exit 1
+
+source "${LOFAR20_DIR}"/sbin/prepare_dev_env.sh --volume="$tmp_volume"
+
+if [ -z "$JUMPPAD_HOME" ]; then
+  JUMPPAD_HOME="$HOME"
+fi
+
+rm -rf "$JUMPPAD_HOME/.jumppad/"
+
+HOME="$JUMPPAD_HOME" jumppad up --var="host_volume=$tmp_volume" infra/dev/main.hcl || true
+
+echo -n "Waiting for tango service to become available .."
+until [[ $(dig @127.0.0.1 -p 8600 tango.service.consul +short) ]]; do
+  sleep 2
+  echo -n "."
+done
+echo ". [ok]"
+
+tango_port=$(dig @127.0.0.1 -p 8600 tango.service.consul SRV +short  | awk '{printf "%s",$3}')
+tango_host=$(dig @127.0.0.1 -p 8600 tango.service.consul +short)
+
+export TANGO_HOST="$tango_host:$tango_port"
+
+
+echo "Using tango host $TANGO_HOST"
+
+cd "$LOFAR20_DIR/docker-compose" || exit 1
 
 # Devices list is used to explitly word split when supplied to commands, must
 # disable shellcheck SC2086 for each case.
@@ -111,36 +173,38 @@ SIMULATORS=(sdptr-sim recvh-sim recvl-sim unb2-sim apsct-sim apspu-sim ccd-sim e
 # jupyter is physically large > 2.5gb and overlayfs is really slow.
 
 [ -n "${no_build}" ] || make build "${SIMULATORS[@]}"
-[ -n "${no_build}" ] || make build logstash integration-test http-json-schemas
+[ -n "${no_build}" ] || make build integration-test
 
 # Start and stop sequence
-make stop http-json-schemas
-make stop object-storage init-object-storage
+make stop init-object-storage
 make stop "${DEVICES[@]}" "${SIMULATORS[@]}"
 make stop device-docker # this one does not test well in docker-in-docker
-make stop logstash
 
-make start logstash http-json-schemas object-storage init-object-storage
+DNS="$DNS" make start init-object-storage
 
 # Update the dsconfig
-# Do not remove `bash`, otherwise statement ignored by gitlab ci shell!
-bash "${LOFAR20_DIR}"/sbin/load_ConfigDb.sh "${LOFAR20_DIR}"/CDB/stations/common.json
-bash "${LOFAR20_DIR}"/sbin/update_ConfigDb.sh "${LOFAR20_DIR}"/CDB/stations/l0.json
-bash "${LOFAR20_DIR}"/sbin/update_ConfigDb.sh "${LOFAR20_DIR}"/CDB/stations/l1.json
-bash "${LOFAR20_DIR}"/sbin/update_ConfigDb.sh "${LOFAR20_DIR}"/CDB/stations/lba.json
-bash "${LOFAR20_DIR}"/sbin/update_ConfigDb.sh "${LOFAR20_DIR}"/CDB/stations/h0.json
-bash "${LOFAR20_DIR}"/sbin/update_ConfigDb.sh "${LOFAR20_DIR}"/CDB/stations/hba_core.json
-bash "${LOFAR20_DIR}"/sbin/update_ConfigDb.sh "${LOFAR20_DIR}"/CDB/stations/cs001.json
-bash "${LOFAR20_DIR}"/sbin/update_ConfigDb.sh "${LOFAR20_DIR}"/CDB/stations/testenv_cs001.json
+docker pull -q "git.astron.nl:5000/lofar2.0/tango/dsconfig:$TAG" || docker pull -q "git.astron.nl:5000/lofar2.0/tango/dsconfig:latest" || true
+if [ -n "$(docker image inspect \""git.astron.nl:5000/lofar2.0/tango/dsconfig":"${TAG}"\")" ]; then
+  docker tag "git.astron.nl:5000/lofar2.0/tango/dsconfig:latest" "git.astron.nl:5000/lofar2.0/tango/dsconfig:$TAG"
+fi
+
+bash "${LOFAR20_DIR}"/sbin/dsconfig.sh --load "${LOFAR20_DIR}"/CDB/stations/common.json
+bash "${LOFAR20_DIR}"/sbin/dsconfig.sh --update "${LOFAR20_DIR}"/CDB/stations/l0.json
+bash "${LOFAR20_DIR}"/sbin/dsconfig.sh --update "${LOFAR20_DIR}"/CDB/stations/l1.json
+bash "${LOFAR20_DIR}"/sbin/dsconfig.sh --update "${LOFAR20_DIR}"/CDB/stations/lba.json
+bash "${LOFAR20_DIR}"/sbin/dsconfig.sh --update "${LOFAR20_DIR}"/CDB/stations/h0.json
+bash "${LOFAR20_DIR}"/sbin/dsconfig.sh --update "${LOFAR20_DIR}"/CDB/stations/hba_core.json
+bash "${LOFAR20_DIR}"/sbin/dsconfig.sh --update "${LOFAR20_DIR}"/CDB/stations/cs001.json
+bash "${LOFAR20_DIR}"/sbin/dsconfig.sh --update "${LOFAR20_DIR}"/CDB/stations/testenv_cs001.json
 
 cd "$LOFAR20_DIR/docker-compose" || exit 1
-make start "${SIMULATORS[@]}"
+DNS="$DNS" make start "${SIMULATORS[@]}"
 
 # Give the simulators time to start
 sleep 5
 
 # shellcheck disable=SC2086
-make start "${DEVICES[@]}"
+DNS="$DNS" make start "${DEVICES[@]}"
 
 # Wait for devices to restart
 make await "${DEVICES[@]}"
@@ -151,7 +215,7 @@ fi
 
 # Start the integration test
 cd "$LOFAR20_DIR/docker-compose" || exit 1
-make up integration-test
+DNS="$DNS" make up integration-test
 
 integration_test default
 
@@ -161,4 +225,4 @@ integration_test digitalbeam_performance "device-sdpfirmware device-sdp device-r
 
 integration_test configuration "device-configuration"
 
-make restart "${DEVICES[@]}"
+DNS="$DNS" make restart "${DEVICES[@]}"
diff --git a/sbin/run_service_test.sh b/sbin/run_service_test.sh
old mode 100644
new mode 100755
index 1a357205e3fa2ea3c0f6a6ce24794df45d522f5a..d0718a84bf79a935947b8670ddee965b5389d362
--- a/sbin/run_service_test.sh
+++ b/sbin/run_service_test.sh
@@ -13,10 +13,39 @@ if [ -z "$LOFAR20_DIR" ]; then
     LOFAR20_DIR=$(readlink -f "${LOFAR20_DIR_RELATIVE}")
 fi
 
-export TANGO_SKIP_BUILD=1
-export NO_BASE=1
+docker network rm station || true
 
-cd "$LOFAR20_DIR/docker-compose" || exit 1
+# prepare a docker volume for nomad
+tmp_volume="test_$(hexdump -n 16 -v -e '/1 "%02X"' /dev/urandom)"
 
-make start grafana loki logstash prometheus
-make await grafana loki logstash prometheus
+function cleanup {
+  cd "$LOFAR20_DIR"
+  if [ -n "${save_logs}" ]; then
+    mkdir -p log
+    for container in $(docker ps -a --format "{{.Names}}")
+    do
+      echo "Saving log for container $container"
+      docker logs "${container}" >& "log/${container}.log"
+    done
+    bash "${LOFAR20_DIR}"/sbin/dsconfig.sh --dump >& log/dump_ConfigDb.log
+  fi
+  if [ -z "${preserve}" ]; then
+    make stop > /dev/null 2>&1
+    HOME="$JUMPPAD_HOME" jumppad down
+    docker volume rm "$tmp_volume" || true
+  fi
+}
+
+trap cleanup EXIT
+
+cd "$LOFAR20_DIR" || exit 1
+
+source "${LOFAR20_DIR}"/sbin/prepare_dev_env.sh --volume="$tmp_volume"
+
+if [ -z "$JUMPPAD_HOME" ]; then
+  JUMPPAD_HOME="$HOME"
+fi
+
+rm -rf "$JUMPPAD_HOME/.jumppad/"
+
+HOME="$JUMPPAD_HOME" jumppad up --var="host_volume=$tmp_volume" infra/dev/services.hcl || true
diff --git a/sbin/tag_and_push_docker_image.sh b/sbin/tag_and_push_docker_image.sh
index 63daae8ef2650ef9b0a4264744f438bb3be24e57..3fc5527eb9defabd74258b8d9d2a06fd695eab06 100755
--- a/sbin/tag_and_push_docker_image.sh
+++ b/sbin/tag_and_push_docker_image.sh
@@ -67,9 +67,9 @@ REMOTE_IMAGES=(
 # integration tests.
 # TODO(Corne): Have this list generated from the .yml files
 LOCAL_IMAGES=(
-  "logstash logstash y"
   "lofar-device-base lofar-device-base y"
-  "http-json-schemas http-json-schemas y"
+
+  "dsconfig dsconfig n"
 
   "ec-sim ec-sim y"
 
diff --git a/sbin/update_ConfigDb.sh b/sbin/update_ConfigDb.sh
index 4e3fc4b210e1bcc2b299e51521cb9e92b94377b4..dd46a81ebee566bfeccad3603d28751cb5e4fdac 100755
--- a/sbin/update_ConfigDb.sh
+++ b/sbin/update_ConfigDb.sh
@@ -1,31 +1,7 @@
 #!/bin/bash
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
+#
+# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
 # SPDX-License-Identifier: Apache-2.0
+#
 
-if [ ${#} -eq 1 ]; then
-    file=${1}
-else
-    echo "A file name must be provided."
-    exit 1
-fi
-
-# dump a copy of the database before updating
-# Do not change -i into -it this will break integration tests in gitlab ci!
-docker exec -i dsconfig bash -c '
-  python -m dsconfig.dump > /tmp/dsconfig-configdb-dump.json
-  /manage_object_properties.py -r > /tmp/dsconfig-objectdb-dump.json
-  /merge_json.py /tmp/dsconfig-objectdb-dump.json /tmp/dsconfig-configdb-dump.json' \
-  > "${LOFAR20_DIR}"/CDB/dump_"$(date "+%Y.%m.%d_%H.%M.%S")".json
-
-# copy file into container to read it from container, as the file's location
-# in the container won't be the same as on the host.
-docker cp "${file}" dsconfig:/tmp/dsconfig-update-settings.json || exit 1
-
-# update settings, Do not change -i into -it this will break integration tests in gitlab ci!
-docker exec -i dsconfig /manage_object_properties.py --write < "${file}"
-
-# update settings, Do not change -i into -it this will break integration tests in gitlab ci!
-docker exec -i dsconfig json2tango --write --update /tmp/dsconfig-update-settings.json
-
-# somehow json2tango does not return 0 on success
-exit 0
+"${LOFAR20_DIR}/sbin/dsconfig.sh" --update "${1}"
diff --git a/setup.sh b/setup.sh
index ee556bb84e2e5badd8767145db70dd3ef368064d..ba9846ce97a934dd7badbb0bd4d5e406f03c2aea 100755
--- a/setup.sh
+++ b/setup.sh
@@ -1,11 +1,17 @@
 #! /usr/bin/env bash
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
+#
+# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
 # SPDX-License-Identifier: Apache-2.0
+#
 
 # Set up station control development
 
 # This file's directory is used to determine the station control directory
 # location.
+if [ -z ${BASH_SOURCE} ]; then
+  BASH_SOURCE=${(%):-%x}
+fi
+
 ABSOLUTE_PATH=$(realpath $(dirname ${BASH_SOURCE}))
 export LOFAR20_DIR=${1:-${ABSOLUTE_PATH}}
 
@@ -15,15 +21,22 @@ if [ ! -f "${LOFAR20_DIR}/.git/hooks/post-checkout" ]; then
   alias git="cp ${LOFAR20_DIR}/bin/update_submodules.sh ${LOFAR20_DIR}/.git/hooks/post-checkout; cp ${LOFAR20_DIR}/bin/update_submodules.sh ${LOFAR20_DIR}/.git/hooks/post-merge; unalias git; git"
 fi
 
-# This needs to be modified for a development environment.
-# In case you run multiple Docker networks on the same host in parallel,
-# you need to specify a unique network name for each of them.
-export NETWORK_MODE=tangonet
+# This is the docker network used within the integration tests/development environment
+export NETWORK_MODE=station
 
-# It is assumed that the Tango host, the computer that runs the TangoDB,
+# It checks if Tango is running within nomad, by trying to query the service from consul.
+# If consul is not available it is assumed that the Tango host, the computer that runs the TangoDB,
 # is this host. If this is not true, then modify to the Tango host's FQDN and
 # port. Example:  export TANGO_HOST=station-xk25.astron.nl:10000
-export TANGO_HOST=$(hostname):10000
+if dig @127.0.0.1 -p 8600 tango.service.consul +short > /dev/null; then
+  TANGO_PORT=$(dig @127.0.0.1 -p 8600 tango.service.consul SRV +short  | awk '{printf "%s",$3}')
+  TANGO_HOST=$(dig @127.0.0.1 -p 8600 tango.service.consul +short)
+  export TANGO_HOST="$TANGO_HOST:$TANGO_PORT"
+else
+  export TANGO_HOST=$(hostname):10000
+fi
+
+echo "Using tango host $TANGO_HOST"
 
 # Configure to install station control in lofar-device-base image
 # TODO(L2SS-520): Extend to support debug and expose as property in devices
diff --git a/tangostationcontrol/MANIFEST.in b/tangostationcontrol/MANIFEST.in
index 4f8b2d03b275d161613f418c6f50d8378462ac4f..0f4ae53bbcfd2d61287ddc3fa1ed5b1b5530192d 100644
--- a/tangostationcontrol/MANIFEST.in
+++ b/tangostationcontrol/MANIFEST.in
@@ -2,6 +2,8 @@ include LICENSE
 include README.md
 include VERSION
 
+include tangostationcontrol/configuration/schemas/*.json
 recursive-include docs *
 recursive-exclude tangostationcontrol/test *
 recursive-exclude tangostationcontrol/integration_test *
+
diff --git a/tangostationcontrol/VERSION b/tangostationcontrol/VERSION
index 2157409059873c80aa93884ecb847639add77b7a..ca222b7cf394c7e48e13308f41fb4c49478f6e12 100644
--- a/tangostationcontrol/VERSION
+++ b/tangostationcontrol/VERSION
@@ -1 +1 @@
-0.22.0
+0.23.0
diff --git a/tangostationcontrol/docs/source/configure_station.rst b/tangostationcontrol/docs/source/configure_station.rst
index fa45b4c34549eae0cde573751916282f2a00c674..4c08ada9bbe9449eb580e823b1961e1628647bb1 100644
--- a/tangostationcontrol/docs/source/configure_station.rst
+++ b/tangostationcontrol/docs/source/configure_station.rst
@@ -5,7 +5,7 @@ The software will need to be told various aspects of your station configuration,
 
 Stock configurations are provided for several stations, as well as using simulators to simulate the station's interface (which is the default after bootstrapping a station). These are provided in the ``CDB/stations/`` directory, and can be loaded using for example::
 
-    sbin/update_ConfigDb.sh CDB/stations/LTS_ConfigDb.json
+    sbin/dsconfig.sh --update CDB/stations/LTS_ConfigDb.json
 
 The following sections describe the settings that are station dependent, and thus must or can be set.
 
diff --git a/tangostationcontrol/docs/source/devices/configure.rst b/tangostationcontrol/docs/source/devices/configure.rst
index 0ed05a0fba41d10c9a73a91da355ddbf3f192b97..c8573f029082d1d88fd8601547c1c334311d6ef0 100644
--- a/tangostationcontrol/docs/source/devices/configure.rst
+++ b/tangostationcontrol/docs/source/devices/configure.rst
@@ -39,10 +39,10 @@ Command-line interaction
 
 The content of the TangoDB can be dumped from the command line using::
 
-  bin/dump_ConfigDb.sh > tangodb-dump.json
+  sbin/dsconfig.sh --dump > tangodb-dump.json
 
 and changes can be applied using::
 
-  bin/update_ConfigDb.sh changeset.json
+  sbin/dsconfig.sh --update changeset.json
 
 .. note:: The ``dsconfig`` docker container needs to be running for these commands to work.
diff --git a/tangostationcontrol/requirements.txt b/tangostationcontrol/requirements.txt
index 93ff226d7284c58835529d2f51230b92aaa89271..a875eb4b0bfff61e9d732e0c0f7048f84730b9e5 100644
--- a/tangostationcontrol/requirements.txt
+++ b/tangostationcontrol/requirements.txt
@@ -10,12 +10,14 @@ psycopg2-binary >= 2.9.2 # LGPL
 pyasn1 == 0.4.8 # BSD, pinned because https://github.com/pyasn1/pyasn1/issues/28
 pysnmp >= 0.1.7 # BSD
 h5py >= 3.1.0 # BSD
-jsonschema >= 4.18 # MIT
+jsonschema > 4.18 # MIT
+referencing > 0.30 # MIT
 docker >= 5.0.3 # Apache 2
-python-logstash-async >= 2.5.0, != 2.7.1 # MIT - https://github.com/eht16/python-logstash-async/issues/88
 python-casacore >= 3.3.1 # LGPLv3
 etrs-itrs@git+https://github.com/brentjens/etrs-itrs # Apache 2
 lofarantpos >= 0.5.0 # Apache 2
 python-geohash >= 0.8.5 # Apache 2 / MIT
 attributewrapper@git+https://git.astron.nl/lofar2.0/attributewrapper # Apache2
 minio >= 7.1.14 # Apache 2
+prometheus-client # Apache 2
+dnspython # ISC
diff --git a/tangostationcontrol/setup.cfg b/tangostationcontrol/setup.cfg
index be133876d3a99016db419ba89ad314506e049f67..90a78e2f90be128adf4a0e6fc8e15878e9217e06 100644
--- a/tangostationcontrol/setup.cfg
+++ b/tangostationcontrol/setup.cfg
@@ -31,34 +31,7 @@ where = .
 
 [options.entry_points]
 console_scripts =
-    l2ss-aps = tangostationcontrol.devices.aps:main
-    l2ss-apsct = tangostationcontrol.devices.apsct:main
-    l2ss-apspu = tangostationcontrol.devices.apspu:main
-    l2ss-ccd = tangostationcontrol.devices.ccd:main
-    l2ss-ec = tangostationcontrol.devices.ec:main
-    l2ss-psoc = tangostationcontrol.devices.psoc:main
-    l2ss-pcon = tangostationcontrol.devices.pcon:main
-    l2ss-tilebeam = tangostationcontrol.devices.tilebeam:main
-    l2ss-beamlet = tangostationcontrol.devices.sdp.beamlet:main
-    l2ss-digitalbeam = tangostationcontrol.devices.sdp.digitalbeam:main
-    l2ss-afh = tangostationcontrol.devices.antennafield.afh:main
-    l2ss-afl = tangostationcontrol.devices.antennafield.afl:main
-    l2ss-boot = tangostationcontrol.devices.boot:main
-    l2ss-station-manager = tangostationcontrol.devices.station_manager:main
-    l2ss-docker = tangostationcontrol.devices.docker:main
-    l2ss-observation = tangostationcontrol.devices.observation:main
-    l2ss-observationcontrol = tangostationcontrol.devices.observation_control:main
-    l2ss-recvh = tangostationcontrol.devices.recv.recvh:main
-    l2ss-recvl = tangostationcontrol.devices.recv.recvl:main
-    l2ss-sdpfirmware = tangostationcontrol.devices.sdp.firmware:main
-    l2ss-sdp = tangostationcontrol.devices.sdp.sdp:main
-    l2ss-bst = tangostationcontrol.devices.sdp.bst:main
-    l2ss-sst = tangostationcontrol.devices.sdp.sst:main
-    l2ss-unb2 = tangostationcontrol.devices.unb2:main
-    l2ss-xst = tangostationcontrol.devices.sdp.xst:main
-    l2ss-temperaturemanager = tangostationcontrol.devices.temperature_manager:main
-    l2ss-configuration = tangostationcontrol.devices.configuration:main
-    l2ss-calibration = tangostationcontrol.devices.calibration:main
+    l2ss-ds = tangostationcontrol.device_server:main
     l2ss-analyse-dsconfig-hierarchies = tangostationcontrol.toolkit.analyse_dsconfig_hierarchies:main
 
 # The following entry points should eventually be removed / replaced
diff --git a/tangostationcontrol/tangostationcontrol/beam/managers/_base.py b/tangostationcontrol/tangostationcontrol/beam/managers/_base.py
index b8ebe85ba3141d1ba8282ad336f01c46c0330e91..225f130a7f0ba76d73f544ed20737826a922357a 100644
--- a/tangostationcontrol/tangostationcontrol/beam/managers/_base.py
+++ b/tangostationcontrol/tangostationcontrol/beam/managers/_base.py
@@ -9,7 +9,7 @@ from statistics import median
 
 import numpy
 
-from tangostationcontrol.devices.device_decorators import (
+from tangostationcontrol.common.device_decorators import (
     TimeIt,
 )
 
diff --git a/tangostationcontrol/tangostationcontrol/beam/managers/_digitalbeam.py b/tangostationcontrol/tangostationcontrol/beam/managers/_digitalbeam.py
index 16e38a6f2804065ecce05b01350cc7b2fe45005f..83fe11f3abc7919fd130f406c7261bd0a3f5f5dd 100644
--- a/tangostationcontrol/tangostationcontrol/beam/managers/_digitalbeam.py
+++ b/tangostationcontrol/tangostationcontrol/beam/managers/_digitalbeam.py
@@ -11,7 +11,7 @@ from tangostationcontrol.common.constants import (
     N_pn,
     A_pn,
 )
-from tangostationcontrol.devices.device_decorators import (
+from tangostationcontrol.common.device_decorators import (
     TimeIt,
 )
 from ._base import AbstractBeamManager
diff --git a/tangostationcontrol/tangostationcontrol/beam/managers/_tilebeam.py b/tangostationcontrol/tangostationcontrol/beam/managers/_tilebeam.py
index 22b15a2ba2104d87c424c404ba83a22fb50d346b..0200c9cfa4477795d32e7115ad9c143c93a27575 100644
--- a/tangostationcontrol/tangostationcontrol/beam/managers/_tilebeam.py
+++ b/tangostationcontrol/tangostationcontrol/beam/managers/_tilebeam.py
@@ -11,7 +11,7 @@ from tangostationcontrol.common.constants import (
     MAX_ANTENNA,
 )
 from tangostationcontrol.common.proxy import create_device_proxy
-from tangostationcontrol.devices.device_decorators import (
+from tangostationcontrol.common.device_decorators import (
     TimeIt,
 )
 from ._base import AbstractBeamManager
diff --git a/tangostationcontrol/tangostationcontrol/common/calibration.py b/tangostationcontrol/tangostationcontrol/common/calibration.py
index 7128a36aacfc17a393a3b631c2b550945e9c3c24..99531481d67eadf0ee3a58a85b142de2e0f49918 100644
--- a/tangostationcontrol/tangostationcontrol/common/calibration.py
+++ b/tangostationcontrol/tangostationcontrol/common/calibration.py
@@ -11,6 +11,7 @@ from lofar_station_client.file_access import member, attribute, read_hdf5
 from minio import Minio
 from tango import DeviceProxy
 
+from tangostationcontrol.common import consul
 from tangostationcontrol.common.constants import (
     N_subbands,
     N_pol,
@@ -70,8 +71,10 @@ class CalibrationManager:
 
         logger.info(f"Derived {self.prefix=} {self.bucket_name=} from {self._url=}")
 
+        service: consul.Service = next(consul.lookup_service(result.netloc))
+        logger.info(f"Use service {service.host}:{service.port}")
         self._storage = Minio(
-            result.netloc,
+            f"{service.addr}:{service.port}",
             access_key=os.getenv("MINIO_ROOT_USER"),
             secret_key=os.getenv("MINIO_ROOT_PASSWORD"),
             secure=result.scheme == "https",
diff --git a/tangostationcontrol/tangostationcontrol/common/configuration.py b/tangostationcontrol/tangostationcontrol/common/configuration.py
index 7682daff065627a9d7df554a0c1d41813ded6832..09be2024ee77f3a7ba60b7df25f2537aecaf6d24 100644
--- a/tangostationcontrol/tangostationcontrol/common/configuration.py
+++ b/tangostationcontrol/tangostationcontrol/common/configuration.py
@@ -1,19 +1,15 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 import json
 import logging
 from itertools import islice
-from jsonschema import Draft7Validator, FormatChecker, ValidationError
 
+from jsonschema import Draft7Validator, FormatChecker, ValidationError
 from tango import DeviceProxy, Database, DevFailed, DbDevInfo
 
 from tangostationcontrol.common.proxy import create_device_proxy
-from tangostationcontrol.configuration.configuration_base import (
-    _ConfigurationBase,
-    RetryHttpRefResolver,
-)
-
+from tangostationcontrol.configuration import REGISTRY
 
 logger = logging.getLogger()
 
@@ -54,13 +50,10 @@ class StationConfiguration:
     def get_validator(cls):
         """Retrieve the JSON validator from Schemas container"""
         name = cls.__name__
-        url = f"{_ConfigurationBase.BASE_URL}{_ConfigurationBase._class_to_url(name)}.json"
-        resolver = RetryHttpRefResolver(
-            base_uri=_ConfigurationBase.BASE_URL, referrer=url
-        )
-        _, resolved = resolver.resolve(url)
         return Draft7Validator(
-            resolved, format_checker=FormatChecker(), resolver=resolver
+            REGISTRY["station-configuration"].contents,
+            format_checker=FormatChecker(),
+            registry=REGISTRY,
         )
 
     #
diff --git a/tangostationcontrol/tangostationcontrol/common/consul.py b/tangostationcontrol/tangostationcontrol/common/consul.py
new file mode 100644
index 0000000000000000000000000000000000000000..7c2f9f980dd5f46e697b5c6595f7fba11b4d70e3
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/common/consul.py
@@ -0,0 +1,42 @@
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
+import collections
+import logging
+from typing import Iterator
+
+from dns import resolver
+
+logger = logging.getLogger()
+
+Service = collections.namedtuple("Service", ["host", "addr", "port"])
+
+
+class ServiceLookupException(Exception):
+    """Exception that is raised when the service lookup failed."""
+
+    def __str__(self) -> str:
+        return "Service lookup failed for %s: %s" % self.args[0:2]
+
+
+def lookup_service(name: str) -> Iterator[Service]:
+    fqdn = f"{name}.service.consul"
+
+    try:
+        result: resolver.Answer = resolver.resolve(fqdn, "SRV")
+    except (
+        resolver.NoAnswer,
+        resolver.NoNameservers,
+        resolver.NotAbsolute,
+        resolver.NoRootSOA,
+        resolver.NXDOMAIN,
+    ) as error:
+        logger.error("Querying SRV %s failed: %r", fqdn, error)
+        raise ServiceLookupException(name, error)
+
+    for resource in result:
+        host = resource.target.to_text(omit_final_dot=True)
+        yield Service(host, host, resource.port)
+
+
+if __name__ == "__main__":
+    lookup_service("s3")
diff --git a/tangostationcontrol/tangostationcontrol/devices/device_decorators.py b/tangostationcontrol/tangostationcontrol/common/device_decorators.py
similarity index 100%
rename from tangostationcontrol/tangostationcontrol/devices/device_decorators.py
rename to tangostationcontrol/tangostationcontrol/common/device_decorators.py
diff --git a/tangostationcontrol/tangostationcontrol/common/lofar_logging.py b/tangostationcontrol/tangostationcontrol/common/lofar_logging.py
index 3f405b6fef954c864a7b797952fd7cee58f125f0..7223b70ee76b6e0344fa0fd2ccbf09184c70bef2 100644
--- a/tangostationcontrol/tangostationcontrol/common/lofar_logging.py
+++ b/tangostationcontrol/tangostationcontrol/common/lofar_logging.py
@@ -1,5 +1,5 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 import logging
 import socket
@@ -7,8 +7,8 @@ import time
 import traceback
 from functools import wraps
 
-from tango.server import Device
 from tango import DevFailed
+from tango.server import Device
 
 from tangostationcontrol import __version__ as version
 
@@ -179,37 +179,6 @@ def configure_logger(logger: logging.Logger = None, log_extra=None, debug=False)
 
     logger.addHandler(handler)
 
-    # If configuring for debug; exit early
-    if debug:
-        return logger
-
-    # Log to Logstash-Loki
-    try:
-        from logstash_async.handler import (
-            AsynchronousLogstashHandler,
-            LogstashFormatter,
-        )
-
-        # log to the tcp_input of logstash in our logstash-loki container
-        handler = AsynchronousLogstashHandler(
-            "logstash", 5959, database_path="/tmp/lofar_pending_log_messages.db"
-        )
-
-        # configure log messages
-        formatter = LogstashFormatter(extra=log_extra, tags=[])
-        handler.setFormatter(formatter)
-        handler.addFilter(LogSuppressErrorSpam())
-        handler.addFilter(LogAnnotator())
-
-        # install the handler
-        logger.addHandler(handler)
-    except ImportError:
-        logger.exception(
-            "Cannot forward logs to Logstash-Loki: logstash_async module not found."
-        )
-    except Exception:
-        logger.exception("Cannot forward logs to Logstash-Loki.")
-
     # Don't log to Tango to reduce log spam
     """
     # Log to Tango
diff --git a/tangostationcontrol/tangostationcontrol/configuration/__init__.py b/tangostationcontrol/tangostationcontrol/configuration/__init__.py
index 735a5ee255aa6d660458612587af18bb5f01110a..d5b9dc9bd9cc8bd7d1e442f79c5e00832c7b2b5b 100644
--- a/tangostationcontrol/tangostationcontrol/configuration/__init__.py
+++ b/tangostationcontrol/tangostationcontrol/configuration/__init__.py
@@ -1,16 +1,11 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
+from ._schemas import REGISTRY
+from .dithering import Dithering
+from .hba import HBA
 from .observation_settings import ObservationSettings
 from .pointing import Pointing
 from .sap import Sap
-from .hba import HBA
-from .dithering import Dithering
 
-__all__ = [
-    "ObservationSettings",
-    "Pointing",
-    "Sap",
-    "HBA",
-    "Dithering",
-]
+__all__ = ["ObservationSettings", "Pointing", "Sap", "HBA", "Dithering", "REGISTRY"]
diff --git a/tangostationcontrol/tangostationcontrol/configuration/_schemas.py b/tangostationcontrol/tangostationcontrol/configuration/_schemas.py
new file mode 100644
index 0000000000000000000000000000000000000000..6a6678e0876fca671519552d76a20f5ee61e352e
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/configuration/_schemas.py
@@ -0,0 +1,24 @@
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
+
+import json
+
+try:
+    from importlib.resources import files
+except ImportError:
+    from importlib_resources import files  # type: ignore
+
+from referencing import Resource
+from referencing import Registry as _Registry
+from referencing.jsonschema import SchemaRegistry as _SchemaRegistry
+
+
+def _schemas():
+    for schema in files(__package__).joinpath("schemas").iterdir():
+        contents = json.loads(schema.read_text(encoding="utf-8"))
+        yield Resource.from_contents(contents)
+
+
+#: A `referencing.jsonschema.SchemaRegistry` containing all of the official
+#: meta-schemas and vocabularies.
+REGISTRY: _SchemaRegistry = (_schemas() @ _Registry()).crawl()
diff --git a/tangostationcontrol/tangostationcontrol/configuration/configuration_base.py b/tangostationcontrol/tangostationcontrol/configuration/configuration_base.py
index 3b879ff2af4145204b9796788bdb7fa817c70278..ea8769d550f9659e1b5ce516c14339574686a84d 100644
--- a/tangostationcontrol/tangostationcontrol/configuration/configuration_base.py
+++ b/tangostationcontrol/tangostationcontrol/configuration/configuration_base.py
@@ -1,45 +1,20 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 import json
 import re
-import time
 from abc import ABC, abstractmethod
 from typing import TypeVar, Type
 
 import jsonschema
-import requests
 from jsonschema import Draft7Validator, FormatChecker, ValidationError
-from jsonschema.validators import RefResolver
+
+from tangostationcontrol.configuration import REGISTRY
 from tangostationcontrol.configuration._json_parser import _from_json_hook_t
 
 T = TypeVar("T")
 
 
-def _fetch_url(url):
-    attempt_nr = 1
-    while True:
-        try:
-            response = requests.get(url)
-            response.raise_for_status()
-            return response.json()
-        except requests.exceptions.RequestException as e:
-            time.sleep(2)  # retry after a little sleep
-            if attempt_nr >= 5:
-                raise e
-            else:
-                attempt_nr += 1
-
-
-class RetryHttpRefResolver(RefResolver):
-    def resolve_remote(self, uri):
-        result = _fetch_url(uri)
-
-        if self.cache_remote:
-            self.store[uri] = result
-        return result
-
-
 def _is_object(_, instance):
     return isinstance(instance, dict) or issubclass(type(instance), _ConfigurationBase)
 
@@ -53,8 +28,6 @@ jsonschema.validators.Draft7Validator.TYPE_CHECKER = (
 
 
 class _ConfigurationBase(ABC):
-    BASE_URL = "http://http-json-schemas/"
-
     @staticmethod
     def _class_to_url(cls_name):
         cls_name = cls_name.replace("HBA", "hba")
@@ -63,14 +36,12 @@ class _ConfigurationBase(ABC):
 
     @classmethod
     def get_validator(cls):
+        """Retrieve the JSON validator from Schemas container"""
         name = cls.__name__
-        url = f"{_ConfigurationBase.BASE_URL}{_ConfigurationBase._class_to_url(name)}.json"
-        resolver = RetryHttpRefResolver(
-            base_uri=_ConfigurationBase.BASE_URL, referrer=url
-        )
-        _, resolved = resolver.resolve(url)
         return Draft7Validator(
-            resolved, format_checker=FormatChecker(), resolver=resolver
+            REGISTRY[_ConfigurationBase._class_to_url(name)].contents,
+            format_checker=FormatChecker(),
+            registry=REGISTRY,
         )
 
     @abstractmethod
diff --git a/docker-compose/http-json-schemas/definitions/dithering.json b/tangostationcontrol/tangostationcontrol/configuration/schemas/dithering.json
similarity index 96%
rename from docker-compose/http-json-schemas/definitions/dithering.json
rename to tangostationcontrol/tangostationcontrol/configuration/schemas/dithering.json
index 87a6077d24f4bd8878ba0a66ba1572417ce44adb..05c327b6eb2bcf92af96b457fef97f6024605987 100644
--- a/docker-compose/http-json-schemas/definitions/dithering.json
+++ b/tangostationcontrol/tangostationcontrol/configuration/schemas/dithering.json
@@ -1,5 +1,6 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema",
+  "$id": "dithering",
   "type": "object",
   "description": "Settings for adding dithering to the signal to increase its linearity",
   "required": [
diff --git a/docker-compose/http-json-schemas/definitions/hba.json b/tangostationcontrol/tangostationcontrol/configuration/schemas/hba.json
similarity index 92%
rename from docker-compose/http-json-schemas/definitions/hba.json
rename to tangostationcontrol/tangostationcontrol/configuration/schemas/hba.json
index 5d1a319fc18c58dc45e720cfbe1d616d7fcb3d88..fa0eb4f311e88c3e653a490b0b81a72985e229bc 100644
--- a/docker-compose/http-json-schemas/definitions/hba.json
+++ b/tangostationcontrol/tangostationcontrol/configuration/schemas/hba.json
@@ -1,12 +1,13 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema",
+  "$id": "hba",
   "type": "object",
   "required": [
     "tile_beam"
   ],
   "properties": {
     "tile_beam": {
-      "$ref": "pointing.json"
+      "$ref": "pointing"
     },
     "DAB_filter": {
       "type": "boolean",
diff --git a/docker-compose/http-json-schemas/definitions/observation-settings.json b/tangostationcontrol/tangostationcontrol/configuration/schemas/observation-settings.json
similarity index 94%
rename from docker-compose/http-json-schemas/definitions/observation-settings.json
rename to tangostationcontrol/tangostationcontrol/configuration/schemas/observation-settings.json
index e6bdf42a3787cd05d819df380871b480c96bd3fc..c23561dc71f93d298a9c03cccfe7bbf388e655f3 100644
--- a/docker-compose/http-json-schemas/definitions/observation-settings.json
+++ b/tangostationcontrol/tangostationcontrol/configuration/schemas/observation-settings.json
@@ -1,5 +1,6 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema",
+  "$id": "observation-settings",
   "type": "object",
   "required": [
     "observation_id",
@@ -53,7 +54,7 @@
       ]
     },
     "dithering": {
-      "$ref": "dithering.json"
+      "$ref": "dithering"
     },
     "filter": {
       "type": "string",
@@ -71,7 +72,7 @@
       "type": "array",
       "minItems": 1,
       "items": {
-        "$ref": "sap.json"
+        "$ref": "sap"
       }
     },
     "first_beamlet": {
@@ -80,7 +81,7 @@
       "minimum": 0
     },
     "HBA": {
-      "$ref": "hba.json"
+      "$ref": "hba"
     }
   }
 }
diff --git a/docker-compose/http-json-schemas/definitions/pointing.json b/tangostationcontrol/tangostationcontrol/configuration/schemas/pointing.json
similarity index 97%
rename from docker-compose/http-json-schemas/definitions/pointing.json
rename to tangostationcontrol/tangostationcontrol/configuration/schemas/pointing.json
index ac3ddc141bfe92a67cd13016bfad5de6fe1191a4..61e1fc80faf70830fd6643dec309c2359e37165f 100644
--- a/docker-compose/http-json-schemas/definitions/pointing.json
+++ b/tangostationcontrol/tangostationcontrol/configuration/schemas/pointing.json
@@ -1,5 +1,6 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema",
+  "$id": "pointing",
   "type": "object",
   "required": [
     "angle1",
@@ -41,4 +42,4 @@
       "type": "string"
     }
   }
-}
\ No newline at end of file
+}
diff --git a/docker-compose/http-json-schemas/definitions/sap.json b/tangostationcontrol/tangostationcontrol/configuration/schemas/sap.json
similarity index 87%
rename from docker-compose/http-json-schemas/definitions/sap.json
rename to tangostationcontrol/tangostationcontrol/configuration/schemas/sap.json
index 50e945e8725eb5cf4d98c1dcbd6baf2144406fa3..7dd382d9ed5191e02a6c4ee6ee38b557179502ba 100644
--- a/docker-compose/http-json-schemas/definitions/sap.json
+++ b/tangostationcontrol/tangostationcontrol/configuration/schemas/sap.json
@@ -1,5 +1,6 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema",
+  "$id": "sap",
   "type": "object",
   "required": [
     "subbands",
@@ -14,7 +15,7 @@
       }
     },
     "pointing": {
-      "$ref": "pointing.json"
+      "$ref": "pointing"
     }
   }
-}
\ No newline at end of file
+}
diff --git a/docker-compose/http-json-schemas/definitions/station-configuration.json b/tangostationcontrol/tangostationcontrol/configuration/schemas/station-configuration.json
similarity index 70%
rename from docker-compose/http-json-schemas/definitions/station-configuration.json
rename to tangostationcontrol/tangostationcontrol/configuration/schemas/station-configuration.json
index 66a11e129366ceb270c5800fb83d742a43214e93..4c8d110c02a082b556225d314d914f5da7f74d65 100644
--- a/docker-compose/http-json-schemas/definitions/station-configuration.json
+++ b/tangostationcontrol/tangostationcontrol/configuration/schemas/station-configuration.json
@@ -1,6 +1,6 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
-  "$id": "http://http-json-schemas/station-configuration.json",
+  "$id": "station-configuration",
   "title": "Tango device generator JSON canonical format",
   "type": "object",
   "additionalProperties": false,
@@ -27,18 +27,18 @@
       "additionalProperties": false,
       "patternProperties": {
         "^[\\-\\w]+$": {
-          "$ref": "station-configuration.json#/$defs/server"
+          "$ref": "#/$defs/server"
         }
       }
     },
     "classes": {
       "type": "object",
       "additionalProperties": {
-        "$ref": "station-configuration.json#/$defs/device"
+        "$ref": "#/$defs/device"
       },
       "properties": {
         "properties": {
-          "$ref": "station-configuration.json#/$defs/property"
+          "$ref": "#/$defs/property"
         }
       }
     }
@@ -48,7 +48,7 @@
       "type": "object",
       "patternProperties": {
         "^[\\-\\w]+$": {
-          "$ref": "station-configuration.json#/$defs/instance"
+          "$ref": "#/$defs/instance"
         }
       }
     },
@@ -56,7 +56,7 @@
       "type": "object",
       "patternProperties": {
         "^[\\-\\w]+$": {
-          "$ref": "station-configuration.json#/$defs/class"
+          "$ref": "#/$defs/class"
         }
       }
     },
@@ -65,7 +65,7 @@
       "additionalProperties": false,
       "patternProperties": {
         "^[\\-\\w.@]+/[\\-\\w.@]+/[\\-\\w.@]+$": {
-          "$ref": "station-configuration.json#/$defs/device"
+          "$ref": "#/$defs/device"
         }
       }
     },
@@ -74,10 +74,10 @@
       "additionalProperties": false,
       "properties": {
         "properties": {
-          "$ref": "station-configuration.json#/$defs/properties"
+          "$ref": "#/$defs/properties"
         },
         "attribute_properties": {
-          "$ref": "station-configuration.json#/$defs/attribute_properties"
+          "$ref": "#/$defs/attribute_properties"
         },
         "alias": {
           "type": "string"
@@ -87,7 +87,7 @@
     "properties": {
       "type": "object",
       "additionalProperties": {
-        "$ref": "station-configuration.json#/$defs/property"
+        "$ref": "#/$defs/property"
       }
     },
     "property": {
@@ -99,20 +99,20 @@
     "attribute_properties": {
       "type": "object",
       "additionalProperties": {
-        "$ref": "station-configuration.json#/$defs/attribute_property"
+        "$ref": "#/$defs/attribute_property"
       }
     },
     "attribute_property": {
       "type": "object",
       "additionalProperties": {
-        "$ref": "station-configuration.json#/$defs/property"
+        "$ref": "#/$defs/property"
       }
     },
     "class_property": {
       "type": "object",
       "additionalProperties": {
         "type": "object",
-        "$ref": "station-configuration.json#/$defs/property"
+        "$ref": "#/$defs/property"
       }
     }
   }
diff --git a/tangostationcontrol/tangostationcontrol/device_server/__init__.py b/tangostationcontrol/tangostationcontrol/device_server/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..04330a21153edff105ad1f804fc13835e4011e26
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/device_server/__init__.py
@@ -0,0 +1,72 @@
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
+
+import logging
+import sys
+
+from prometheus_client import start_http_server, Info
+from tango import Util
+from tango.server import run
+
+from tangostationcontrol import __version__ as version
+from tangostationcontrol import devices  # noqa: F401
+from tangostationcontrol.common.lofar_logging import configure_logger
+from tangostationcontrol.metrics import labelnames
+
+logger = logging.getLogger()
+
+tango_ds_info = Info("tango_ds", "Info about the tango device server", labelnames)
+
+
+def post_init_cb():
+    """This callback runs after the device server was initialised. It sets the
+    station control version as the server version, as well as initialises common
+    prometheus metrics about the device server."""
+    util = Util.instance()
+    util.set_server_version(version)
+    tango_version = util.get_tango_lib_release()
+    tango_ds_info.labels(
+        domain="dserver",
+        family=util.get_ds_exec_name(),
+        member=util.get_ds_inst_name(),
+        device_class="DServer",
+    ).info(
+        dict(
+            idl_version=util.get_version_str(),
+            server_version=util.get_server_version(),
+            lib_version=".".join(
+                [
+                    str(x)
+                    for x in [
+                        tango_version // 100,
+                        tango_version % 100 // 10,
+                        tango_version % 10,
+                    ]
+                ]
+            ),
+        )
+    )
+
+
+# ----------
+# Run server
+# ----------
+def main(**kwargs):
+    device_class_str = sys.argv[1]
+    device_class = getattr(sys.modules["tangostationcontrol.devices"], device_class_str)
+
+    start_http_server(8000)
+
+    # Remove first argument which is filename
+    args = sys.argv[1:]
+
+    # Setup logging
+    configure_logger()
+
+    if device_class_str == "ObservationControl":
+        classes = (device_class, devices.Observation)
+    else:
+        classes = (device_class,)
+
+    # Start the device server
+    run(classes, args=args, post_init_callback=post_init_cb, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/__init__.py b/tangostationcontrol/tangostationcontrol/devices/__init__.py
index c92b615444d854a6e87370b16cf733a5859a07e7..b6038faf0137d4b710dbe92be7f0a953755d1a0f 100644
--- a/tangostationcontrol/tangostationcontrol/devices/__init__.py
+++ b/tangostationcontrol/tangostationcontrol/devices/__init__.py
@@ -1,2 +1,62 @@
 #  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
 #  SPDX-License-Identifier: Apache-2.0
+
+from .antennafield.afh import AFH
+from .antennafield.afl import AFL
+from .aps import APS
+from .apsct import APSCT
+from .apspu import APSPU
+from .calibration import Calibration
+from .ccd import CCD
+from .configuration import Configuration
+from .docker import Docker
+from .ec import EC
+from .observation import Observation
+from .observation_control import ObservationControl
+from .pcon import PCON
+from .psoc import PSOC
+from .recv.recvh import RECVH
+from .recv.recvl import RECVL
+from .sdp.beamlet import Beamlet
+from .sdp.bst import BST
+from .sdp.digitalbeam import DigitalBeam
+from .sdp.firmware import SDPFirmware
+from .sdp.sdp import SDP
+from .sdp.sst import SST
+from .sdp.statistics import Statistics
+from .sdp.xst import XST
+from .station_manager import StationManager
+from .temperature_manager import TemperatureManager
+from .tilebeam import TileBeam
+from .unb2 import UNB2
+
+__all__ = [
+    "AFH",
+    "AFL",
+    "APS",
+    "APSCT",
+    "APSPU",
+    "Calibration",
+    "CCD",
+    "Configuration",
+    "Docker",
+    "EC",
+    "Observation",
+    "ObservationControl",
+    "PCON",
+    "PSOC",
+    "RECVL",
+    "RECVH",
+    "Beamlet",
+    "BST",
+    "DigitalBeam",
+    "SDPFirmware",
+    "SDP",
+    "SST",
+    "Statistics",
+    "XST",
+    "StationManager",
+    "TemperatureManager",
+    "TileBeam",
+    "UNB2",
+]
diff --git a/tangostationcontrol/tangostationcontrol/devices/aps.py b/tangostationcontrol/tangostationcontrol/devices/aps.py
index 5e4b21073dba03c9ee514e1d9aaa623bf6302101..1abc1524a634a88a915c7baf3b34a36b8d454baf 100644
--- a/tangostationcontrol/tangostationcontrol/devices/aps.py
+++ b/tangostationcontrol/tangostationcontrol/devices/aps.py
@@ -1,18 +1,17 @@
-# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ APS Device Server for LOFAR2.0
 
 """
 
 # PyTango imports
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import device_logging_to_python
 from tangostationcontrol.devices.base_device_classes.lofar_device import LOFARDevice
 
 # Additional import
 
-__all__ = ["APS", "main"]
+__all__ = ["APS"]
 
 
 @device_logging_to_python()
@@ -33,11 +32,3 @@ class APS(LOFARDevice):
     # --------
     # Commands
     # --------
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the APS module."""
-    return entry(APS, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/apsct.py b/tangostationcontrol/tangostationcontrol/devices/apsct.py
index 9b7023b1cb12830a5afcc3e437958f6b304d2cd2..6048169b93d32108e41ae7d9bd4166b227eab55a 100644
--- a/tangostationcontrol/tangostationcontrol/devices/apsct.py
+++ b/tangostationcontrol/tangostationcontrol/devices/apsct.py
@@ -1,5 +1,5 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ APSCT Device Server for LOFAR2.0
 
@@ -15,17 +15,16 @@ from tango import AttrWriteType
 from tango import DebugIt
 from tango.server import command, attribute, device_property
 from tangostationcontrol.common.constants import DEFAULT_POLLING_PERIOD
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import device_logging_to_python
 from tangostationcontrol.common.states import DEFAULT_COMMAND_STATES
-from tangostationcontrol.devices.device_decorators import only_in_states
 from tangostationcontrol.devices.base_device_classes.opcua_device import OPCUADevice
+from tangostationcontrol.common.device_decorators import only_in_states
 
 # Additional import
 
 logger = logging.getLogger()
 
-__all__ = ["APSCT", "main"]
+__all__ = ["APSCT"]
 
 
 @device_logging_to_python()
@@ -268,11 +267,3 @@ class APSCT(OPCUADevice):
         :return:None
         """
         self.opcua_connection.call_method(["APSCT_160MHz_on"])
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the APSCT module."""
-    return entry(APSCT, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/apspu.py b/tangostationcontrol/tangostationcontrol/devices/apspu.py
index fd89a2796809d0c0afc65f98d5b4fc6f29088656..7d82bf25605fce0b8ed10af19a1f6d8e33c95932 100644
--- a/tangostationcontrol/tangostationcontrol/devices/apspu.py
+++ b/tangostationcontrol/tangostationcontrol/devices/apspu.py
@@ -1,5 +1,5 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ APSPU Device Server for LOFAR2.0
 
@@ -11,14 +11,14 @@ from attribute_wrapper.attribute_wrapper import AttributeWrapper
 # PyTango imports
 from tango import AttrWriteType
 from tango.server import attribute, device_property
+
 from tangostationcontrol.common.constants import DEFAULT_POLLING_PERIOD
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import device_logging_to_python
 from tangostationcontrol.devices.base_device_classes.opcua_device import OPCUADevice
 
 # Additional import
 
-__all__ = ["APSPU", "main"]
+__all__ = ["APSPU"]
 
 
 @device_logging_to_python()
@@ -100,9 +100,8 @@ class APSPU(OPCUADevice):
     # ----------
     # Summarising Attributes
     # ----------
-    APSPU_error_R = attribute(dtype=bool, fisallowed="is_attribute_access_allowed")
-
-    def read_APSPU_error_R(self):
+    @attribute(dtype=bool, fisallowed="is_attribute_access_allowed")
+    def APSPU_error_R(self):
         return (
             (self.read_attribute("APSPUTR_I2C_error_R") > 0)
             or self.alarm_val("APSPU_PCB_ID_R")
@@ -147,11 +146,3 @@ class APSPU(OPCUADevice):
     # --------
     # Commands
     # --------
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the ObservationControl module."""
-    return entry(APSPU, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/antennafield_device.py b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/antennafield_device.py
index 75433fd6d6445c2154be40f157b8648f5e3280b3..b8dc08d6852340177ce102caa10a5f7282de6fe3 100644
--- a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/antennafield_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/antennafield_device.py
@@ -1,12 +1,12 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ AntennaField Abstract Device Server for LOFAR2.0
 
 """
 import logging
-from typing import List, Dict
 from enum import IntEnum
+from typing import List, Dict
 
 import numpy
 
@@ -22,6 +22,7 @@ from tango.server import device_property, attribute, command
 from tangostationcontrol.beam.geo import ETRS_to_ITRF
 from tangostationcontrol.beam.geo import GEO_to_GEOHASH
 from tangostationcontrol.beam.geo import ITRF_to_GEO
+from tangostationcontrol.common.antennas import antenna_set_to_mask
 from tangostationcontrol.common.cables import cable_types
 from tangostationcontrol.common.constants import (
     MAX_ANTENNA,
@@ -32,7 +33,7 @@ from tangostationcontrol.common.constants import (
     A_pn,
     N_ANTENNA_SETS,
 )
-from tangostationcontrol.common.entrypoint import entry
+from tangostationcontrol.common.device_decorators import only_in_states
 from tangostationcontrol.common.frequency_bands import bands, Band
 from tangostationcontrol.common.lofar_logging import (
     device_logging_to_python,
@@ -40,10 +41,7 @@ from tangostationcontrol.common.lofar_logging import (
 )
 from tangostationcontrol.common.proxy import create_device_proxy
 from tangostationcontrol.common.states import DEFAULT_COMMAND_STATES
-from tangostationcontrol.common.antennas import antenna_set_to_mask
-from tangostationcontrol.devices.device_decorators import only_in_states
 from tangostationcontrol.devices.base_device_classes.lofar_device import LOFARDevice
-from tangostationcontrol.devices.types import DeviceTypes
 from tangostationcontrol.devices.base_device_classes.mapper import (
     MappingKeys,
     MappedAttribute,
@@ -52,10 +50,11 @@ from tangostationcontrol.devices.base_device_classes.mapper import (
     HBAToRecvMapper,
     LBAToRecvMapper,
 )
+from tangostationcontrol.devices.types import DeviceTypes
 
 logger = logging.getLogger()
 
-__all__ = ["AF", "AntennaUse", "AntennaQuality", "main"]
+__all__ = ["AF", "AntennaUse", "AntennaQuality"]
 
 
 class AntennaUse(IntEnum):
@@ -964,11 +963,3 @@ class AF(LOFARDevice):
             self.read_attribute("Antenna_Usage_Mask_R"),
         )
         walker.walk_receivers(self.recv_proxies, lambda recv: recv.RCU_DTH_off())
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the AntennaField module."""
-    return entry(AF, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/beam_device.py b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/beam_device.py
index 0e50d03354ffe351a1c74509ae3041ec6f246801..a6d3dab2a643701a187bc06abc779101c00c87c0 100644
--- a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/beam_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/beam_device.py
@@ -29,9 +29,12 @@ from tango.server import attribute, command, device_property
 from tangostationcontrol.beam.delays import Delays, pointing_to_str
 from tangostationcontrol.beam.managers import AbstractBeamManager
 from tangostationcontrol.common.constants import MAX_POINTINGS, N_point_prop
+from tangostationcontrol.common.device_decorators import (
+    only_in_states,
+    fault_on_error,
+)
 
 # Additional import
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import log_exceptions, exception_to_str
 from tangostationcontrol.common.measures import (
     download_measures,
@@ -41,14 +44,9 @@ from tangostationcontrol.common.measures import (
     use_measures_directory,
 )
 from tangostationcontrol.common.states import DEFAULT_COMMAND_STATES
-from tangostationcontrol.devices.device_decorators import (
-    only_in_states,
-    fault_on_error,
-)
-
 from tangostationcontrol.devices.base_device_classes.async_device import AsyncDevice
 
-__all__ = ["BeamDevice", "main", "BeamTracker"]
+__all__ = ["BeamDevice", "BeamTracker"]
 
 logger = logging.getLogger()
 
@@ -447,16 +445,6 @@ class BeamDevice(AsyncDevice):
         restart_python()
 
 
-# ----------
-# Run server
-# ----------
-
-
-def main(**kwargs):
-    """Main function of the Docker module."""
-    return entry(BeamDevice, **kwargs)
-
-
 # ----------
 # Beam Tracker
 # ----------
diff --git a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/lofar_device.py b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/lofar_device.py
index 61ac4db5663ea7873948faa97404fe665e250dff..cabc55e4c49831aeb7162dc0a5cf45c9d1b75380 100644
--- a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/lofar_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/lofar_device.py
@@ -1,16 +1,16 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """Hardware Device Server for LOFAR2.0
 
 """
-from functools import partial
-import pprint
-from typing import List
+import logging
 import math
+import pprint
 import textwrap
 import time
-import logging
+from functools import partial
+from typing import List
 
 import numpy
 from attribute_wrapper.attribute_wrapper import AttributeWrapper
@@ -24,20 +24,21 @@ from tango import (
 
 # PyTango imports
 from tango.server import attribute, command, Device, device_property
+
+# Additional import
+from tangostationcontrol import __version__ as version
+from tangostationcontrol.common.device_decorators import only_in_states, fault_on_error
 from tangostationcontrol.common.lofar_logging import log_exceptions
+from tangostationcontrol.common.proxy import create_device_proxy
 from tangostationcontrol.common.states import (
     DEFAULT_COMMAND_STATES,
     INITIALISED_STATES,
 )
-from tangostationcontrol.common.proxy import create_device_proxy
 from tangostationcontrol.common.type_checking import sequence_not_str
-from tangostationcontrol.devices.device_decorators import only_in_states, fault_on_error
 from tangostationcontrol.devices.base_device_classes.control_hierarchy import (
     ControlHierarchyDevice,
 )
-
-# Additional import
-from tangostationcontrol import __version__ as version
+from tangostationcontrol.metrics import DeviceMetricCollector
 
 __all__ = ["LOFARDevice"]
 
@@ -94,7 +95,7 @@ class LOFARDevice(Device):
         doc="How long this software device has been running (wall-clock time, in seconds)",
         unit="s",
         dtype=numpy.int64,
-        fget=lambda self: numpy.int64(time.time() - self._device_start_time),
+        fget=lambda self: numpy.int64(time.time() - self.device_start_time),
     )
 
     access_count_R = attribute(
@@ -247,7 +248,8 @@ class LOFARDevice(Device):
         self._access_count = 0
 
         # record when this device was started
-        self._device_start_time = time.time()
+        self.device_start_time = time.time()
+        DeviceMetricCollector.add_device(self)
 
     def _init_device(self):
         logger.debug("[LOFARDevice] init_device")
diff --git a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/opcua_device.py b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/opcua_device.py
index 1462c7a1015528b1fd1e91bf6bca754ca8b43d2c..6799dbe87d4ca190b26a3fa930bdcf5d1a6dd78e 100644
--- a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/opcua_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/opcua_device.py
@@ -1,5 +1,5 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ Generic OPC-UA Device Server for LOFAR2.0
 
@@ -7,11 +7,13 @@
 
 import asyncio
 import logging
+from datetime import datetime
 
 import numpy
+import prometheus_client as pc
 
 # PyTango imports
-from tango.server import device_property, attribute
+from tango.server import device_property, attribute, Device
 from tangostationcontrol.clients.opcua_client import (
     OPCUAConnection,
     test_OPCUA_connection,
@@ -19,7 +21,7 @@ from tangostationcontrol.clients.opcua_client import (
 )
 from tangostationcontrol.common.lofar_logging import log_exceptions, exception_to_str
 from tangostationcontrol.devices.base_device_classes.lofar_device import LOFARDevice
-
+from tangostationcontrol.metrics import device_metrics, labelnames
 
 # Additional import
 
@@ -28,6 +30,7 @@ logger = logging.getLogger()
 __all__ = ["OPCUADevice"]
 
 
+@device_metrics(exclude=["*_RW", "can_connect_R"])
 class OPCUADevice(LOFARDevice):
     """
 
@@ -42,8 +45,46 @@ class OPCUADevice(LOFARDevice):
             - Type:'DevDouble'
     """
 
-    #  Contains the connection status information
-    OPCUA_connection_status = OPCUAConnectionStatus()
+    class MonitoredOPCUAConnectionStatus(OPCUAConnectionStatus):
+        ds_opcua_nr_connection_losses = pc.Counter(
+            "ds_opcua_connection_losses", "Amount of connection losses", labelnames
+        )
+        ds_opcua_connected = pc.Gauge(
+            "ds_opcua_connected", "Is connected to OPCUA server", labelnames
+        )
+
+        def __init__(self, device: Device):
+            self.last_disconnect_exception = None
+            self.last_disconnect_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+            self._nr_connection_losses = 0
+            self._connected = False
+            self._device = device
+
+        @property
+        def nr_connection_losses(self):
+            return self._nr_connection_losses
+
+        @nr_connection_losses.setter
+        def nr_connection_losses(self, value):
+            try:
+                self.ds_opcua_nr_connection_losses.labels(
+                    self._device.metric_labels
+                ).inc(value - self._nr_connection_losses)
+            except ValueError:
+                pass
+            self._nr_connection_losses = value
+
+        @property
+        def connected(self):
+            return self._connected
+
+        @connected.setter
+        def connected(self, value):
+            self._connected = value
+            try:
+                self.ds_opcua_connected.labels(self._device.metric_labels).set(value)
+            except ValueError:
+                pass
 
     # -----------------
     # Device Properties
@@ -99,11 +140,8 @@ class OPCUADevice(LOFARDevice):
         fisallowed="is_attribute_access_allowed",
     )
 
-    can_connect_R = attribute(
-        dtype=bool,
-    )
-
-    def read_can_connect_R(self):
+    @attribute(dtype=bool)
+    def can_connect_R(self):
         """
         Test whether we can connect with the OPC-ua server.
         Sets up an OPC ua connection from scratch, as it cannot be assumed
@@ -132,7 +170,7 @@ class OPCUADevice(LOFARDevice):
             self.OPC_namespace,
             self.OPC_Time_Out,
             self.Fault,
-            self.OPCUA_connection_status,
+            self.MonitoredOPCUAConnectionStatus(self),
         )
         self.opcua_connection.node_path_prefix = self.OPC_Node_Path_Prefix
 
diff --git a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/power_hierarchy.py b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/power_hierarchy.py
index 2fb996242bde7eb0c97dd50ff2d0e3cf41733a40..1e6626be9550d349b94d51e20217cfe04dec64dd 100644
--- a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/power_hierarchy.py
+++ b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/power_hierarchy.py
@@ -17,7 +17,7 @@ from tangostationcontrol.common.states import (
 )
 from tangostationcontrol.common.type_checking import device_class_matches
 
-from tangostationcontrol.devices.device_decorators import suppress_exceptions
+from tangostationcontrol.common.device_decorators import suppress_exceptions
 
 logger = logging.getLogger()
 
diff --git a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/recv_device.py b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/recv_device.py
index 63dcd36d6541b695cccd6910b9ba7e10c0229b75..c8c8dde1a1effa81add04665b9bd4f4444e381d8 100644
--- a/tangostationcontrol/tangostationcontrol/devices/base_device_classes/recv_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/base_device_classes/recv_device.py
@@ -1,18 +1,19 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ RECV Abstract Device Server for LOFAR2.0
 
 """
 import logging
 from enum import IntEnum
+
 import numpy
 from attribute_wrapper.attribute_wrapper import AttributeWrapper
+from tango import AttrWriteType, DevString, DevLong
 
 # PyTango imports
 from tango import DebugIt
 from tango.server import command, device_property, attribute
-from tango import AttrWriteType, DevString, DevLong
 
 # Additional import
 from tangostationcontrol.common.constants import (
@@ -24,12 +25,12 @@ from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.frequency_bands import bands
 from tangostationcontrol.common.lofar_logging import device_logging_to_python
 from tangostationcontrol.common.states import DEFAULT_COMMAND_STATES
-from tangostationcontrol.devices.device_decorators import only_in_states
 from tangostationcontrol.devices.base_device_classes.opcua_device import OPCUADevice
+from tangostationcontrol.common.device_decorators import only_in_states
 
 logger = logging.getLogger()
 
-__all__ = ["RECVDevice", "main"]
+__all__ = ["RECVDevice"]
 
 
 class RCUType(IntEnum):
@@ -449,9 +450,6 @@ class RECVDevice(OPCUADevice):
         return bands[filter_name].rcu_band
 
 
-# ----------
-# Run server
-# ----------
 def main(**kwargs):
     """Main function of the RECV module."""
     return entry(RECVDevice, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/calibration.py b/tangostationcontrol/tangostationcontrol/devices/calibration.py
index 238d09cfbfee87ad17b5e59506b0b3c34b0333bd..b99351c7b3ef3d121ad88fccb93e0800f86e66aa 100644
--- a/tangostationcontrol/tangostationcontrol/devices/calibration.py
+++ b/tangostationcontrol/tangostationcontrol/devices/calibration.py
@@ -6,11 +6,10 @@
 """
 import datetime
 import logging
-import numpy
 
+import numpy
 from tango import EventType, Database
 from tango.server import device_property, command, attribute
-
 from tangostationcontrol.common.calibration import (
     CalibrationManager,
     calibrate_RCU_attenuator_dB,
@@ -20,7 +19,7 @@ from tangostationcontrol.common.case_insensitive_dict import (
     CaseInsensitiveDict,
     CaseInsensitiveString,
 )
-from tangostationcontrol.common.entrypoint import entry
+from tangostationcontrol.common.device_decorators import only_in_states
 from tangostationcontrol.common.lofar_logging import (
     device_logging_to_python,
     log_exceptions,
@@ -28,15 +27,14 @@ from tangostationcontrol.common.lofar_logging import (
 from tangostationcontrol.common.proxy import create_device_proxy
 from tangostationcontrol.common.states import DEFAULT_COMMAND_STATES
 from tangostationcontrol.common.type_checking import device_name_matches
-from tangostationcontrol.devices.antennafield.afl import AFL
 from tangostationcontrol.devices.antennafield.afh import AFH
-from tangostationcontrol.devices.device_decorators import only_in_states
+from tangostationcontrol.devices.antennafield.afl import AFL
 from tangostationcontrol.devices.base_device_classes.lofar_device import LOFARDevice
-from tangostationcontrol.devices.sdp.sdp import SDP
 from tangostationcontrol.devices.sdp.firmware import SDPFirmware
+from tangostationcontrol.devices.sdp.sdp import SDP
 
 logger = logging.getLogger()
-__all__ = ["Calibration", "main"]
+__all__ = ["Calibration"]
 
 
 @device_logging_to_python()
@@ -140,7 +138,7 @@ class Calibration(LOFARDevice):
         dtype="DevString",
         mandatory=False,
         update_db=True,
-        default_value="http://object-storage:9000/caltables",
+        default_value="http://s3/caltables",
     )
 
     @attribute(dtype=(str,), max_dim_x=20)
@@ -333,15 +331,3 @@ class Calibration(LOFARDevice):
         self.event_subscriptions = []
         for prx, s in subscriptions:
             prx.unsubscribe_event(s)
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the Calibration module."""
-    return entry(Calibration, **kwargs)
-
-
-if __name__ == "__main__":
-    main()
diff --git a/tangostationcontrol/tangostationcontrol/devices/ccd.py b/tangostationcontrol/tangostationcontrol/devices/ccd.py
index 1b01452f56f530717b67a6f30e9278133b4d2be6..830179e39812fac28b8a247a3ea869cf940c8b17 100644
--- a/tangostationcontrol/tangostationcontrol/devices/ccd.py
+++ b/tangostationcontrol/tangostationcontrol/devices/ccd.py
@@ -1,5 +1,5 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ CCD Device Server for LOFAR2.0
 
@@ -15,17 +15,16 @@ from tango import AttrWriteType
 from tango import DebugIt
 from tango.server import command, attribute, device_property
 from tangostationcontrol.common.constants import DEFAULT_POLLING_PERIOD
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import device_logging_to_python
 from tangostationcontrol.common.states import DEFAULT_COMMAND_STATES
-from tangostationcontrol.devices.device_decorators import only_in_states
 from tangostationcontrol.devices.base_device_classes.opcua_device import OPCUADevice
+from tangostationcontrol.common.device_decorators import only_in_states
 
 # Additional import
 
 logger = logging.getLogger()
 
-__all__ = ["CCD", "main"]
+__all__ = ["CCD"]
 
 
 @device_logging_to_python()
@@ -253,11 +252,3 @@ class CCD(OPCUADevice):
         :return:None
         """
         self.opcua_connection.call_method(["CCD_on"])
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the ObservationControl module."""
-    return entry(CCD, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/configuration.py b/tangostationcontrol/tangostationcontrol/devices/configuration.py
index 3de99aaaa88b0ada268a02e95b6fd218230eae40..703c89f84b927252a47dd299e54e12e3592b522d 100644
--- a/tangostationcontrol/tangostationcontrol/devices/configuration.py
+++ b/tangostationcontrol/tangostationcontrol/devices/configuration.py
@@ -1,5 +1,5 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ Configuration Device Server for LOFAR2.0
 
@@ -15,18 +15,17 @@ from tango.server import attribute, command, device_property
 
 # Additional import
 from tangostationcontrol.common.configuration import StationConfiguration
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import (
     device_logging_to_python,
     log_exceptions,
 )
 from tangostationcontrol.common.states import DEFAULT_COMMAND_STATES
-from tangostationcontrol.devices.device_decorators import only_in_states
 from tangostationcontrol.devices.base_device_classes.lofar_device import LOFARDevice
+from tangostationcontrol.common.device_decorators import only_in_states
 
 logger = logging.getLogger()
 
-__all__ = ["Configuration", "main"]
+__all__ = ["Configuration"]
 
 
 @device_logging_to_python()
@@ -155,11 +154,3 @@ class Configuration(LOFARDevice):
             sort_keys=True,
         )
         self._write_backup_station_configuration(tango_db_configuration)
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the Boot module."""
-    return entry(Configuration, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/docker.py b/tangostationcontrol/tangostationcontrol/devices/docker.py
index a308132a7d674f0c066dd0af263e04ff409bc147..506397d072a77da676d04fd2ebad82ccbae7103e 100644
--- a/tangostationcontrol/tangostationcontrol/devices/docker.py
+++ b/tangostationcontrol/tangostationcontrol/devices/docker.py
@@ -1,5 +1,5 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ Docker Device Server for LOFAR2.0
 
@@ -7,6 +7,7 @@
 
 import asyncio
 import logging
+
 from attribute_wrapper.attribute_wrapper import AttributeWrapper
 from tango import AttrWriteType
 
@@ -15,7 +16,6 @@ from tango.server import device_property
 from tangostationcontrol.clients.docker_client import DockerClient
 
 # Additional import
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import (
     device_logging_to_python,
     log_exceptions,
@@ -24,7 +24,7 @@ from tangostationcontrol.devices.base_device_classes.lofar_device import LOFARDe
 
 logger = logging.getLogger()
 
-__all__ = ["Docker", "main"]
+__all__ = ["Docker"]
 
 
 @device_logging_to_python()
@@ -394,11 +394,3 @@ class Docker(LOFARDevice):
     # --------
     # Commands
     # --------
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the Docker module."""
-    return entry(Docker, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/ec.py b/tangostationcontrol/tangostationcontrol/devices/ec.py
index d466361e18d2d6e35b55cbd3d5f6b6b0b75a368b..2998c3caa594cb998d77bffb641865dd2f4a8a1b 100644
--- a/tangostationcontrol/tangostationcontrol/devices/ec.py
+++ b/tangostationcontrol/tangostationcontrol/devices/ec.py
@@ -1,18 +1,17 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ EC (Environmental Control) Device Server for LOFAR2.0
 
 """
 
 import logging
-import numpy
 
+import numpy
 from attribute_wrapper.attribute_wrapper import AttributeWrapper
 from tango import AttrWriteType
 
 # PyTango imports
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import device_logging_to_python
 from tangostationcontrol.devices.base_device_classes.opcua_device import OPCUADevice
 
@@ -20,7 +19,7 @@ from tangostationcontrol.devices.base_device_classes.opcua_device import OPCUADe
 
 logger = logging.getLogger()
 
-__all__ = ["EC", "main"]
+__all__ = ["EC"]
 
 
 @device_logging_to_python()
@@ -121,13 +120,3 @@ class EC(OPCUADevice):
     # --------
     # Commands
     # --------
-
-    pass
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the EnvironmentalControl module."""
-    return entry(EC, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/observation.py b/tangostationcontrol/tangostationcontrol/devices/observation.py
index 4962f4f5445cc59528ccb324c6e091a2fff97259..9ccd2a821b2d0c3a8b01554ece37c9394e3aecdc 100644
--- a/tangostationcontrol/tangostationcontrol/devices/observation.py
+++ b/tangostationcontrol/tangostationcontrol/devices/observation.py
@@ -1,8 +1,8 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
-from itertools import chain
 import logging
+from itertools import chain
 from time import time
 from typing import Optional
 
@@ -20,19 +20,18 @@ from tangostationcontrol.common.constants import (
     N_point_prop,
     N_pol,
 )
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import device_logging_to_python
 from tangostationcontrol.common.lofar_logging import log_exceptions
 from tangostationcontrol.common.proxy import create_device_proxy
 from tangostationcontrol.common.antennas import device_member_to_full_device_name
 from tangostationcontrol.configuration import ObservationSettings
-from tangostationcontrol.devices.device_decorators import fault_on_error
-from tangostationcontrol.devices.device_decorators import only_in_states
 from tangostationcontrol.devices.base_device_classes.lofar_device import LOFARDevice
+from tangostationcontrol.common.device_decorators import fault_on_error
+from tangostationcontrol.common.device_decorators import only_in_states
 
 logger = logging.getLogger()
 
-__all__ = ["Observation", "main"]
+__all__ = ["Observation"]
 
 
 @device_logging_to_python()
@@ -483,11 +482,3 @@ class Observation(LOFARDevice):
             raise ValueError(f"Unsupported element selection: {element_selection}")
 
         return selection.reshape(nr_antennas, N_elements * N_pol)
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the ObservationControl module."""
-    return entry(Observation, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/observation_control.py b/tangostationcontrol/tangostationcontrol/devices/observation_control.py
index acf35a664d82d2523b3fc14b7e947586f1715f48..ef6eb5fdd377454dbf8795c3eed605e4203104f0 100644
--- a/tangostationcontrol/tangostationcontrol/devices/observation_control.py
+++ b/tangostationcontrol/tangostationcontrol/devices/observation_control.py
@@ -1,5 +1,5 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 import logging
 
@@ -13,19 +13,17 @@ from tango import (
 )
 from tango.server import Device, command, attribute
 from tangostationcontrol.common import ObservationController
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import (
     device_logging_to_python,
     log_exceptions,
 )
 from tangostationcontrol.configuration import ObservationSettings
-from tangostationcontrol.devices.device_decorators import only_when_on
 from tangostationcontrol.devices.base_device_classes.lofar_device import LOFARDevice
-from tangostationcontrol.devices.observation import Observation
+from tangostationcontrol.common.device_decorators import only_when_on
 
 logger = logging.getLogger()
 
-__all__ = ["ObservationControl", "main"]
+__all__ = ["ObservationControl"]
 
 
 @device_logging_to_python()
@@ -196,14 +194,3 @@ class ObservationControl(LOFARDevice):
     @log_exceptions()
     def is_any_observation_running(self) -> DevBoolean:
         return len(self._observation_controller.running_observations) > 0
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the ObservationControl module."""
-
-    # The ObservationControl device spawns Observation devices, so we manage
-    # both in this DeviceServer.
-    return entry((ObservationControl, Observation), verbose=True, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/pcon.py b/tangostationcontrol/tangostationcontrol/devices/pcon.py
index f42339d6e6389d8dc041befa06533b3843babc25..7bc383f9af821caf43dbfba3730b9cd5614d4739 100644
--- a/tangostationcontrol/tangostationcontrol/devices/pcon.py
+++ b/tangostationcontrol/tangostationcontrol/devices/pcon.py
@@ -1,5 +1,5 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ PCON Device Server for LOFAR2.0
 
@@ -8,23 +8,22 @@
 import logging
 
 import numpy
-from pysmi import debug
 from attribute_wrapper.attribute_wrapper import AttributeWrapper
+from pysmi import debug
+from tangostationcontrol.clients.snmp.attribute_classes import PCON_sim
 
 # Additional import
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import (
     device_logging_to_python,
     log_exceptions,
 )
 from tangostationcontrol.devices.base_device_classes.snmp_device import SNMPDevice
-from tangostationcontrol.clients.snmp.attribute_classes import PCON_sim
 
 debug.setLogger(debug.Debug("searcher", "compiler", "borrower", "reader"))
 
 logger = logging.getLogger()
 
-__all__ = ["PCON", "main"]
+__all__ = ["PCON"]
 
 
 @device_logging_to_python()
@@ -50,11 +49,3 @@ class PCON(SNMPDevice):
         """user code here. is called when the state is set to STANDBY"""
 
         super().configure_for_initialise(simulator_class=PCON_sim)
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the PCON module."""
-    return entry(PCON, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/psoc.py b/tangostationcontrol/tangostationcontrol/devices/psoc.py
index 147e6d4596f5f3c508cd076c369368a13fca5001..b671e7275aadc6e06a0923b9f6540e07327e65d1 100644
--- a/tangostationcontrol/tangostationcontrol/devices/psoc.py
+++ b/tangostationcontrol/tangostationcontrol/devices/psoc.py
@@ -1,5 +1,5 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ PSOC Device Server for LOFAR2.0
 
@@ -9,26 +9,25 @@ import logging
 from datetime import timedelta
 
 import numpy
-from pysmi import debug
 from attribute_wrapper.attribute_wrapper import AttributeWrapper
+from pysmi import debug
 from tango.server import device_property, command
+from tangostationcontrol.clients.snmp.attribute_classes import PSOC_sim
 
 # Additional import
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import (
     device_logging_to_python,
     log_exceptions,
 )
-from tangostationcontrol.devices.base_device_classes.snmp_device import SNMPDevice
 from tangostationcontrol.common.states import DEFAULT_COMMAND_STATES
-from tangostationcontrol.devices.device_decorators import only_in_states
-from tangostationcontrol.clients.snmp.attribute_classes import PSOC_sim
+from tangostationcontrol.devices.base_device_classes.snmp_device import SNMPDevice
+from tangostationcontrol.common.device_decorators import only_in_states
 
 debug.setLogger(debug.Debug("searcher", "compiler", "borrower", "reader"))
 
 logger = logging.getLogger()
 
-__all__ = ["PSOC", "main"]
+__all__ = ["PSOC"]
 
 
 @device_logging_to_python()
@@ -182,11 +181,3 @@ class PSOC(SNMPDevice):
     def power_ccd_off(self):
         """Turn off 230V to the CCD"""
         self.socket_off("socket_1")
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the PSOC module."""
-    return entry(PSOC, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/recv/recvh.py b/tangostationcontrol/tangostationcontrol/devices/recv/recvh.py
index 902f4552ee724b0c6ac07928816e09c268ab5dbf..b4315744bd138ff6a1dc84c30c77603cf68c60c6 100644
--- a/tangostationcontrol/tangostationcontrol/devices/recv/recvh.py
+++ b/tangostationcontrol/tangostationcontrol/devices/recv/recvh.py
@@ -1,15 +1,15 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ Receiver Unit High Device Server for LOFAR2.0
 
 """
 import numpy
 from attribute_wrapper.attribute_wrapper import AttributeWrapper
+from tango import AttrWriteType, DevVarFloatArray
 
 # PyTango imports
 from tango.server import command, device_property
-from tango import AttrWriteType, DevVarFloatArray
 
 # Additional import
 from tangostationcontrol.common.constants import (
@@ -18,12 +18,10 @@ from tangostationcontrol.common.constants import (
     N_pol,
     N_rcu_inp,
 )
-
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import device_logging_to_python
 from tangostationcontrol.devices.base_device_classes.recv_device import RECVDevice
 
-__all__ = ["RECVH", "main"]
+__all__ = ["RECVH"]
 
 
 @device_logging_to_python()
@@ -221,11 +219,3 @@ class RECVH(RECVDevice):
         HBAT_bf_delay_steps = self._calculate_HBAT_bf_delay_steps(delays)
 
         return HBAT_bf_delay_steps.flatten()
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the RECVH module."""
-    return entry(RECVH, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/recv/recvl.py b/tangostationcontrol/tangostationcontrol/devices/recv/recvl.py
index 4f1e0f34b561df96d967d7776826f4979291b912..b6a31130ac7fab3e6eb4f71bb2ba94dfdd696fc0 100644
--- a/tangostationcontrol/tangostationcontrol/devices/recv/recvl.py
+++ b/tangostationcontrol/tangostationcontrol/devices/recv/recvl.py
@@ -1,15 +1,14 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ Receiver Unit Low Device Server for LOFAR2.0
 
 """
 
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import device_logging_to_python
 from tangostationcontrol.devices.base_device_classes.recv_device import RECVDevice
 
-__all__ = ["RECVL", "main"]
+__all__ = ["RECVL"]
 
 
 @device_logging_to_python()
@@ -26,11 +25,3 @@ class RECVL(RECVDevice):
     # ----------
     # Attributes
     # ----------
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the RECVL module."""
-    return entry(RECVL, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py b/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py
index d6c4122599a96bcdc26e6d1347048307d4417a37..42938fd3c1ee3b5a0a5cdd8925157207460bc156 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py
@@ -20,7 +20,6 @@ from tango import (
 
 # PyTango imports
 from tango.server import device_property, command, attribute
-
 from tangostationcontrol.common.constants import (
     N_pn,
     A_pn,
@@ -33,13 +32,12 @@ from tangostationcontrol.common.constants import (
 )
 
 # Additional import
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import log_exceptions
 from tangostationcontrol.common.proxy import create_device_proxy
 from tangostationcontrol.common.sdp import phases_to_weights
 from tangostationcontrol.devices.base_device_classes.opcua_device import OPCUADevice
 
-__all__ = ["Beamlet", "main"]
+__all__ = ["Beamlet"]
 
 logger = logging.getLogger()
 
@@ -630,11 +628,3 @@ class Beamlet(OPCUADevice):
         bf_weights = self._calculate_bf_weights(delays, beamlet_frequencies)
 
         return bf_weights.flatten()
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the SST Device module."""
-    return entry(Beamlet, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/bst.py b/tangostationcontrol/tangostationcontrol/devices/sdp/bst.py
index 621c3ef6e4a74128b4112f863e54bbe0d67e5ad5..36dc2fd27e626aa7be767208f2359db9545c2fa4 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/bst.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/bst.py
@@ -1,16 +1,15 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ BST Device Server for LOFAR2.0
 
 """
 
 import numpy
-from lofar_station_client.statistics.collector import BSTCollector
 from attribute_wrapper.attribute_wrapper import AttributeWrapper
+from lofar_station_client.statistics.collector import BSTCollector
 from tango import AttrWriteType
 from tango.server import device_property, attribute
-
 from tangostationcontrol.clients.opcua_client import OPCUAConnection
 from tangostationcontrol.clients.statistics.client import StatisticsClient
 from tangostationcontrol.common.constants import (
@@ -20,10 +19,9 @@ from tangostationcontrol.common.constants import (
 )
 
 # Own imports
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.devices.sdp.statistics import Statistics
 
-__all__ = ["BST", "main"]
+__all__ = ["BST"]
 
 
 class BST(Statistics):
@@ -190,11 +188,3 @@ class BST(Statistics):
     # --------
     # Commands
     # --------
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the BST Device module."""
-    return entry(BST, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py b/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py
index 022263aab3eeeac20823a37a1b50e1368deab4f9..53f2cbe762158488b02a08bb97a87113b5139a5d 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py
@@ -11,7 +11,6 @@ import numpy
 
 # PyTango imports
 from tango.server import attribute, AttrWriteType
-
 from tangostationcontrol.beam.delays import Delays
 from tangostationcontrol.beam.managers import DigitalBeamManager
 from tangostationcontrol.common.antennas import antenna_set_to_mask
@@ -22,14 +21,13 @@ from tangostationcontrol.common.constants import (
 )
 
 # Additional import
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import log_exceptions
 from tangostationcontrol.devices.base_device_classes.beam_device import BeamDevice
 from tangostationcontrol.devices.types import DeviceTypes
 
 logger = logging.getLogger()
 
-__all__ = ["DigitalBeam", "main"]
+__all__ = ["DigitalBeam"]
 
 
 class DigitalBeam(BeamDevice):
@@ -195,13 +193,3 @@ class DigitalBeam(BeamDevice):
 
         # relative positions of each antenna
         self._beam_manager.relative_antenna_positions = antenna_itrf - reference_itrf
-
-
-# ----------
-# Run server
-# ----------
-
-
-def main(**kwargs):
-    """Main function of the SST Device module."""
-    return entry(DigitalBeam, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py b/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py
index 37c87ccf3abda72d329ca956b974b46f93c090e5..13bac5ed8b182a7990b61ae2d2f3318447b27035 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/firmware.py
@@ -1,11 +1,12 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ SDP Firmware Device Server for LOFAR2.0
 
 """
 
 import logging
+
 import numpy
 from attribute_wrapper.attribute_wrapper import AttributeWrapper
 from tango import AttrWriteType
@@ -22,16 +23,13 @@ from tangostationcontrol.common.constants import (
     N_subbands,
     DEFAULT_POLLING_PERIOD,
 )
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import device_logging_to_python
-
 from tangostationcontrol.devices.base_device_classes.opcua_device import OPCUADevice
-
 from tangostationcontrol.devices.types import DeviceTypes
 
 logger = logging.getLogger()
 
-__all__ = ["SDPFirmware", "main"]
+__all__ = ["SDPFirmware"]
 
 
 @device_logging_to_python()
@@ -306,11 +304,3 @@ class SDPFirmware(OPCUADevice):
     # --------
     # Commands
     # --------
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the SDP Firmware module."""
-    return entry(SDPFirmware, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py
index 9b6edf4164c4c6817bff92440ce42a87738dba01..11b128bd2ebf42c0a7af1924c1fb7a2a4b2a4f06 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py
@@ -24,13 +24,12 @@ from tangostationcontrol.common.constants import (
     DEFAULT_POLLING_PERIOD,
     SDP_UNIT_WEIGHT,
 )
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import device_logging_to_python
 from tangostationcontrol.common.sdp import subband_frequencies
-from tangostationcontrol.devices.device_decorators import TimeIt
 from tangostationcontrol.devices.base_device_classes.opcua_device import OPCUADevice
+from tangostationcontrol.common.device_decorators import TimeIt
 
-__all__ = ["SDP", "main"]
+__all__ = ["SDP"]
 
 
 @device_logging_to_python()
@@ -514,11 +513,3 @@ class SDP(OPCUADevice):
     # --------
     # Support functions
     # --------
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the SDP module."""
-    return entry(SDP, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py
index 33add52eb86be1302fd2f9be0e5ae294245820cf..91168f9b0bea2d644371afe0faa8aef6e42922cc 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sst.py
@@ -1,27 +1,25 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ SST Device Server for LOFAR2.0
 
 """
 
 import numpy
-from lofar_station_client.statistics.collector import SSTCollector
 from attribute_wrapper.attribute_wrapper import AttributeWrapper
+from lofar_station_client.statistics.collector import SSTCollector
 
 # PyTango imports
 from tango import AttrWriteType
 from tango.server import device_property, attribute
-
 from tangostationcontrol.clients.opcua_client import OPCUAConnection
 from tangostationcontrol.clients.statistics.client import StatisticsClient
 from tangostationcontrol.common.constants import N_pn, MAX_INPUTS, N_subbands
 
 # Additional import
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.devices.sdp.statistics import Statistics
 
-__all__ = ["SST", "main"]
+__all__ = ["SST"]
 
 
 class SST(Statistics):
@@ -216,11 +214,3 @@ class SST(Statistics):
     # --------
     # Commands
     # --------
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the SST Device module."""
-    return entry(SST, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py b/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py
index fcedd5c1bffa9cc1bf7fe2daaab74da8326516f4..1347339ebb75696c9f113e0acf0b675a525924f8 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py
@@ -24,10 +24,9 @@ from tangostationcontrol.common.constants import (
 )
 
 # Additional import
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.devices.sdp.statistics import Statistics
 
-__all__ = ["XST", "main"]
+__all__ = ["XST"]
 
 
 class XST(Statistics):
@@ -716,11 +715,3 @@ class XST(Statistics):
     # --------
     # Commands
     # --------
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the XST Device module."""
-    return entry(XST, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/station_manager.py b/tangostationcontrol/tangostationcontrol/devices/station_manager.py
index dc5d6c3f2cb3aa2432903e04bcdf7c80cd787da7..c182c12738c7749e93fc5bb6b1211982580c23a9 100644
--- a/tangostationcontrol/tangostationcontrol/devices/station_manager.py
+++ b/tangostationcontrol/tangostationcontrol/devices/station_manager.py
@@ -1,5 +1,5 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ StationManager Device Server for LOFAR2.0
 
@@ -7,29 +7,27 @@
 
 import logging
 
+from tango import DebugIt, DevState, DevFailed, Except
+
 # pytango imports
 from tango.server import attribute, command, device_property
-from tango import DebugIt, DevState, DevFailed, Except
 
 # Additional import
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import device_logging_to_python
 from tangostationcontrol.common.lofar_logging import exception_to_str
 from tangostationcontrol.common.lofar_logging import log_exceptions
-from tangostationcontrol.devices.base_device_classes.lofar_device import LOFARDevice
-from tangostationcontrol.devices.base_device_classes.power_hierarchy import (
-    PowerHierarchyDevice,
-)
-
 from tangostationcontrol.common.states import (
     StationState,
     ALLOWED_STATION_STATE_TRANSITIONS,
 )
-
+from tangostationcontrol.devices.base_device_classes.lofar_device import LOFARDevice
+from tangostationcontrol.devices.base_device_classes.power_hierarchy import (
+    PowerHierarchyDevice,
+)
 
 logger = logging.getLogger()
 
-__all__ = ["StationManager", "main"]
+__all__ = ["StationManager"]
 
 
 @device_logging_to_python()
@@ -305,11 +303,3 @@ class StationManager(LOFARDevice):
 
         # update the station_state variable when successful
         self.station_state = StationState.ON
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the Docker module."""
-    return entry(StationManager, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/temperature_manager.py b/tangostationcontrol/tangostationcontrol/devices/temperature_manager.py
index a19d090f5e26e56a14ebddd01a5f7349d4461a8f..68c8be644ad8193487d5eb898e6d33eb999edf75 100644
--- a/tangostationcontrol/tangostationcontrol/devices/temperature_manager.py
+++ b/tangostationcontrol/tangostationcontrol/devices/temperature_manager.py
@@ -1,5 +1,5 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ overtemperature managing Device Server for LOFAR2.0
 
@@ -7,7 +7,6 @@
 import logging
 
 import numpy as np
-
 from tango import (
     Util,
     Database,
@@ -22,7 +21,6 @@ from tango.server import command
 
 # Additional import
 from tangostationcontrol.common.constants import DEFAULT_POLLING_PERIOD
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import (
     device_logging_to_python,
     log_exceptions,
@@ -32,7 +30,7 @@ from tangostationcontrol.devices.base_device_classes.lofar_device import LOFARDe
 
 logger = logging.getLogger()
 
-__all__ = ["TemperatureManager", "main"]
+__all__ = ["TemperatureManager"]
 
 
 class AttrInfo:
@@ -225,11 +223,3 @@ class TemperatureManager(LOFARDevice):
         logger.warning(
             "Temperature alarm triggered auto shutdown of all hardware devices"
         )
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the temperature manager module."""
-    return entry(TemperatureManager, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/tilebeam.py b/tangostationcontrol/tangostationcontrol/devices/tilebeam.py
index 80dad276393a5793103651258d086bf5209d6456..f5538b24fa787d2fb2b8f6ba48b5ea208c41ec63 100644
--- a/tangostationcontrol/tangostationcontrol/devices/tilebeam.py
+++ b/tangostationcontrol/tangostationcontrol/devices/tilebeam.py
@@ -11,7 +11,6 @@ import logging
 from tangostationcontrol.beam.delays import Delays
 from tangostationcontrol.beam.managers import TileBeamManager
 from tangostationcontrol.common.constants import N_xyz, N_elements
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import (
     device_logging_to_python,
     log_exceptions,
@@ -20,7 +19,7 @@ from tangostationcontrol.devices.base_device_classes.beam_device import BeamDevi
 
 logger = logging.getLogger()
 
-__all__ = ["TileBeam", "main"]
+__all__ = ["TileBeam"]
 
 
 @device_logging_to_python()
@@ -67,11 +66,3 @@ class TileBeam(BeamDevice):
             antenna_reference_itrf[tile] + hbat_antenna_itrf_offsets[tile]
             for tile in range(self._beam_manager.nr_tiles)
         ]
-
-
-# ----------
-# Run server
-# ----------
-def main(**kwargs):
-    """Main function of the ObservationControl module."""
-    return entry(TileBeam, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/devices/unb2.py b/tangostationcontrol/tangostationcontrol/devices/unb2.py
index c6f42aec3c37bfe42c255f8012a83c1fedde815e..bd930b5b3b6cef35982c601734882182981b3b93 100644
--- a/tangostationcontrol/tangostationcontrol/devices/unb2.py
+++ b/tangostationcontrol/tangostationcontrol/devices/unb2.py
@@ -1,5 +1,5 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
 """ UNB2 Device Server for LOFAR2.0
 
@@ -18,15 +18,14 @@ from tangostationcontrol.common.constants import (
     N_qsfp,
     DEFAULT_POLLING_PERIOD,
 )
-from tangostationcontrol.common.entrypoint import entry
 from tangostationcontrol.common.lofar_logging import device_logging_to_python
 from tangostationcontrol.common.states import DEFAULT_COMMAND_STATES
-from tangostationcontrol.devices.device_decorators import only_in_states
 from tangostationcontrol.devices.base_device_classes.opcua_device import OPCUADevice
+from tangostationcontrol.common.device_decorators import only_in_states
 
 # Additional import
 
-__all__ = ["UNB2", "main"]
+__all__ = ["UNB2"]
 
 
 @device_logging_to_python()
@@ -455,11 +454,3 @@ class UNB2(OPCUADevice):
         :return:None
         """
         self.opcua_connection.call_method(["UNB2_on"])
-
-
-# ----------
-# Run server
-# ----------
-def main(args=None, **kwargs):
-    """Main function of the UNB2 module."""
-    return entry(UNB2, **kwargs)
diff --git a/tangostationcontrol/tangostationcontrol/metrics/__init__.py b/tangostationcontrol/tangostationcontrol/metrics/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b360c8faa7f6508ded7d1f0121d70b2a8a0f33d7
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/metrics/__init__.py
@@ -0,0 +1,9 @@
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
+
+from ._collectors import DeviceMetricCollector
+from ._decorators import device_metrics, DeviceMetricsAttribute
+
+labelnames = DeviceMetricsAttribute.labelnames
+
+__all__ = ["device_metrics", "labelnames", "DeviceMetricCollector"]
diff --git a/tangostationcontrol/tangostationcontrol/metrics/_collectors.py b/tangostationcontrol/tangostationcontrol/metrics/_collectors.py
new file mode 100644
index 0000000000000000000000000000000000000000..ea4dfa93d96a9ffb0bf19a228ebe445721832b84
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/metrics/_collectors.py
@@ -0,0 +1,43 @@
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
+import time
+
+from prometheus_client import REGISTRY
+from prometheus_client.core import GaugeMetricFamily
+from prometheus_client.metrics_core import InfoMetricFamily
+
+from tangostationcontrol import __version__ as version
+from ._decorators import DeviceMetricsAttribute
+
+
+class DeviceMetricCollector:
+    """
+    Collects general device metrics for the prometheus metrics endpoint.
+    """
+
+    _devices = []
+
+    @staticmethod
+    def add_device(device):
+        DeviceMetricCollector._devices.append(device)
+
+    def collect(self):
+        uptime_metric = GaugeMetricFamily(
+            "device_uptime_seconds",
+            "How long this software device has been running (wall-clock time, in seconds)",
+            labels=DeviceMetricsAttribute.labelnames,
+        )
+        version_metric = InfoMetricFamily(
+            "device", "Version of the device", labels=DeviceMetricsAttribute.labelnames
+        )
+        t = time.time()
+        for device in self._devices:
+            if not hasattr(device, "metric_labels"):
+                continue
+            uptime_metric.add_metric(device.metric_labels, t - device.device_start_time)
+            version_metric.add_metric(device.metric_labels, dict(version=version))
+        yield uptime_metric
+        yield version_metric
+
+
+REGISTRY.register(DeviceMetricCollector())
diff --git a/tangostationcontrol/tangostationcontrol/metrics/_decorators.py b/tangostationcontrol/tangostationcontrol/metrics/_decorators.py
new file mode 100644
index 0000000000000000000000000000000000000000..c2b64134f851cd4547a28b64419ae1da96175b14
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/metrics/_decorators.py
@@ -0,0 +1,133 @@
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
+import fnmatch
+import functools
+import logging
+
+from prometheus_client import Gauge, Enum, Counter
+from tango import Attribute, DevState
+from tango.server import Device, attribute
+
+logger = logging.getLogger()
+
+
+class DeviceMetricsAttribute:
+    """
+    Injects code to provide prometheus metrics for devices annotated with device_metrics
+    """
+
+    def __init__(self, exclude=None):
+        self.exclude = [] if exclude is None else exclude
+
+    labelnames = ["domain", "family", "member", "device_class"]
+    ds_state_str = Enum(
+        "device_state_str",
+        "State of the device",
+        labelnames,
+        states=list(DevState.names),
+    )
+    ds_state = Gauge("device_state", "State of the device", labelnames)
+    dev_access_count = Counter(
+        "device_access",
+        "How often this software device was accessed for commands or attributes",
+        labelnames,
+    )
+
+    @staticmethod
+    def get_device_attributes(cls):
+        return [
+            v
+            for k, v in cls.__dict__.items()
+            if isinstance(
+                v,
+                (
+                    attribute,
+                    Attribute,
+                ),
+            )
+        ]
+
+    def exclude_attr(self, name):
+        for p in self.exclude:
+            yield fnmatch.fnmatch(name, p)
+
+    @staticmethod
+    def wrap_method(cls, func, wrapper, post_execute=True, double_wrap=False):
+        if not double_wrap and hasattr(func, "__wrapped__"):
+            return
+
+        def decorated_pre(instance, *args, **kwargs):
+            wrapper(instance)
+            func(instance, *args, **kwargs)
+
+        def decorated_post(instance, *args, **kwargs):
+            wrapper(instance, *args, **kwargs)
+            func(instance, *args, **kwargs)
+
+        setattr(
+            cls,
+            func.__name__,
+            functools.update_wrapper(
+                decorated_post if post_execute else decorated_pre, func
+            ),
+        )
+
+    @staticmethod
+    def init_device_wrapper():
+        def new_init_device(instance):
+            metric_values = instance.get_name().split("/")
+            metric_values.append(str(instance.get_device_class().get_name()))
+            instance.metric_labels = metric_values
+
+        return new_init_device
+
+    def set_state_wrapper(self):
+        def new_set_state(instance, new_state):
+            try:
+                self.ds_state_str.labels(*instance.metric_labels).state(new_state.name)
+                self.ds_state.labels(*instance.metric_labels).set(new_state)
+            except ValueError as e:
+                logger.warning(e)
+
+        return new_set_state
+
+    def __call__(self, cls):
+        # we'll be doing very weird things if this class isn't
+        if not issubclass(cls, Device):
+            raise ValueError(
+                "device_metrics decorator is to be used on Tango Device classes only."
+            )
+
+        for v in self.get_device_attributes(cls):
+            if any(self.exclude_attr(v.attr_name)):
+                # @todo: add code in cae the attribute should be excluded
+                pass
+            else:
+                # @todo: add code in cae the attribute should be included
+                pass
+
+        cls.metric_labelnames = self.labelnames
+
+        self.wrap_method(
+            cls,
+            cls.init_device,
+            self.init_device_wrapper(),
+            post_execute=False,
+            double_wrap=True,
+        )
+
+        self.wrap_method(
+            cls,
+            cls.always_executed_hook,
+            lambda instance: self.dev_access_count.labels(
+                *instance.metric_labels
+            ).inc(),
+        )
+
+        self.wrap_method(cls, cls.set_state, self.set_state_wrapper())
+
+        return cls
+
+
+def device_metrics(*args, **kwargs):
+    return DeviceMetricsAttribute(*args, **kwargs)
diff --git a/tangostationcontrol/test-requirements.txt b/tangostationcontrol/test-requirements.txt
index 9c0aa76bdf9fb625f94179426336d5774092d13b..450da51eaace2090c33ccba0ea08fb052046d18e 100644
--- a/tangostationcontrol/test-requirements.txt
+++ b/tangostationcontrol/test-requirements.txt
@@ -24,7 +24,6 @@ testtools>=2.4.0 # MIT
 timeout-decorator>=0.5.0  # MIT
 xenon>=0.8.0 # MIT
 prometheus_client # Apache-2.0
-python-logstash-async # MIT
 pytest>=7.3.0 # MIT
 pytest-forked>=1.6.0 # MIT
 pytest-cov >= 3.0.0 # MIT
diff --git a/tangostationcontrol/test/common/test_calibration.py b/tangostationcontrol/test/common/test_calibration.py
index a87b4dc6553994e2d7c31a334a20d0318e8e894a..1916e557dd6c66af04b65ba1290c18c7e08bbf97 100644
--- a/tangostationcontrol/test/common/test_calibration.py
+++ b/tangostationcontrol/test/common/test_calibration.py
@@ -8,6 +8,7 @@ from test import base
 import numpy
 from numpy.testing import assert_array_equal
 
+from tangostationcontrol.common import consul
 from tangostationcontrol.common.calibration import (
     delay_compensation,
     loss_compensation,
@@ -24,7 +25,12 @@ class MockMinio:
         self.args = kwargs
 
 
+def new_lookup_service(*args, **kwargs):
+    yield consul.Service(host="test", port=9000, addr="test")
+
+
 @patch("tangostationcontrol.common.calibration.Minio")
+@patch("tangostationcontrol.common.consul.lookup_service", new=new_lookup_service)
 @patch.dict(
     os.environ,
     {"MINIO_ROOT_USER": "my_user", "MINIO_ROOT_PASSWORD": "my_passwd"},
diff --git a/tangostationcontrol/test/configuration/_mock_requests.py b/tangostationcontrol/test/configuration/_mock_requests.py
deleted file mode 100644
index d89e75cb5c66a1939f4f6f8a3ea49be4595a2747..0000000000000000000000000000000000000000
--- a/tangostationcontrol/test/configuration/_mock_requests.py
+++ /dev/null
@@ -1,246 +0,0 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
-
-import json
-
-POINTING_SCHEMA = """
-{
-  "type": "object",
-  "required": [
-    "angle1",
-    "angle2",
-    "direction_type"
-  ],
-  "properties": {
-    "angle1": {
-      "default": 0.6624317181687094,
-      "description": "First angle (e.g. RA)",
-      "title": "Angle 1",
-      "type": "number"
-    },
-    "angle2": {
-      "default": 1.5579526427549426,
-      "description": "Second angle (e.g. DEC)",
-      "title": "Angle 2",
-      "type": "number"
-    },
-    "direction_type": {
-      "default": "J2000",
-      "description": "",
-      "enum": [
-        "J2000",
-        "AZELGEO",
-        "LMN",
-        "SUN",
-        "MOON",
-        "MERCURY",
-        "VENUS",
-        "MARS",
-        "JUPITER",
-        "SATURN",
-        "URANUS",
-        "NEPTUNE",
-        "PLUTO"
-      ],
-      "title": "Reference frame",
-      "type": "string"
-    }
-  }
-}
-"""
-
-SAP_SCHEMA = """
-{
-  "$schema": "http://json-schema.org/draft-07/schema",
-  "type": "object",
-  "required": [
-    "subbands",
-    "pointing"
-  ],
-  "properties": {
-    "subbands": {
-      "type": "array",
-      "minItems": 1,
-      "items": {
-        "type": "number"
-      }
-    },
-    "pointing": {
-      "$ref": "pointing.json"
-    }
-  }
-}
-"""
-
-HBA_SCHEMA = """
-{
-  "$schema": "http://json-schema.org/draft-07/schema",
-  "type": "object",
-  "required": [
-    "tile_beam"
-  ],
-  "properties": {
-    "tile_beam": {
-      "$ref": "pointing.json"
-    },
-    "dab_filter": {
-      "type": "boolean",
-      "default": false,
-      "description": "Enable hardware filter on DAB frequencies"
-    },
-    "element_selection": {
-      "type": "string",
-      "default": "ALL",
-      "description": "Which element(s) to enable in each tile",
-      "enum": [
-        "ALL",
-        "GENERIC_201512"
-      ]
-    }
-  }
-}
-"""
-
-DITHERING_SCHEMA = """
-{
-  "$schema": "http://json-schema.org/draft-07/schema",
-  "type": "object",
-  "description": "Settings for adding dithering to the signal to increase its linearity",
-  "required": [
-    "enabled"
-  ],
-  "properties": {
-    "enabled": {
-      "type": "boolean",
-      "default": false
-    },
-    "power": {
-      "type": "number",
-      "description": "Power of dithering signal, in dBm",
-      "minimum": -25.0,
-      "maximum": -4.0,
-      "default": -4.0
-    },
-    "frequency": {
-      "type": "number",
-      "description": "Frequency of dithering signal, in Hz",
-      "default": 102000000
-    }
-  }
-}
-"""
-
-OBSERVATION_SETTINGS_SCHEMA = """
-{
-  "$schema": "http://json-schema.org/draft-07/schema",
-  "type": "object",
-  "required": [
-    "observation_id",
-    "stop_time",
-    "antenna_field",
-    "antenna_set",
-    "filter",
-    "SAPs"
-  ],
-  "properties": {
-    "observation_id": {
-      "type": "number",
-      "minimum": 1
-    },
-    "start_time": {
-      "type": "string",
-      "format": "date-time",
-      "default": "1970-01-01T00:00:00"
-    },
-    "stop_time": {
-      "type": "string",
-      "format": "date-time"
-    },
-    "lead_time": {
-      "type": "number",
-      "description": "Number of seconds to start before the provided start time, to account for initialising the on-line signal chain, and for possibly negative geometrical delay compensation.",
-      "default": 2.0,
-      "minimum": 0
-    },
-    "antenna_field": {
-      "default": "HBA",
-      "description": "Antenna field to use",
-      "type": "string",
-      "enum": [
-        "LBA",
-        "HBA",
-        "HBA0",
-        "HBA1"
-      ]
-    },
-    "antenna_set": {
-      "default": "ALL",
-      "description": "Fields & antennas to use",
-      "type": "string",
-      "enum": [
-        "ALL",
-        "INNER",
-        "OUTER",
-        "SPARSE_EVEN",
-        "SPARSE_ODD"
-      ]
-    },
-    "dithering": {
-      "$ref": "dithering.json"
-    },
-    "filter": {
-      "type": "string",
-      "enum": [
-        "LBA_10_90",
-        "LBA_10_70",
-        "LBA_30_90",
-        "LBA_30_70",
-        "HBA_170_230",
-        "HBA_110_190",
-        "HBA_210_250"
-      ]
-    },
-    "SAPs": {
-      "type": "array",
-      "minItems": 1,
-      "items": {
-        "$ref": "sap.json"
-      }
-    },
-    "first_beamlet": {
-      "type": "number",
-      "default": 0,
-      "minimum": 0
-    },
-    "HBA": {
-      "$ref": "hba.json"
-    }
-  }
-}
-"""
-
-
-def mocked_requests_get(*args, **kwargs):
-    class MockResponse:
-        def __init__(self, text, status_code):
-            self.text = text
-            self.status_code = status_code
-
-        def raise_for_status(self):
-            pass
-
-        def json(self):
-            return json.loads(self.text)
-
-    if args[0] == "http://http-json-schemas/pointing.json":
-        return MockResponse(POINTING_SCHEMA, 200)
-    elif args[0] == "http://http-json-schemas/sap.json":
-        return MockResponse(SAP_SCHEMA, 200)
-    elif args[0] == "http://http-json-schemas/hba.json":
-        return MockResponse(HBA_SCHEMA, 200)
-    elif args[0] == "http://http-json-schemas/dithering.json":
-        return MockResponse(DITHERING_SCHEMA, 200)
-    elif args[0] == "http://http-json-schemas/observation-settings.json":
-        return MockResponse(OBSERVATION_SETTINGS_SCHEMA, 200)
-
-    return MockResponse(None, 404)
diff --git a/tangostationcontrol/test/configuration/test_observation_settings.py b/tangostationcontrol/test/configuration/test_observation_settings.py
index 4f876563126bbe5e3c670bbe15a1d211bae18a62..6220062ee59e905d6ba0db31a93e7fb526234de0 100644
--- a/tangostationcontrol/test/configuration/test_observation_settings.py
+++ b/tangostationcontrol/test/configuration/test_observation_settings.py
@@ -1,22 +1,17 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
+import json
 from datetime import datetime
-from unittest import mock
 
-import json
-import requests
-from jsonschema.exceptions import ValidationError, RefResolutionError
+from jsonschema.exceptions import ValidationError
 
 from tangostationcontrol.configuration import Pointing, ObservationSettings, Sap
-
 from test import base
-from test.configuration._mock_requests import mocked_requests_get
 
 
-@mock.patch("requests.get", side_effect=mocked_requests_get)
 class TestObservationSettings(base.TestCase):
-    def test_from_json(self, _):
+    def test_from_json(self):
         sut = ObservationSettings.from_json(
             '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", '
             '"antenna_field": "HBA", '
@@ -52,7 +47,7 @@ class TestObservationSettings(base.TestCase):
 
         self.assertEqual(sut.first_beamlet, 2)
 
-    def test_from_json_type_missmatch(self, _):
+    def test_from_json_type_missmatch(self):
         for json_str in [
             # observation_id
             '{"observation_id": "3", "stop_time": "2012-04-23T18:25:43", "antenna_field": "HBA", "antenna_set": "ALL", "filter": "HBA_110_190","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}], "first_beamlet": 2}',
@@ -70,7 +65,7 @@ class TestObservationSettings(base.TestCase):
             with self.assertRaises((ValidationError, ValueError), msg=f"{json_str}"):
                 ObservationSettings.from_json(json_str)
 
-    def test_from_json_missing_fields(self, _):
+    def test_from_json_missing_fields(self):
         complete_json = '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", "antenna_field": "HBA", "antenna_set": "ALL", "filter": "HBA_110_190","SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}], "HBA": {"tile_beam": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}, "first_beamlet": 2}'
 
         for field in (
@@ -92,7 +87,7 @@ class TestObservationSettings(base.TestCase):
             ):
                 ObservationSettings.from_json(json_str)
 
-    def test_to_json(self, _):
+    def test_to_json(self):
         sut = ObservationSettings(
             5,
             datetime.fromisoformat("2022-10-26T11:35:54.704150"),
@@ -116,19 +111,7 @@ class TestObservationSettings(base.TestCase):
             '"angle2": 4.4, "direction_type": "MOON"}}], "first_beamlet": 0}',
         )
 
-    def test_throw_exception_if_schema_not_available(self, mock_get):
-        ObservationSettings.VALIDATOR = None
-        mock_get.side_effect = requests.exceptions.Timeout
-        with self.assertRaises(RefResolutionError):
-            ObservationSettings.from_json(
-                '{"observation_id": 3, "stop_time": "2012-04-23T18:25:43", '
-                '"antenna_field": "HBA", '
-                '"antenna_set": "ALL", "filter": "HBA_110_190",'
-                '"SAPs": [{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}]}'
-            )
-        self.assertEqual(5, mock_get.call_count)
-
-    def test_throw_wrong_instance(self, _):
+    def test_throw_wrong_instance(self):
         for json_str in [
             '{"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}',
             '{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}',
diff --git a/tangostationcontrol/test/configuration/test_pointing.py b/tangostationcontrol/test/configuration/test_pointing.py
index a5ca5955d508c8bd4744bb0f049ae793f212abeb..1c864b99f0d7cbe2839e39c00c6914f72d1c85ca 100644
--- a/tangostationcontrol/test/configuration/test_pointing.py
+++ b/tangostationcontrol/test/configuration/test_pointing.py
@@ -1,27 +1,21 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
-from unittest import mock
-
-import requests
-from jsonschema.exceptions import ValidationError, RefResolutionError
+from jsonschema.exceptions import ValidationError
 
 from tangostationcontrol.configuration import Pointing
-
 from test import base
-from test.configuration._mock_requests import mocked_requests_get
 
 
-@mock.patch("requests.get", side_effect=mocked_requests_get)
 class TestPointing(base.TestCase):
-    def test_from_json(self, _):
+    def test_from_json(self):
         ps = Pointing.from_json('{"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}')
 
         self.assertEqual(1.2, ps.angle1)
         self.assertEqual(2.1, ps.angle2)
         self.assertEqual("LMN", ps.direction_type)
 
-    def test_from_json_type_missmatch(self, _):
+    def test_from_json_type_missmatch(self):
         for json in [
             '{"angle1":"1.2", "angle2": 2.1, "direction_type":"LMN"}',
             '{"angle1":1.2, "angle2": "2.1", "direction_type":"LMN"}',
@@ -30,7 +24,7 @@ class TestPointing(base.TestCase):
             with self.assertRaises(ValidationError):
                 Pointing.from_json(json)
 
-    def test_from_json_missing_fields(self, _):
+    def test_from_json_missing_fields(self):
         for json in [
             '{"angle2": 2.1, "direction_type":"LMN"}',
             '{"angle1":1.2, "direction_type":"LMN"}',
@@ -39,15 +33,8 @@ class TestPointing(base.TestCase):
             with self.assertRaises(ValidationError):
                 Pointing.from_json(json)
 
-    def test_to_json(self, _):
+    def test_to_json(self):
         ps = Pointing(1.3, 2.3, "URANUS")
         self.assertEqual(
             ps.to_json(), '{"angle1": 1.3, "angle2": 2.3, "direction_type": "URANUS"}'
         )
-
-    def test_throw_exception_if_schema_not_available(self, mock_get):
-        Pointing.VALIDATOR = None
-        mock_get.side_effect = requests.exceptions.Timeout
-        with self.assertRaises(RefResolutionError):
-            Pointing.from_json('{"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}')
-        self.assertEqual(5, mock_get.call_count)
diff --git a/tangostationcontrol/test/configuration/test_sap_settings.py b/tangostationcontrol/test/configuration/test_sap_settings.py
index a9afbfd9e80f0db49447bcce5b634882e7a43fd8..b013f1feb2f7b7f447404730583ac9002d02cc14 100644
--- a/tangostationcontrol/test/configuration/test_sap_settings.py
+++ b/tangostationcontrol/test/configuration/test_sap_settings.py
@@ -1,20 +1,14 @@
-# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
-# SPDX-License-Identifier: Apache-2.0
+#  Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy)
+#  SPDX-License-Identifier: Apache-2.0
 
-from unittest import mock
-
-import requests
-from jsonschema.exceptions import ValidationError, RefResolutionError
+from jsonschema.exceptions import ValidationError
 
 from tangostationcontrol.configuration import Pointing, Sap
-
 from test import base
-from test.configuration._mock_requests import mocked_requests_get
 
 
-@mock.patch("requests.get", side_effect=mocked_requests_get)
 class TestSapSettings(base.TestCase):
-    def test_from_json(self, _):
+    def test_from_json(self):
         sap = Sap.from_json(
             '{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}'
         )
@@ -24,7 +18,7 @@ class TestSapSettings(base.TestCase):
         self.assertEqual(sap.pointing.angle2, 2.1)
         self.assertEqual(sap.pointing.direction_type, "LMN")
 
-    def test_from_json_type_missmatch(self, _):
+    def test_from_json_type_missmatch(self):
         for json in [
             '{"subbands": ["3", 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}',
             '{"subbands": "3", "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}',
@@ -36,7 +30,7 @@ class TestSapSettings(base.TestCase):
             with self.assertRaises(ValidationError):
                 Sap.from_json(json)
 
-    def test_from_json_missing_fields(self, _):
+    def test_from_json_missing_fields(self):
         for json in [
             '{"subbands": [], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}',
             '{"pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}',
@@ -46,22 +40,13 @@ class TestSapSettings(base.TestCase):
             with self.assertRaises(ValidationError):
                 Sap.from_json(json)
 
-    def test_to_json(self, _):
+    def test_to_json(self):
         sut = Sap([3, 2, 1], Pointing(1.3, 2.3, "URANUS"))
         self.assertEqual(
             sut.to_json(),
             '{"subbands": [3, 2, 1], "pointing": {"angle1": 1.3, "angle2": 2.3, "direction_type": "URANUS"}}',
         )
 
-    def test_throw_exception_if_schema_not_available(self, mock_get):
-        Sap.VALIDATOR = None
-        mock_get.side_effect = requests.exceptions.Timeout
-        with self.assertRaises(RefResolutionError):
-            Sap.from_json(
-                '{"subbands": [3, 2, 1], "pointing": {"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}}'
-            )
-        self.assertEqual(5, mock_get.call_count)
-
-    def test_throw_wrong_instance(self, _):
+    def test_throw_wrong_instance(self):
         with self.assertRaises(ValidationError):
             Sap.from_json('{"angle1":1.2, "angle2": 2.1, "direction_type":"LMN"}')