diff --git a/ovn/controller/encaps.c b/ovn/controller/encaps.c
index fde017586..2169920ba 100644
--- a/ovn/controller/encaps.c
+++ b/ovn/controller/encaps.c
@@ -79,8 +79,9 @@ tunnel_create_name(struct tunnel_ctx *tc, const char *chassis_id)
 }
 
 static void
-tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id,
-           const struct sbrec_encap *encap)
+tunnel_add(struct tunnel_ctx *tc, const struct sbrec_sb_global *sbg,
+           const char *new_chassis_id, const struct sbrec_encap *encap,
+           const char *local_ip)
 {
     struct smap options = SMAP_INITIALIZER(&options);
     smap_add(&options, "remote_ip", encap->ip);
@@ -90,6 +91,16 @@ tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id,
         smap_add(&options, "csum", csum);
     }
 
+    /* Add auth info if ipsec is enabled. */
+    if (sbg->ipsec) {
+        smap_add(&options, "remote_name", new_chassis_id);
+        if (local_ip) {
+            smap_add(&options, "local_ip", local_ip);
+        } else {
+            VLOG_INFO("Need to specify encap ip for IPsec tunnels.");
+        }
+    }
+
     /* If there's an existing chassis record that does not need any change,
      * keep it.  Otherwise, create a new record (if there was an existing
      * record, the new record will supplant it and encaps_run() will delete
@@ -157,7 +168,9 @@ encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
            const struct ovsrec_bridge_table *bridge_table,
            const struct ovsrec_bridge *br_int,
            const struct sbrec_chassis_table *chassis_table,
-           const char *chassis_id)
+           const char *chassis_id,
+           const struct sbrec_sb_global *sbg,
+           const struct ovsrec_open_vswitch_table *ovs_table)
 {
     if (!ovs_idl_txn || !br_int) {
         return;
@@ -201,6 +214,16 @@ encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
         }
     }
 
+    /* Get IP address of local chassis. */
+    const char *chassis_ip;
+    const struct ovsrec_open_vswitch *cfg;
+    cfg = ovsrec_open_vswitch_table_first(ovs_table);
+    if (cfg) {
+        chassis_ip = smap_get(&cfg->external_ids, "ovn-encap-ip");
+    } else {
+        chassis_ip = NULL;
+    }
+
     SBREC_CHASSIS_TABLE_FOR_EACH (chassis_rec, chassis_table) {
         if (strcmp(chassis_rec->name, chassis_id)) {
             /* Create tunnels to the other chassis. */
@@ -209,7 +232,7 @@ encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
                 VLOG_INFO("No supported encaps for '%s'", chassis_rec->name);
                 continue;
             }
-            tunnel_add(&tc, chassis_rec->name, encap);
+            tunnel_add(&tc, sbg, chassis_rec->name, encap, chassis_ip);
         }
     }
 
diff --git a/ovn/controller/encaps.h b/ovn/controller/encaps.h
index 054bdfa78..680b62df7 100644
--- a/ovn/controller/encaps.h
+++ b/ovn/controller/encaps.h
@@ -23,13 +23,18 @@ struct ovsdb_idl_txn;
 struct ovsrec_bridge;
 struct ovsrec_bridge_table;
 struct sbrec_chassis_table;
+struct sbrec_sb_global;
+struct ovsrec_open_vswitch_table;
 
 void encaps_register_ovs_idl(struct ovsdb_idl *);
 void encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
                 const struct ovsrec_bridge_table *,
                 const struct ovsrec_bridge *br_int,
                 const struct sbrec_chassis_table *,
