diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8dc2303883c44a3a4ad40e059a734378c6887e6f..e4fab6713dc1dddfdfdda89d18899c0841fda204 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -15,6 +15,7 @@ stages:
   - linting
   - static-analysis
   - unit-tests
+  - integration-tests
 linting:
   stage: linting
   script:
@@ -34,3 +35,25 @@ unit_test:
   script:
     - cd devices
     - tox -e py37
+integration_test:
+  stage: integration-tests
+  allow_failure: true
+  services:
+    - name: docker:20.10.8-dind
+  variables:
+    DOCKER_TLS_CERTDIR: "/certs"
+# Everything below does not work currently, we need a privileged container
+# that can run the dind service
+  before_script:
+    - sudo apt update
+    - sudo apt install -y docker.io
+    - export USER=$(id | awk -F'=' '{print $2}' | awk -F'(' '{print $2}' | awk -F')' '{print $1}')
+    - echo $USER
+#    - sudo usermod -aG docker $USER
+    - sudo docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
+  script:
+    - touch /home/$USER/.Xauthority
+    - source bootstrap/etc/lofar20rc.sh
+    - export HOSTNAME=$(cat /run/systemd/netif/leases/2 | grep ^ADDRESS= | awk -F'=' '{print $2}')
+    - echo $HOSTNAME
+    - sudo $CI_PROJECT_DIR/sbin/run_integration_test.sh
diff --git a/CDB/integration_ConfigDb.json b/CDB/integration_ConfigDb.json
new file mode 100644
index 0000000000000000000000000000000000000000..b2f9cca6dc8db917942f35bb8be25e4cb88bdb93
--- /dev/null
+++ b/CDB/integration_ConfigDb.json
@@ -0,0 +1,64 @@
+{
+    "servers": {
+        "PCC": {
+            "LTS": {
+                "PCC": {
+                    "LTS/PCC/1": {
+                        "properties": {
+                            "OPC_Server_Name": [
+                                "pypcc-sim"
+                            ],
+                            "OPC_Server_Port": [
+                                "4842"
+                            ],
+                            "OPC_Time_Out": [
+                                "5.0"
+                            ]
+                        }
+                    }
+                }
+            }
+        },
+        "SDP": {
+            "LTS": {
+                "SDP": {
+                    "LTS/SDP/1": {
+                        "properties": {
+                            "OPC_Server_Name": [
+                                "sdptr-sim"
+                            ],
+                            "OPC_Server_Port": [
+                                "4840"
+                            ],
+                            "OPC_Time_Out": [
+                                "5.0"
+                            ]
+                        }
+                    }
+                }
+            }
+        },
+        "SST": {
+            "LTS": {
+                "SST": {
+                    "LTS/SST/1": {
+                        "properties": {
+                            "SST_Client_Port": [
+                                "5001"
+                            ],
+                            "OPC_Server_Name": [
+                                "sdptr-sim"
+                            ],
+                            "OPC_Server_Port": [
+                                "4840"
+                            ],
+                            "OPC_Time_Out": [
+                                "5.0"
+                            ]
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/devices/.stestr.conf b/devices/.stestr.conf
index ddc59860d5117ed8bdc44faeea1d893760b5520e..07147c8697683f270e9388da8b914c20cb8e4c45 100644
--- a/devices/.stestr.conf
+++ b/devices/.stestr.conf
@@ -1,3 +1,3 @@
 [DEFAULT]
-test_path=./test
+test_path=${TESTS_DIR:-./test}
 top_dir=./
diff --git a/devices/integration_test/README.md b/devices/integration_test/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..3292bfa0049b5c2312f8e0536e00cc581433ed61
--- /dev/null
+++ b/devices/integration_test/README.md
@@ -0,0 +1,26 @@
+# Integration Tests
+
+## Approach
+
+A special docker container is build to perform the integration tests. This
+container will be build by the makefiles but should only be started by the
+dedicated integration test script. This script will ensure that other containers
+are running and are in the required state.
+
+* Launch pypcc-sim and sdptr-sim simulators.
+* Reconfigure dsconfig to use these simulators.
+* Create and start the integration-test container.
+
+## Running
+
+**Warning running these tests will make changes to your CDB database config!**
+
+```shell
+source bootstrap/etc/lofar20rc.sh
+$LOFAR20_DIR/sbin/run_integration_test.sh
+```
+
+## Limitations
+
+Our makefile will always launch the new container upon creation, resulting in
+the integration tests actually being run twice.
\ No newline at end of file
diff --git a/devices/integration_test/__init__.py b/devices/integration_test/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/devices/integration_test/base.py b/devices/integration_test/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..92601ec2d440753ae7f7be22fcbfad0c5028875c
--- /dev/null
+++ b/devices/integration_test/base.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of the LOFAR 2.0 Station Software
+#
+#
+#
+# Distributed under the terms of the APACHE license.
+# See LICENSE.txt for more info.
+
+import unittest
+import testscenarios
+
+
+class BaseIntegrationTestCase(testscenarios.WithScenarios, unittest.TestCase):
+    """Integration test base class."""
+
+    def setUp(self):
+        super().setUp()
+
+
+class IntegrationTestCase(BaseIntegrationTestCase):
+    """Integration test case base class for all unit tests."""
+
+    def setUp(self):
+        super().setUp()
diff --git a/devices/integration_test/client/__init__.py b/devices/integration_test/client/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/devices/integration_test/client/test_apsct_sim.py b/devices/integration_test/client/test_apsct_sim.py
new file mode 100644
index 0000000000000000000000000000000000000000..775c34cd207699f7febb435000314c65db97b66a
--- /dev/null
+++ b/devices/integration_test/client/test_apsct_sim.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of the LOFAR 2.0 Station Software
+#
+#
+#
+# Distributed under the terms of the APACHE license.
+# See LICENSE.txt for more info.
+
+from opcua import Client
+
+from integration_test import base
+
+
+class TestAPSCTSim(base.IntegrationTestCase):
+
+    def setUp(self):
+        super(TestAPSCTSim, self).setUp()
+
+    def test_opcua_connection(self):
+        """Check if we can connect to apsct-sim"""
+
+        #TODO(Corne): Replace to APSCT name once simulator name has changed
+        client = Client("opc.tcp://pypcc-sim:4842")
+        root_node = None
+
+        try:
+            client.connect()
+            root_node = client.get_root_node()
+        finally:
+            client.disconnect()
+
+        self.assertNotEqual(None, root_node)
diff --git a/devices/integration_test/client/test_sdptr_sim.py b/devices/integration_test/client/test_sdptr_sim.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ba48e7d761c7ef366c8690e2d114c773de7311d
--- /dev/null
+++ b/devices/integration_test/client/test_sdptr_sim.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of the LOFAR 2.0 Station Software
+#
+#
+#
+# Distributed under the terms of the APACHE license.
+# See LICENSE.txt for more info.
+
+from opcua import Client
+
+from integration_test import base
+
+
+class TestSDPTRSim(base.IntegrationTestCase):
+
+    def setUp(self):
+        super(TestSDPTRSim, self).setUp()
+
+    def test_opcua_connection(self):
+        """Check if we can connect to sdptr-sim"""
+
+        client = Client("opc.tcp://sdptr-sim:4840")
+        root_node = None
+
+        try:
+            client.connect()
+            root_node = client.get_root_node()
+        finally:
+            client.disconnect()
+
+        self.assertNotEqual(None, root_node)
diff --git a/devices/integration_test/devices/__init__.py b/devices/integration_test/devices/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/devices/integration_test/devices/test_device_pcc.py b/devices/integration_test/devices/test_device_pcc.py
new file mode 100644
index 0000000000000000000000000000000000000000..b3b7a4672dbb18790d19144aeb35bcacd68e4bfb
--- /dev/null
+++ b/devices/integration_test/devices/test_device_pcc.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of the LOFAR 2.0 Station Software
+#
+#
+#
+# Distributed under the terms of the APACHE license.
+# See LICENSE.txt for more info.
+
+import time
+
+from tango import DeviceProxy
+from tango._tango import DevState
+
+from integration_test import base
+
+
+class TestDevicePCC(base.IntegrationTestCase):
+
+    def setUp(self):
+        super(TestDevicePCC, self).setUp()
+
+    def tearDown(self):
+        """Turn device Off in teardown to prevent blocking tests"""
+        d = DeviceProxy("LTS/PCC/1")
+
+        try:
+            d.Off()
+        except Exception as e:
+            """Failing to turn Off devices should not raise errors here"""
+            print(f"Failed to turn device off in teardown {e}")
+
+    def test_device_proxy_pcc(self):
+        """Test if we can successfully create a DeviceProxy and fetch state"""
+
+        d = DeviceProxy("LTS/PCC/1")
+
+        self.assertEqual(DevState.OFF, d.state())
+
+    def test_device_pcc_initialize(self):
+        """Test if we can transition to standby"""
+
+        d = DeviceProxy("LTS/PCC/1")
+
+        d.initialise()
+
+        self.assertEqual(DevState.STANDBY, d.state())
+
+    def test_device_pcc_on(self):
+        """Test if we can transition to on"""
+
+        d = DeviceProxy("LTS/PCC/1")
+
+        d.initialise()
+
+        d.on()
+
+        self.assertEqual(DevState.ON, d.state())
diff --git a/devices/integration_test/devices/test_device_sdp.py b/devices/integration_test/devices/test_device_sdp.py
new file mode 100644
index 0000000000000000000000000000000000000000..cfd656748054cb21e0e3bb2110ce60072d9fb28a
--- /dev/null
+++ b/devices/integration_test/devices/test_device_sdp.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of the LOFAR 2.0 Station Software
+#
+#
+#
+# Distributed under the terms of the APACHE license.
+# See LICENSE.txt for more info.
+
+import time
+
+from tango import DeviceProxy
+from tango._tango import DevState
+
+from integration_test import base
+
+
+class TestDeviceSDP(base.IntegrationTestCase):
+
+    def setUp(self):
+        """Intentionally recreate the device object in each test"""
+        super(TestDeviceSDP, self).setUp()
+
+    def tearDown(self):
+        """Turn device Off in teardown to prevent blocking tests"""
+        d = DeviceProxy("LTS/SDP/1")
+
+        try:
+            d.Off()
+        except Exception as e:
+            """Failing to turn Off devices should not raise errors here"""
+            print(f"Failed to turn device off in teardown {e}")
+
+    def test_device_proxy_sdp(self):
+        """Test if we can successfully create a DeviceProxy and fetch state"""
+
+        d = DeviceProxy("LTS/SDP/1")
+
+        self.assertEqual(DevState.OFF, d.state())
+
+    def test_device_sdp_initialize(self):
+        """Test if we can transition to standby"""
+
+        d = DeviceProxy("LTS/SDP/1")
+
+        d.initialise()
+
+        self.assertEqual(DevState.STANDBY, d.state())
+
+    def test_device_sdp_on(self):
+        """Test if we can transition to on"""
+
+        d = DeviceProxy("LTS/SDP/1")
+
+        d.initialise()
+
+        d.on()
+
+        self.assertEqual(DevState.ON, d.state())
diff --git a/devices/tox.ini b/devices/tox.ini
index 18c6cda38751d7bc447e8fb23d92e63b64288ddb..94d33c3e392272ac7341e039791f567cf2a7b9b4 100644
--- a/devices/tox.ini
+++ b/devices/tox.ini
@@ -17,6 +17,14 @@ deps = -r{toxinidir}/test-requirements.txt
     -r{toxinidir}/../docker-compose/lofar-device-base/lofar-requirements.txt
 commands = stestr run {posargs}
 
+[testenv:integration]
+; Warning running integration tests will make changes to your docker system!
+; These tests should only be run by the integration-test docker container.
+passenv = TANGO_HOST
+setenv = TESTS_DIR=./integration_test
+commands =
+    stestr run --serial
+
 ; TODO(Corne): Integrate Hacking to customize pep8 rules
 [testenv:pep8]
 commands =
diff --git a/docker-compose/integration-test.yml b/docker-compose/integration-test.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e6a0e54939179ba0c4f5b043da3191dd9e11945d
--- /dev/null
+++ b/docker-compose/integration-test.yml
@@ -0,0 +1,29 @@
+#
+# Docker compose file that launches integration tests
+#
+# Defines:
+#   - integration: Integration testing
+#
+version: '2'
+
+services:
+  integration-test:
+    build:
+        context: itango
+        args:
+            SOURCE_IMAGE: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}-tango-itango:${TANGO_ITANGO_VERSION}
+    container_name: ${CONTAINER_NAME_PREFIX}integration-test
+    networks:
+      - control
+    volumes:
+        - ${TANGO_LOFAR_CONTAINER_MOUNT}
+    environment:
+      - TANGO_HOST=${TANGO_HOST}
+    working_dir: ${TANGO_LOFAR_CONTAINER_DIR}/devices
+    entrypoint:
+      - /usr/local/bin/wait-for-it.sh
+      - ${TANGO_HOST}
+      - --timeout=30
+      - --strict
+      - --
+      - tox -e integration
diff --git a/sbin/run_integration_test.sh b/sbin/run_integration_test.sh
new file mode 100755
index 0000000000000000000000000000000000000000..d54163625541c13816bf0309c09a2713ce35add9
--- /dev/null
+++ b/sbin/run_integration_test.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# Check if lofar20rc.sh is sourced and environment variables are set.
+if [ -z "$LOFAR20_DIR" ]; then
+  echo "\$LOFAR20_DIR is unset or blank, is lofar20rc.sh sourced correctly?"
+  exit 1
+fi
+
+# Start all required containers
+cd "$LOFAR20_DIR/docker-compose" || exit 1
+make start databaseds dsconfig device-sdp device-pcc jupyter elk sdptr-sim pypcc-sim
+
+# Update the dsconfig
+cd "$TANGO_LOFAR_LOCAL_DIR" || exit 1
+sbin/update_ConfigDb.sh CDB/integration_ConfigDb.json
+
+# Start the integration test
+cd "$LOFAR20_DIR/docker-compose" || exit 1
+make start integration-test
+
+# Run the integration test with the output displayed on stdout
+docker start -a integration-test
\ No newline at end of file