diff --git a/controller/chassis.c b/controller/chassis.c
index c8a332786..f121af3c3 100644
--- a/controller/chassis.c
+++ b/controller/chassis.c
@@ -37,6 +37,8 @@ VLOG_DEFINE_THIS_MODULE(chassis);
 #define HOST_NAME_MAX 255
 #endif /* HOST_NAME_MAX */
 
+char *file_system_id = NULL;
+
 /*
  * Structure for storing the chassis config parsed from the ovs table.
  */
@@ -277,6 +279,10 @@ chassis_parse_ovs_iface_types(char **iface_types, size_t n_iface_types,
 const char *
 get_ovs_chassis_id(const struct ovsrec_open_vswitch_table *ovs_table)
 {
+    if (file_system_id) {
+        return file_system_id;
+    }
+
     const struct ovsrec_open_vswitch *cfg =
         ovsrec_open_vswitch_table_first(ovs_table);
     const char *chassis_id = cfg
diff --git a/controller/chassis.h b/controller/chassis.h
index 05a96bb0c..baa327059 100644
--- a/controller/chassis.h
+++ b/controller/chassis.h
@@ -31,6 +31,8 @@ struct sset;
 struct eth_addr;
 struct smap;
 
+extern char *file_system_id;
+
 void chassis_register_ovs_idl(struct ovsdb_idl *);
 const struct sbrec_chassis *chassis_run(
     struct ovsdb_idl_txn *ovnsb_idl_txn,
diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 1814e0f8d..3f9878b5c 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -18,10 +18,14 @@
 #include "ovn-controller.h"
 
 #include <errno.h>
+#include <fcntl.h>
 #include <getopt.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include "bfd.h"
 #include "binding.h"
@@ -55,6 +59,7 @@
 #include "lib/ip-mcast-index.h"
 #include "lib/mac-binding-index.h"
 #include "lib/mcast-group-index.h"
+#include "lib/ovn-dirs.h"
 #include "lib/ovn-sb-idl.h"
 #include "lib/ovn-util.h"
 #include "patch.h"
@@ -151,6 +156,29 @@ struct pending_pkt {
 /* Registered ofctrl seqno type for nb_cfg propagation. */
 static size_t ofctrl_seq_type_nb_cfg;
 
+static char *get_file_system_id(void)
+{
+    char *ret = NULL;
+    char *filename = xasprintf("%s/system-id-override", ovn_sysconfdir());
+    errno = 0;
+    int fd = open(filename, O_RDONLY);
+    if (fd != -1) {
+        char system_id[64];
+        int nread = read(fd, system_id, sizeof system_id);
+        if (nread) {
+            system_id[nread] = '\0';
+            if (system_id[nread - 1] == '\n') {
+                system_id[nread - 1] = '\0';
+            }
+            ret = xstrdup(system_id);
+        }
+        close(fd);
+    }
+
+    free(filename);
+    return ret;
+}
+
 static unsigned int
 update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
                    const struct sbrec_chassis *chassis,
@@ -3573,6 +3601,9 @@ main(int argc, char *argv[])
     struct ovn_controller_exit_args exit_args = {&exiting, &restart};
     int retval;
 
+    /* Read from system-id-override file once on startup. */
+    file_system_id = get_file_system_id();
+
     ovs_cmdl_proctitle_init(argc, argv);
     ovn_set_program_name(argv[0]);
     service_start(&argc, &argv);
@@ -4545,6 +4576,9 @@ loop_done:
 
     ovs_feature_support_destroy();
     free(ovs_remote);
+    if (file_system_id) {
+        free(file_system_id);
+    }
     service_stop();
 
     exit(retval);
diff --git a/tests/ovn.at b/tests/ovn.at
index e45df02ae..d10a582fe 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -32930,3 +32930,45 @@ AT_CHECK(test x$encap_hv1_ip == x)
 OVN_CLEANUP([hv1],[hv2])
 AT_CLEANUP
 ])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([chassis name override via file])
+ovn_start
+net_add n1
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+
+ovs-vsctl \
+    -- set Open_vSwitch . external-ids:ovn-encap-type-hv3=geneve \
+    -- set Open_vSwitch . external-ids:ovn-encap-ip-hv3=192.168.1.1
+
+as hv1 ovs-vsctl set-ssl \
+   $PKIDIR/testpki-hv3-privkey.pem \
+   $PKIDIR/testpki-hv3-cert.pem \
+   $PKIDIR/testpki-cacert.pem
+
+echo hv3 > ${OVN_SYSCONFDIR}/system-id-override
+ovn_attach n1 br-phys 192.168.0.1 24 vxlan
+
+sim_add hv2
+as hv2
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2 24 geneve
+
+# despite that we configured ovn-encap-ip=192.168.0.1, this setting is
+# overridden by chassis specific ovn-encap-ip-hv3
+OVS_WAIT_UNTIL([
+    test "1" = "$(ovn-sbctl list Encap | grep -c '192.168.1.1')"
+])
+
+encap_hv3_ip=$(fetch_column Encap ip chassis_name=hv3 type=geneve)
+AT_CHECK(test x$encap_hv3_ip == x192.168.1.1)
+
+encap_hv1_ip=$(fetch_column Encap ip chassis_name=hv1 type=vxlan)
+AT_CHECK(test x$encap_hv1_ip == x)
+
+OVN_CLEANUP([hv1],[hv2])
+AT_CLEANUP
+])
diff --git a/tests/ovs-macros.at b/tests/ovs-macros.at
index 5a06fe956..36b58b5ae 100644
--- a/tests/ovs-macros.at
+++ b/tests/ovs-macros.at
@@ -88,7 +88,9 @@ ovs_setenv() {
     OVS_LOGDIR=$ovs_dir; export OVS_LOGDIR
     OVS_DBDIR=$ovs_dir; export OVS_DBDIR
     OVS_SYSCONFDIR=$ovs_dir; export OVS_SYSCONFDIR
+    OVN_SYSCONFDIR=$ovs_dir; export OVN_SYSCONFDIR
     OVS_PKGDATADIR=$ovs_dir; export OVS_PKGDATADIR
+    OVN_PKGDATADIR=$ovs_dir; export OVN_PKGDATADIR
 }
 
 # Prints the integers from $1 to $2, increasing by $3 (default 1) on stdout.
