diff --git a/interface-definitions/service-event-handler.xml.in b/interface-definitions/service-event-handler.xml.in
new file mode 100644
index 000000000..aef6bc1bc
--- /dev/null
+++ b/interface-definitions/service-event-handler.xml.in
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interfaceDefinition>
+  <node name="service">
+    <children>
+      <node name="event-handler" owner="${vyos_conf_scripts_dir}/service_event_handler.py">
+        <properties>
+          <help>Service event handler</help>
+        </properties>
+        <children>
+          <tagNode name="event">
+            <properties>
+              <help>Event handler name</help>
+            </properties>
+            <children>
+              <node name="filter">
+                <properties>
+                  <help>Logs filter settings</help>
+                </properties>
+                <children>
+                  <leafNode name="pattern">
+                    <properties>
+                      <help>Match pattern (regex)</help>
+                    </properties>
+                  </leafNode>
+                  <leafNode name="syslog-identifier">
+                    <properties>
+                      <help>Identifier of a process in syslog (string)</help>
+                    </properties>
+                  </leafNode>
+                </children>
+              </node>
+              <node name="script">
+                <properties>
+                  <help>Event handler script file</help>
+                </properties>
+                <children>
+                  <leafNode name="arguments">
+                    <properties>
+                      <help>Script arguments</help>
+                    </properties>
+                  </leafNode>
+                  <tagNode name="environment">
+                    <properties>
+                      <help>Script environment arguments</help>
+                    </properties>
+                    <children>
+                      <leafNode name="value">
+                        <properties>
+                          <help>Environment value</help>
+                        </properties>
+                      </leafNode>
+                    </children>
+                  </tagNode>
+                  <leafNode name="path">
+                    <properties>
+                      <help>Path to the script</help>
+                      <constraint>
+                        <validator name="script"/>
+                      </constraint>
+                    </properties>
+                  </leafNode>
+                </children>
+              </node>
+            </children>
+          </tagNode>
+        </children>
+      </node>
+    </children>
+  </node>
+</interfaceDefinition>
diff --git a/src/conf_mode/service_event_handler.py b/src/conf_mode/service_event_handler.py
new file mode 100755
index 000000000..5440d1056
--- /dev/null
+++ b/src/conf_mode/service_event_handler.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import json
+from pathlib import Path
+
+from vyos.config import Config
+from vyos.util import call, dict_search
+from vyos import ConfigError
+from vyos import airbag
+
+airbag.enable()
+
+service_name = 'vyos-event-handler'
+service_conf = Path(f'/run/{service_name}.conf')
+
+
+def get_config(config=None):
+    if config:
+        conf = config
+    else:
+        conf = Config()
+
+    base = ['service', 'event-handler', 'event']
+    config = conf.get_config_dict(base,
+                                  get_first_key=True,
+                                  no_tag_node_value_mangle=True)
+
+    return config
+
+
+def verify(config):
+    # bail out early - looks like removal from running config
+    if not config:
+        return None
+
+    for name, event_config in config.items():
+        if not dict_search('filter.pattern', event_config) or not dict_search(
+                'script.path', event_config):
+            raise ConfigError(
+                'Event-handler: both pattern and script path items are mandatory'
+            )
+
+        if dict_search('script.environment.message', event_config):
+            raise ConfigError(
+                'Event-handler: "message" environment variable is reserved for log message text'
+            )
+
+
+def generate(config):
+    if not config:
+        # Remove old config and return
+        service_conf.unlink(missing_ok=True)
+        return None
+
+    # Write configuration file
+    conf_json = json.dumps(config, indent=4)
+    service_conf.write_text(conf_json)
+
+    return None
+
+
+def apply(config):
+    if config:
+        call(f'systemctl restart {service_name}.service')
+    else:
+        call(f'systemctl stop {service_name}.service')
+
+
+if __name__ == '__main__':
+    try:
+        c = get_config()
+        verify(c)
+        generate(c)
+        apply(c)
+    except ConfigError as e:
+        print(e)
+        exit(1)
diff --git a/src/system/vyos-event-handler.py b/src/system/vyos-event-handler.py
new file mode 100755
index 000000000..691f674b2
--- /dev/null
+++ b/src/system/vyos-event-handler.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import argparse
+import select
+import re
+import json
+from os import getpid, environ
+from pathlib import Path
+from signal import signal, SIGTERM, SIGINT
+from systemd import journal
+from sys import exit
+from vyos.util import run, dict_search
+
+# Identify this script
+my_pid = getpid()
+my_name = Path(__file__).stem
+
+
+# handle termination signal
+def handle_signal(signal_type, frame):
+    if signal_type == SIGTERM:
+        journal.send('Received SIGTERM signal, stopping normally',
+                     SYSLOG_IDENTIFIER=my_name)
+    if signal_type == SIGINT:
+        journal.send('Received SIGINT signal, stopping normally',
+                     SYSLOG_IDENTIFIER=my_name)
+    exit(0)
+
+
+# Class for analyzing and process messages
+class Analyzer:
+    # Initialize settings
+    def __init__(self, config: dict) -> None:
+        self.config = {}
+        # Prepare compiled regex objects
+        for event_id, event_config in config.items():
+            script = dict_search('script.path', event_config)
+            # Check for arguments
+            if dict_search('script.arguments', event_config):
+                script_arguments = dict_search('script.arguments', event_config)
+                script = f'{script} {script_arguments}'
+            # Prepare environment
+            environment = environ
+            # Check for additional environment options
+            if dict_search('script.environment', event_config):
+                for env_variable, env_value in dict_search(
+                        'script.environment', event_config).items():
+                    environment[env_variable] = env_value.get('value')
+            # Create final config dictionary
+            pattern_raw = event_config['filter']['pattern']
+            pattern_compiled = re.compile(
+                rf'{event_config["filter"]["pattern"]}')
+            pattern_config = {
+                pattern_compiled: {
+                    'pattern_raw':
+                        pattern_raw,
+                    'syslog_id':
+                        dict_search('filter.syslog_identifier', event_config),
+                    'pattern_script': {
+                        'path': script,
+                        'environment': environment
+                    }
+                }
+            }
+            self.config.update(pattern_config)
+
+    # Execute script safely
+    def script_run(self, pattern: str, script_path: str,
+                   script_env: dict) -> None:
+        try:
+            run(script_path, env=script_env)
+            journal.send(
+                f'Pattern found: "{pattern}", script executed: "{script_path}"',
+                SYSLOG_IDENTIFIER=my_name)
+        except Exception as err:
+            journal.send(
+                f'Pattern found: "{pattern}", failed to execute script "{script_path}": {err}',
+                SYSLOG_IDENTIFIER=my_name)
+
+    # Analyze a message
+    def process_message(self, message: dict) -> None:
+        for pattern_compiled, pattern_config in self.config.items():
+            # Check if syslog id is presented in config and matches
+            syslog_id = pattern_config.get('syslog_id')
+            if syslog_id and message['SYSLOG_IDENTIFIER'] != syslog_id:
+                continue
+            if pattern_compiled.fullmatch(message['MESSAGE']):
+                # Add message to environment variables
+                pattern_config['pattern_script']['environment'][
+                    'message'] = message['MESSAGE']
+                # Run script
+                self.script_run(
+                    pattern=pattern_config['pattern_raw'],
+                    script_path=pattern_config['pattern_script']['path'],
+                    script_env=pattern_config['pattern_script']['environment'])
+
+
+if __name__ == '__main__':
+    # Parse command arguments and get config
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-c',
+                        '--config',
+                        action='store',
+                        help='Path to even-handler configuration',
+                        required=True,
+                        type=Path)
+
+    args = parser.parse_args()
+    try:
+        config_path = Path(args.config)
+        config = json.loads(config_path.read_text())
+        # Create an object for analazyng messages
+        analyzer = Analyzer(config)
+    except Exception as err:
+        print(
+            f'Configuration file "{config_path}" does not exist or malformed: {err}'
+        )
+        exit(1)
+
+    # Prepare for proper exitting
+    signal(SIGTERM, handle_signal)
+    signal(SIGINT, handle_signal)
+
+    # Set up journal connection
+    data = journal.Reader()
+    data.seek_tail()
+    data.get_previous()
+    p = select.poll()
+    p.register(data, data.get_events())
+
+    journal.send(f'Started with configuration: {config}',
+                 SYSLOG_IDENTIFIER=my_name)
+
+    while p.poll():
+        if data.process() != journal.APPEND:
+            continue
+        for entry in data:
+            message = entry['MESSAGE']
+            pid = entry['_PID']
+            # Skip empty messages and messages from this process
+            if message and pid != my_pid:
+                try:
+                    analyzer.process_message(entry)
+                except Exception as err:
+                    journal.send(f'Unable to process message: {err}',
+                                 SYSLOG_IDENTIFIER=my_name)
diff --git a/src/systemd/vyos-event-handler.service b/src/systemd/vyos-event-handler.service
new file mode 100644
index 000000000..6afe4f95b
--- /dev/null
+++ b/src/systemd/vyos-event-handler.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=VyOS event handler
+After=network.target vyos-router.service
+
+[Service]
+Type=simple
+Restart=always
+ExecStart=/usr/bin/python3 /usr/libexec/vyos/system/vyos-event-handler.py --config /run/vyos-event-handler.conf
+
+[Install]
+WantedBy=multi-user.target