-                const char *chassis_id);
+                const char *chassis_id,
+                const struct sbrec_sb_global *,
+                const struct ovsrec_open_vswitch_table *);
+
 bool encaps_cleanup(struct ovsdb_idl_txn *ovs_idl_txn,
                     const struct ovsrec_bridge *br_int);
 
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 70db66bf6..9c71425a0 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -698,7 +698,9 @@ main(int argc, char *argv[])
                                   chassis_id, br_int);
             encaps_run(ovs_idl_txn,
                        ovsrec_bridge_table_get(ovs_idl_loop.idl), br_int,
-                       sbrec_chassis_table_get(ovnsb_idl_loop.idl), chassis_id);
+                       sbrec_chassis_table_get(ovnsb_idl_loop.idl), chassis_id,
+                       sbrec_sb_global_first(ovnsb_idl_loop.idl),
+                       ovsrec_open_vswitch_table_get(ovs_idl_loop.idl));
             bfd_calculate_active_tunnels(br_int, &active_tunnels);
             binding_run(ovnsb_idl_txn, ovs_idl_txn, sbrec_chassis_by_name,
                         sbrec_datapath_binding_by_key,
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 067d52d82..df90d05e0 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -7064,8 +7064,8 @@ ovnnb_db_run(struct northd_context *ctx,
     }
     hmap_destroy(&ports);
 
-    /* Copy nb_cfg from northbound to southbound database.
-     *
+    /* Sync ipsec configuration.
+     * Copy nb_cfg from northbound to southbound database.
      * Also set up to update sb_cfg once our southbound transaction commits. */
     const struct nbrec_nb_global *nb = nbrec_nb_global_first(ctx->ovnnb_idl);
     if (!nb) {
@@ -7075,6 +7075,9 @@ ovnnb_db_run(struct northd_context *ctx,
     if (!sb) {
         sb = sbrec_sb_global_insert(ctx->ovnsb_txn);
     }
+    if (nb->ipsec != sb->ipsec) {
+        sbrec_sb_global_set_ipsec(sb, nb->ipsec);
+    }
     sbrec_sb_global_set_nb_cfg(sb, nb->nb_cfg);
     sb_loop->next_cfg = nb->nb_cfg;
 
@@ -7578,6 +7581,7 @@ main(int argc, char *argv[])
 
     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_sb_global);
     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_nb_cfg);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_ipsec);
 
     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_logical_flow);
     add_column_noalert(ovnsb_idl_loop.idl,
diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml
index ae5ca8e4a..55a5db9ea 100644
--- a/ovn/ovn-architecture.7.xml
+++ b/ovn/ovn-architecture.7.xml
@@ -1621,6 +1621,45 @@
     </li>
   </ol>
 
+  <h2>Encrypt Tunnel Traffic with IPsec</h2>
+  <p>
+    OVN tunnel traffic goes through physical routers and switches. These
+    physical devices could be untrusted (devices in public network) or might be
+    compromised. Enabling encryption to the tunnel traffic can prevent the
+    traffic data from being monitored and manipulated.
+  </p>
+  <p>
+    The tunnel traffic is encrypted with IPsec. The CMS sets the
+    <code>ipsec</code> column in the northbound <code>NB_Global</code> table to
+    enable or disable IPsec encrytion. If <code>ipsec</code> is true, all OVN
+    tunnels will be encrypted. If <code>ipsec</code> is false, no OVN tunnels
+    will be encrypted.
+  </p>
+  <p>
+    When CMS updates the <code>ipsec</code> column in the northbound
+    <code>NB_Global</code> table, <code>ovn-northd</code> copies the value to
+    the <code>ipsec</code> column in the southbound <code>SB_Global</code>
+    table. <code>ovn-controller</code> in each chassis monitors the southbound
+    database and sets the options of the OVS tunnel interface accordingly. OVS
+    tunnel interface options are monitored by the
+    <code>ovs-monitor-ipsec</code> daemon which configures IKE daemon to set up
+    IPsec connections.
+  </p>
+  <p>
+    Chassis authenticates each other by using certificate. The authentication
+    succeeds if the other end in tunnel presents a certificate signed by a
+    trusted CA and the common name (CN) matches the expected chassis name.  The
+    SSL certificates used in role-based access controls (RBAC) can be used in
+    IPsec. Or use <code>ovs-pki</code> to create different certificates. The
+    certificate is required to be x.509 version 3, and with CN field and
+    subjectAltName field being set to the chassis name.
+  </p>
+  <p>
+    The CA certificate, chassis certificate and private key are required to be
+    installed in each chassis before enabling IPsec. Please see
+    <code>ovs-vswitchd.conf.db</code>(5) for setting up CA based IPsec
+    authentication.
+  </p>
   <h1>Design Decisions</h1>
 
   <h2>Tunnel Encapsulations</h2>
diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
index 3e7164baa..3ee288935 100644
--- a/ovn/ovn-nb.ovsschema
+++ b/ovn/ovn-nb.ovsschema
@@ -1,7 +1,7 @@
 {
     "name": "OVN_Northbound",
-    "version": "5.13.0",
-    "cksum": "1278623084 20312",
+    "version": "5.14.0",
+    "cksum": "483549684 20358",
     "tables": {
         "NB_Global": {
             "columns": {
@@ -19,7 +19,8 @@
                 "ssl": {
                     "type": {"key": {"type": "uuid",
                                      "refTable": "SSL"},
-                                     "min": 0, "max": 1}}},
+                                     "min": 0, "max": 1}},
+                "ipsec": {"type": "boolean"}},
             "maxRows": 1,
             "isRoot": true},
         "Logical_Switch": {
diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
index bc60a25dd..8c2d736d2 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -80,6 +80,12 @@
         Global SSL configuration.
       </column>
     </group>
+    <group title="Security Configurations">
+      <column name="ipsec">
+        Tunnel encryption configuration. If this column is set to be true, all
+        OVN tunnels will be encrypted with IPsec.
+      </column>
+    </group>
   </table>
 
   <table name="Logical_Switch" title="L2 logical switch">
diff --git a/ovn/ovn-sb.ovsschema b/ovn/ovn-sb.ovsschema
index ad6ad3b71..669b034e1 100644
--- a/ovn/ovn-sb.ovsschema
+++ b/ovn/ovn-sb.ovsschema
@@ -1,7 +1,7 @@
 {
     "name": "OVN_Southbound",
-    "version": "1.16.0",
-    "cksum": "3046632234 14844",
+    "version": "1.17.0",
+    "cksum": "3604753998 14890",
     "tables": {
         "SB_Global": {
             "columns": {
@@ -17,7 +17,8 @@
                 "ssl": {
                     "type": {"key": {"type": "uuid",
                                      "refTable": "SSL"},
-                                     "min": 0, "max": 1}}},
+                                     "min": 0, "max": 1}},
+                "ipsec": {"type": "boolean"}},
             "maxRows": 1,
             "isRoot": true},
         "Chassis": {
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index 68e31db31..6d4b662b9 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -174,6 +174,12 @@
         Global SSL configuration.
       </column>
     </group>
+    <group title="Security Configurations">
+      <column name="ipsec">
+        Tunnel encryption configuration. If this column is set to be true, all
+        OVN tunnels will be encrypted with IPsec.
+      </column>
+    </group>
   </table>
 
   <table name="Chassis" title="Physical Network Hypervisor and Gateway Information">
