diff --git a/src/util/virfirewall.c b/src/util/virfirewall.c
index 9def8999d5..b9b4140ad6 100644
--- a/src/util/virfirewall.c
+++ b/src/util/virfirewall.c
@@ -470,7 +470,7 @@ void virFirewallStartTransaction(virFirewall *firewall,
  * Returns the virFirewallTransactionFlags for the currently active
  * group (transaction) in @firewall.
  */
-static virFirewallTransactionFlags G_GNUC_UNUSED
+static virFirewallTransactionFlags
 virFirewallTransactionGetFlags(virFirewall *firewall)
 {
     return firewall->groups[firewall->currentGroup]->actionFlags;
@@ -525,16 +525,25 @@ virFirewallCmdToString(const char *cmd,
 }
 
 
+#define VIR_IPTABLES_ARG_IS_CREATE(arg) \
+    (STREQ(arg, "--insert") || STREQ(arg, "-I") || \
+     STREQ(arg, "--append") || STREQ(arg, "-A"))
+
+
 static int
-virFirewallApplyCmdDirect(virFirewallCmd *fwCmd,
-                           char **output)
+virFirewallCmdIptablesApply(virFirewall *firewall,
+                            virFirewallCmd *fwCmd,
+                            char **output)
 {
-    size_t i;
     const char *bin = virFirewallLayerCommandTypeToString(fwCmd->layer);
+    bool checkRollback = (virFirewallTransactionGetFlags(firewall) &
+                          VIR_FIREWALL_TRANSACTION_AUTO_ROLLBACK);
+    bool needRollback = false;
     g_autoptr(virCommand) cmd = NULL;
     g_autofree char *cmdStr = NULL;
-    int status;
     g_autofree char *error = NULL;
+    size_t i;
+    int status;
 
     if (!bin) {
         virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -558,8 +567,13 @@ virFirewallApplyCmdDirect(virFirewallCmd *fwCmd,
         break;
     }
 
-    for (i = 0; i < fwCmd->argsLen; i++)
+    for (i = 0; i < fwCmd->argsLen; i++) {
+        /* the -I/-A arg could be at any position in the list */
+        if (checkRollback && VIR_IPTABLES_ARG_IS_CREATE(fwCmd->args[i]))
+            needRollback = true;
+
         virCommandAddArg(cmd, fwCmd->args[i]);
+    }
 
     cmdStr = virCommandToString(cmd, false);
     VIR_INFO("Running firewall command '%s'", NULLSTR(cmdStr));
@@ -571,8 +585,10 @@ virFirewallApplyCmdDirect(virFirewallCmd *fwCmd,
         return -1;
 
     if (status != 0) {
+        /* the command failed, decide whether or not to report it */
         if (fwCmd->ignoreErrors) {
             VIR_DEBUG("Ignoring error running command");
+            return 0;
         } else {
             virReportError(VIR_ERR_INTERNAL_ERROR,
                            _("Failed to run firewall command %1$s: %2$s"),
@@ -582,6 +598,31 @@ virFirewallApplyCmdDirect(virFirewallCmd *fwCmd,
         }
     }
 
+    /* the command was successful, see if we need to add a
+     * rollback command
+     */
+
+    if (needRollback) {
+        virFirewallCmd *rollback
+            = virFirewallAddRollbackCmd(firewall, fwCmd->layer, NULL);
+        g_autofree char *rollbackStr = NULL;
+
+        for (i = 0; i < fwCmd->argsLen; i++) {
+            /* iptables --delete wants the entire commandline that
+             * was used for --insert but with s/insert/delete/
+             */
+            if (VIR_IPTABLES_ARG_IS_CREATE(fwCmd->args[i])) {
+                virFirewallCmdAddArg(firewall, rollback, "--delete");
+            } else {
+                virFirewallCmdAddArg(firewall, rollback, fwCmd->args[i]);
+            }
+        }
+
+        rollbackStr = virFirewallCmdToString(virFirewallLayerCommandTypeToString(fwCmd->layer),
+                                             rollback);
+        VIR_DEBUG("Recording Rollback command '%s'", NULLSTR(rollbackStr));
+    }
+
     return 0;
 }
 
@@ -599,7 +640,7 @@ virFirewallApplyCmd(virFirewall *firewall,
         return -1;
     }
 
-    if (virFirewallApplyCmdDirect(fwCmd, &output) < 0)
+    if (virFirewallCmdIptablesApply(firewall, fwCmd, &output) < 0)
         return -1;
 
     if (fwCmd->queryCB && output) {