diff --git a/.editorconfig b/.editorconfig
index dba68101b0c71d2aa813d59c4dd8a5c410068da8..df0525fcbe8afa97ae30d6a22e3b0cb7332a7e0e 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -149,3 +149,6 @@ ij_yaml_spaces_within_brackets = true
 
 [{*.hcl,*.hcl.tmpl,*.nomad,*.hcl.j2}]
 indent_size = 2
+
+[*.proto]
+indent_size = 2
diff --git a/.gitignore b/.gitignore
index e653de55b5cddd83321df34349a06e3d277ace06..d6ac0c2e4e550e7005cae51a6dacd05dd0e5bb1a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,8 @@
 **/vscode-server.tar
 **/*.orig
 **/venv
+**/*_pb2.*
+**/*_pb2_grpc.*
 
 tangostationcontrol/build
 tangostationcontrol/cover
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 848c5c00ba5d83c39038194886d0cd528899cf4f..f293ca1a0c1a4e9149ec80fb0f7281de746ef4c6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -391,6 +391,7 @@ deploy_nomad:
           - jupyter
           - snmp-exporter
           - landing-page
+          - rpc-server
   environment:
     name: $STATION
   script:
diff --git a/README.md b/README.md
index 743942ee22ebc6b18ebefb17414d798ab6326d97..19f96a99e3c6c2801906e925be81a6be9a487237 100644
--- a/README.md
+++ b/README.md
@@ -161,6 +161,7 @@ Next change the version in the following places:
 
 # Release Notes
 
+* 0.34.0 Add gRPC based 'external' station control service
 * 0.33.3 Add support for new PCON devices (ACX)
 * 0.33.2 Fix for XSTs in Observations: Also write FPGA_xst_nof_crosslets_RW
 * 0.33.1 SDPFirmware: replace FPGA_ucp monitoring points with new TR_ucp ones
diff --git a/infra/jobs/station/rpc-server.levant.nomad b/infra/jobs/station/rpc-server.levant.nomad
new file mode 100644
index 0000000000000000000000000000000000000000..2ff5431b872f5d5f16d102277f3e1f34a75f25af
--- /dev/null
+++ b/infra/jobs/station/rpc-server.levant.nomad
@@ -0,0 +1,42 @@
+job "rpc-server" {
+  datacenters = ["stat"]
+  type        = "service"
+  reschedule {
+    unlimited      = true
+    delay          = "30s"
+    delay_function = "constant"
+  }
+  group "station-rpc" {
+    network {
+      mode = "bridge"
+
+      port "grpc" {
+        static       = "50051"
+        host_network = "external"
+      }
+    }
+
+    service {
+      name = "rpc"
+      port = "grpc"
+    }
+
+    task "l2ss-rpc-server" {
+      driver = "docker"
+
+      config {
+        image      = "[[ $.registry.astron.url ]]/lofar-device-base:[[ $.image_tag ]]"
+        entrypoint = [""]
+        command    = "l2ss-rpc-server"
+      }
+      env {
+        TANGO_HOST = "tango.service.consul:10000"
+      }
+
+      resources {
+        cpu    = 25
+        memory = 512
+      }
+    }
+  }
+}
diff --git a/tangostationcontrol/VERSION b/tangostationcontrol/VERSION
index 90d7d2b462d7ddd7b29c887c866893205e8f30b9..85e60ed180c43f96ad25d7f1ecab8ef2461a75d9 100644
--- a/tangostationcontrol/VERSION
+++ b/tangostationcontrol/VERSION
@@ -1 +1 @@
-0.33.3
+0.34.0
diff --git a/tangostationcontrol/proto/observation.proto b/tangostationcontrol/proto/observation.proto
new file mode 100644
index 0000000000000000000000000000000000000000..3a08fe61c83d546fcda1012a641227ff7246e00d
--- /dev/null
+++ b/tangostationcontrol/proto/observation.proto
@@ -0,0 +1,18 @@
+syntax = "proto3";
+
+service Observation {
+  rpc StartObservation(StartObservationRequest) returns (ObservationReply){}
+  rpc StopObservation(StopObservationRequest) returns (ObservationReply) {}
+}
+
+message StartObservationRequest {
+  string configuration = 1;
+}
+message StopObservationRequest {
+  int64 observation_id = 1;
+}
+
+message ObservationReply {
+  bool success = 1;
+  string exception = 2;
+}
diff --git a/tangostationcontrol/requirements.txt b/tangostationcontrol/requirements.txt
index d6d27e18c61951a01b1fc234b4314b087c6ff155..6b628f3e0c3c3cd3f7dde3b2f0171b534b600c91 100644
--- a/tangostationcontrol/requirements.txt
+++ b/tangostationcontrol/requirements.txt
@@ -23,3 +23,5 @@ prometheus-client # Apache 2
 dnspython # ISC
 logfmter # MIT
 psutil >= 5.4.6 # BSD3
