diff --git a/webservice/common.py b/webservice/common.py
new file mode 100644
index 0000000000000000000000000000000000000000..84f47617f16fadb68bd2326817955a6e11768cc1
--- /dev/null
+++ b/webservice/common.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT-0
+#
+# Implement the systemd notify protocol without external dependencies.
+# Taken from the sd_notify man page
+
+import errno
+import os
+import socket
+
+def notify(message):
+    if not message:
+        raise ValueError("notify() requires a message")
+
+    if not (socket_path := os.environ.get("NOTIFY_SOCKET")):
+        return  # Not started by systemd
+
+    if socket_path[0] not in ("/", "@"):
+        raise OSError(errno.EAFNOSUPPORT, "Unsupported socket type")
+
+    # Handle abstract socket.
+    if socket_path[0] == "@":
+        socket_path = "\0" + socket_path[1:]
+
+    with socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM | socket.SOCK_CLOEXEC) as sock:
+        sock.connect(socket_path)
+        sock.sendall(message)
+
+def notify_ready():
+    notify(b"READY=1")
diff --git a/webservice/job_monitor.py b/webservice/job_monitor.py
index 9023620d6ee0da66d16db94ad2c8b28d55d03a59..b9a6b5fcbce62a4e4b3b862368b51a486bc1b528 100644
--- a/webservice/job_monitor.py
+++ b/webservice/job_monitor.py
@@ -15,10 +15,12 @@ from kafka import KafkaProducer
 from kafka.errors import KafkaError
 
 try:
+    from .common import notify_ready
     from .config import webservice as config
     from .messages import MDC, Errors, MigrationError, Success
     from .webservice import init_job_db, init_md_client, time_db_transaction
 except ImportError:
+    from common import notify_ready
     from config import webservice as config
     from messages import MDC, Errors, MigrationError, Success
     from webservice import init_job_db, init_md_client, time_db_transaction
@@ -419,6 +421,7 @@ def main(argv=None):
     signal.signal(signal.SIGTERM, interrupted)
 
     with JobsMonitor(config) as jm:
+        notify_ready()
         try:
             jm.run()
         except KeyboardInterrupt:
diff --git a/webservice/serve_overview.py b/webservice/serve_overview.py
index dabfae7e46e6a84faca59061c4d3e940181c854b..3ce5f3da8664624012a5a9d8423118bce70c9767 100644
--- a/webservice/serve_overview.py
+++ b/webservice/serve_overview.py
@@ -17,8 +17,10 @@ from jinja2 import Template
 from xfel_calibrate.settings import free_nodes_cmd, preempt_nodes_cmd
 
 try:
+    from .common import notify_ready
     from .config import serve_overview as config
 except:
+    from common import notify_ready
     from config import serve_overview as config
 
 
@@ -344,6 +346,7 @@ def run(config_file: Optional[str] = None):
     server_address = (sconfig["host"], sconfig["port"])
     httpd = HTTPServer(server_address, RequestHandler)
     print('running server...', flush=True)
+    notify_ready()
     httpd.serve_forever()
 
 
diff --git a/webservice/systemd/cal-job-monitor.service b/webservice/systemd/cal-job-monitor.service
index 72fad67d63acb539643d2a55b0f1ecf113fcd999..7c330eb19b8a5d666b74b4cd80a5ec84270fa403 100644
--- a/webservice/systemd/cal-job-monitor.service
+++ b/webservice/systemd/cal-job-monitor.service
@@ -3,11 +3,9 @@ Description=XFEL offline calibration Slurm job monitor
 PartOf=cal-services.target
 
 [Service]
-Type=exec
+Type=notify
 WorkingDirectory=%h/deployments/development/git.xfel.eu/detectors/pycalibration/current/
 ExecStart=%h/deployments/development/git.xfel.eu/detectors/pycalibration/current/.venv/bin/python -m webservice.job_monitor --log-level DEBUG
-# Pause to make errors in startup more obvious
-ExecStartPost=/usr/bin/sleep 1
 Restart=on-failure
 SyslogIdentifier=job_monitor
 
diff --git a/webservice/systemd/cal-overview-server.service b/webservice/systemd/cal-overview-server.service
index 6c186ef6c45c6c0a12acdc1d673a0fedfa337d0e..685cf71e09a979a0867a9be9ee14bfa71bb4a417 100644
--- a/webservice/systemd/cal-overview-server.service
+++ b/webservice/systemd/cal-overview-server.service
@@ -3,11 +3,9 @@ Description=XFEL offline calibration status web page
 PartOf=cal-services.target
 
 [Service]
-Type=exec
+Type=notify
 WorkingDirectory=%h/deployments/development/git.xfel.eu/detectors/pycalibration/current/
 ExecStart=%h/deployments/development/git.xfel.eu/detectors/pycalibration/current/.venv/bin/python -m webservice.serve_overview
-# Pause to make errors in startup more obvious
-ExecStartPost=/usr/bin/sleep 1
 Restart=on-failure
 SyslogIdentifier=serve_overview
 
diff --git a/webservice/systemd/cal-webservice.service b/webservice/systemd/cal-webservice.service
index 1456ca56a88fb27c8a9e86d68d06f3353adb9ede..64e8fed31ce7cd64986dcedfa04c1079cb580057 100644
--- a/webservice/systemd/cal-webservice.service
+++ b/webservice/systemd/cal-webservice.service
@@ -3,11 +3,9 @@ Description=XFEL offline calibration ZMQ service
 PartOf=cal-services.target
 
 [Service]
-Type=exec
+Type=notify
 WorkingDirectory=%h/deployments/development/git.xfel.eu/detectors/pycalibration/current/
 ExecStart=%h/deployments/development/git.xfel.eu/detectors/pycalibration/current/.venv/bin/python -m webservice.webservice --mode prod --log-level DEBUG
-# Pause to make errors in startup more obvious
-ExecStartPost=/usr/bin/sleep 1
 Restart=on-failure
 SyslogIdentifier=webservice
 
diff --git a/webservice/webservice.py b/webservice/webservice.py
index 3c9b5056839354a6d935bc2df03a1fc8a76e52c7..54e94ddd0810af7bbeb8e862f910f2f044b87706 100644
--- a/webservice/webservice.py
+++ b/webservice/webservice.py
@@ -33,6 +33,7 @@ from git import InvalidGitRepositoryError, Repo
 from metadata_client.metadata_client import MetadataClient
 
 try:
+    from .common import notify_ready
     from .config import webservice as config
     from .messages import (
         MDC,
@@ -43,6 +44,7 @@ try:
         Success,
     )
 except ImportError:
+    from common import notify_ready
     from config import webservice as config
     from messages import MDC, Errors, MigrationError, Success, PDUsNotFoundError, OperationModeNotFoundError
 
@@ -1644,6 +1646,7 @@ def main(argv: Optional[List[str]] = None):
 
     # Launch the ZMQ server to handle requests for calibration
     server = ActionsServer(config, mode)
+    notify_ready()
     loop = asyncio.get_event_loop()
     loop.run_until_complete(server.run())
     loop.close()