+grpcio # Apache 2
+grpcio-tools # Apache 2
diff --git a/tangostationcontrol/setup.cfg b/tangostationcontrol/setup.cfg
index f9594fe96de49f5420aad7bf8ba5850ff30f1edb..4779dcb7c988133388cff6d6fbf21f6ff3bb5b45 100644
--- a/tangostationcontrol/setup.cfg
+++ b/tangostationcontrol/setup.cfg
@@ -35,6 +35,7 @@ console_scripts =
     l2ss-analyse-dsconfig-hierarchies = tangostationcontrol.toolkit.analyse_dsconfig_hierarchies:main
     l2ss-version = tangostationcontrol:print_version
     l2ss-health = tangostationcontrol.common.health:main
+    l2ss-rpc-server = tangostationcontrol.rpc.server:main
 
 # The following entry points should eventually be removed / replaced
     l2ss-hardware-device-template = tangostationcontrol.examples.HW_device_template:main
diff --git a/tangostationcontrol/tangostationcontrol/rpc/__init__.py b/tangostationcontrol/tangostationcontrol/rpc/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tangostationcontrol/tangostationcontrol/rpc/_proto/__init__.py b/tangostationcontrol/tangostationcontrol/rpc/_proto/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tangostationcontrol/tangostationcontrol/rpc/server.py b/tangostationcontrol/tangostationcontrol/rpc/server.py
new file mode 100644
index 0000000000000000000000000000000000000000..23a220bbea175c948ea2439743703135ff9f32f4
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/rpc/server.py
@@ -0,0 +1,57 @@
+from concurrent import futures
+
+import grpc
+import logging
+
+from ._proto import observation_pb2
+from ._proto import observation_pb2_grpc
+from tangostationcontrol.common.lofar_logging import configure_logger, exception_to_str
+from tangostationcontrol.common.proxy import create_proxy
+
+logger = logging.getLogger()
+
+TIMEOUT = 5000
+PORT = 50051
+
+
+class Observation(observation_pb2_grpc.ObservationServicer):
+    def StartObservation(
+        self, request: observation_pb2.StartObservationRequest, context
+    ):
+        logger.info("Start observation")
+        try:
+            observation_control = create_proxy("STAT/ObservationControl/1", TIMEOUT)
+            observation_control.add_observation(request.configuration)
+            return observation_pb2.ObservationReply(success=True)
+        except Exception as e:
+            logger.exception(f"Starting observation failed: {exception_to_str(e)}")
+            return observation_pb2.ObservationReply(
+                success=False, exception=exception_to_str(e)
+            )
+
+    def StopObservation(self, request: observation_pb2.StopObservationRequest, context):
+        logger.info(f"Stop observation {request.observation_id}")
+        try:
+            observation_control = create_proxy("STAT/ObservationControl/1", TIMEOUT)
+            observation_control.stop_observation_now(request.observation_id)
+            return observation_pb2.ObservationReply(success=True)
+        except Exception as e:
+            logger.exception(f"Stopping observation failed: {exception_to_str(e)}")
+            return observation_pb2.ObservationReply(
+                success=False, exception=exception_to_str(e)
+            )
+
+
+def main():
+    configure_logger()
+    logger.info(f"Start server on insecure port 0.0.0.0:{PORT}")
+    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
+    observation_pb2_grpc.add_ObservationServicer_to_server(Observation(), server)
+    server.add_insecure_port(f"0.0.0.0:{PORT}")
+    server.start()
+    logger.info(f"Server started, listening on port {PORT}")
+    server.wait_for_termination()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tangostationcontrol/tox.ini b/tangostationcontrol/tox.ini
index 499efc4f571bfa6e6626dda90a79c1724afe4d8a..79aa5947d0458c6d1c3ee6202c38ffc255c18be8 100644
--- a/tangostationcontrol/tox.ini
+++ b/tangostationcontrol/tox.ini
@@ -94,7 +94,9 @@ commands =
 
 [testenv:build]
 usedevelop = False
-commands = {envpython} -m build
+commands =
+    {envpython} -m grpc_tools.protoc -Itangostationcontrol/rpc/_proto=proto --python_out=. --pyi_out=. --grpc_python_out=. proto/observation.proto
+    {envpython} -m build
 
 [testenv:docs]
 envdir = {toxworkdir}/docs