diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index e36adde708..156b8613d8 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -1504,6 +1504,8 @@ struct mlx5_priv {
 #if defined(HAVE_IBV_FLOW_DV_SUPPORT) || !defined(HAVE_INFINIBAND_VERBS_H)
 	/* Item template list. */
 	LIST_HEAD(flow_hw_itt, rte_flow_pattern_template) flow_hw_itt;
+	/* Action template list. */
+	LIST_HEAD(flow_hw_at, rte_flow_actions_template) flow_hw_at;
 	struct mlx5dr_context *dr_ctx; /**< HW steering DR context. */
 	uint32_t nb_queue; /* HW steering queue number. */
 	/* HW steering queue polling mechanism job descriptor LIFO. */
diff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c
index 673e0ec55f..18c313c6f0 100644
--- a/drivers/net/mlx5/mlx5_flow.c
+++ b/drivers/net/mlx5/mlx5_flow.c
@@ -827,6 +827,16 @@ static int
 mlx5_flow_pattern_template_destroy(struct rte_eth_dev *dev,
 				   struct rte_flow_pattern_template *template,
 				   struct rte_flow_error *error);
+static struct rte_flow_actions_template *
+mlx5_flow_actions_template_create(struct rte_eth_dev *dev,
+			const struct rte_flow_actions_template_attr *attr,
+			const struct rte_flow_action actions[],
+			const struct rte_flow_action masks[],
+			struct rte_flow_error *error);
+static int
+mlx5_flow_actions_template_destroy(struct rte_eth_dev *dev,
+				   struct rte_flow_actions_template *template,
+				   struct rte_flow_error *error);
 
 static const struct rte_flow_ops mlx5_flow_ops = {
 	.validate = mlx5_flow_validate,
@@ -852,6 +862,8 @@ static const struct rte_flow_ops mlx5_flow_ops = {
 	.configure = mlx5_flow_port_configure,
 	.pattern_template_create = mlx5_flow_pattern_template_create,
 	.pattern_template_destroy = mlx5_flow_pattern_template_destroy,
+	.actions_template_create = mlx5_flow_actions_template_create,
+	.actions_template_destroy = mlx5_flow_actions_template_destroy,
 };
 
 /* Tunnel information. */
@@ -8000,6 +8012,72 @@ mlx5_flow_pattern_template_destroy(struct rte_eth_dev *dev,
 	return fops->pattern_template_destroy(dev, template, error);
 }
 
+/**
+ * Create flow item template.
+ *
+ * @param[in] dev
+ *   Pointer to the rte_eth_dev structure.
+ * @param[in] attr
+ *   Pointer to the action template attributes.
+ * @param[in] actions
+ *   Associated actions (list terminated by the END action).
+ * @param[in] masks
+ *   List of actions that marks which of the action's member is constant.
+ * @param[out] error
+ *   Pointer to error structure.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static struct rte_flow_actions_template *
+mlx5_flow_actions_template_create(struct rte_eth_dev *dev,
+			const struct rte_flow_actions_template_attr *attr,
+			const struct rte_flow_action actions[],
+			const struct rte_flow_action masks[],
+			struct rte_flow_error *error)
+{
+	const struct mlx5_flow_driver_ops *fops;
+
+	if (flow_get_drv_type(dev, NULL) != MLX5_FLOW_TYPE_HW) {
+		rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				NULL,
+				"action create with incorrect steering mode");
+		return NULL;
+	}
+	fops = flow_get_drv_ops(MLX5_FLOW_TYPE_HW);
+	return fops->actions_template_create(dev, attr, actions, masks, error);
+}
+
+/**
+ * Destroy flow action template.
+ *
+ * @param[in] dev
+ *   Pointer to the rte_eth_dev structure.
+ * @param[in] template
+ *   Pointer to the action template to be destroyed.
+ * @param[out] error
+ *   Pointer to error structure.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+mlx5_flow_actions_template_destroy(struct rte_eth_dev *dev,
+				   struct rte_flow_actions_template *template,
+				   struct rte_flow_error *error)
+{
+	const struct mlx5_flow_driver_ops *fops;
+
+	if (flow_get_drv_type(dev, NULL) != MLX5_FLOW_TYPE_HW)
+		return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				NULL,
+				"action destroy with incorrect steering mode");
+	fops = flow_get_drv_ops(MLX5_FLOW_TYPE_HW);
+	return fops->actions_template_destroy(dev, template, error);
+}
+
 /**
  * Allocate a new memory for the counter values wrapped by all the needed
  * management.
diff --git a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h
index 49027d6a3a..9a643fe0a8 100644
--- a/drivers/net/mlx5/mlx5_flow.h
+++ b/drivers/net/mlx5/mlx5_flow.h
@@ -1026,6 +1026,16 @@ struct rte_flow_pattern_template {
 	uint32_t refcnt;  /* Reference counter. */
 };
 
+/* Flow action template struct. */
+struct rte_flow_actions_template {
+	LIST_ENTRY(rte_flow_actions_template) next;
+	/* Template attributes. */
+	struct rte_flow_actions_template_attr attr;
+	struct rte_flow_action *actions; /* Cached flow actions. */
+	struct rte_flow_action *masks; /* Cached action masks.*/
+	uint32_t refcnt; /* Reference counter. */
+};
+
 #endif
 
 /*
@@ -1290,6 +1300,16 @@ typedef int (*mlx5_flow_pattern_template_destroy_t)
 			(struct rte_eth_dev *dev,
 			 struct rte_flow_pattern_template *template,
 			 struct rte_flow_error *error);
+typedef struct rte_flow_actions_template *(*mlx5_flow_actions_template_create_t)
+			(struct rte_eth_dev *dev,
+			 const struct rte_flow_actions_template_attr *attr,
+			 const struct rte_flow_action actions[],
+			 const struct rte_flow_action masks[],
+			 struct rte_flow_error *error);
+typedef int (*mlx5_flow_actions_template_destroy_t)
+			(struct rte_eth_dev *dev,
+			 struct rte_flow_actions_template *template,
+			 struct rte_flow_error *error);
 
 struct mlx5_flow_driver_ops {
 	mlx5_flow_validate_t validate;
@@ -1332,6 +1352,8 @@ struct mlx5_flow_driver_ops {
 	mlx5_flow_port_configure_t configure;
 	mlx5_flow_pattern_template_create_t pattern_template_create;
 	mlx5_flow_pattern_template_destroy_t pattern_template_destroy;
+	mlx5_flow_actions_template_create_t actions_template_create;
+	mlx5_flow_actions_template_destroy_t actions_template_destroy;
 };
 
 /* mlx5_flow.c */
diff --git a/drivers/net/mlx5/mlx5_flow_hw.c b/drivers/net/mlx5/mlx5_flow_hw.c
index 66c5825084..4214a63a73 100644
--- a/drivers/net/mlx5/mlx5_flow_hw.c
+++ b/drivers/net/mlx5/mlx5_flow_hw.c
@@ -12,6 +12,115 @@
 
 const struct mlx5_flow_driver_ops mlx5_flow_hw_drv_ops;
 
+/**
+ * Create flow action template.
+ *
+ * @param[in] dev
+ *   Pointer to the rte_eth_dev structure.
+ * @param[in] attr
+ *   Pointer to the action template attributes.
+ * @param[in] actions
+ *   Associated actions (list terminated by the END action).
+ * @param[in] masks
+ *   List of actions that marks which of the action's member is constant.
+ * @param[out] error
+ *   Pointer to error structure.
+ *
+ * @return
+ *   Action template pointer on success, NULL otherwise and rte_errno is set.
+ */
+static struct rte_flow_actions_template *
+flow_hw_actions_template_create(struct rte_eth_dev *dev,
+			const struct rte_flow_actions_template_attr *attr,
+			const struct rte_flow_action actions[],
+			const struct rte_flow_action masks[],
+			struct rte_flow_error *error)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+	int len, act_len, mask_len, i;
+	struct rte_flow_actions_template *at;
+
+	act_len = rte_flow_conv(RTE_FLOW_CONV_OP_ACTIONS,
+				NULL, 0, actions, error);
+	if (act_len <= 0)
+		return NULL;
+	len = RTE_ALIGN(act_len, 16);
+	mask_len = rte_flow_conv(RTE_FLOW_CONV_OP_ACTIONS,
+				 NULL, 0, masks, error);
+	if (mask_len <= 0)
+		return NULL;
+	len += RTE_ALIGN(mask_len, 16);
+	at = mlx5_malloc(MLX5_MEM_ZERO, len + sizeof(*at), 64, rte_socket_id());
+	if (!at) {
+		rte_flow_error_set(error, ENOMEM,
+				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				   NULL,
+				   "cannot allocate action template");
+		return NULL;
+	}
+	at->attr = *attr;
+	at->actions = (struct rte_flow_action *)(at + 1);
+	act_len = rte_flow_conv(RTE_FLOW_CONV_OP_ACTIONS, at->actions, len,
+				actions, error);
+	if (act_len <= 0)
+		goto error;
+	at->masks = (struct rte_flow_action *)
+		    (((uint8_t *)at->actions) + act_len);
+	mask_len = rte_flow_conv(RTE_FLOW_CONV_OP_ACTIONS, at->masks,
+				 len - act_len, masks, error);
+	if (mask_len <= 0)
+		goto error;
+	/*
+	 * mlx5 PMD hacks indirect action index directly to the action conf.
+	 * The rte_flow_conv() function copies the content from conf pointer.
+	 * Need to restore the indirect action index from action conf here.
+	 */
+	for (i = 0; actions->type != RTE_FLOW_ACTION_TYPE_END;
+	     actions++, masks++, i++) {
+		if (actions->type == RTE_FLOW_ACTION_TYPE_INDIRECT) {
+			at->actions[i].conf = actions->conf;
+			at->masks[i].conf = masks->conf;
+		}
+	}
+	__atomic_fetch_add(&at->refcnt, 1, __ATOMIC_RELAXED);
+	LIST_INSERT_HEAD(&priv->flow_hw_at, at, next);
+	return at;
+error:
+	mlx5_free(at);
+	return NULL;
+}
+
+/**
+ * Destroy flow action template.
+ *
+ * @param[in] dev
+ *   Pointer to the rte_eth_dev structure.
+ * @param[in] template
+ *   Pointer to the action template to be destroyed.
+ * @param[out] error
+ *   Pointer to error structure.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+flow_hw_actions_template_destroy(struct rte_eth_dev *dev __rte_unused,
+				 struct rte_flow_actions_template *template,
+				 struct rte_flow_error *error __rte_unused)
+{
+	if (__atomic_load_n(&template->refcnt, __ATOMIC_RELAXED) > 1) {
+		DRV_LOG(WARNING, "Action template %p is still in use.",
+			(void *)template);
+		return rte_flow_error_set(error, EBUSY,
+				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				   NULL,
+				   "action template in using");
+	}
+	LIST_REMOVE(template, next);
+	mlx5_free(template);
+	return 0;
+}
+
 /**
  * Create flow item template.
  *
@@ -234,6 +343,7 @@ flow_hw_resource_release(struct rte_eth_dev *dev)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
 	struct rte_flow_pattern_template *it;
+	struct rte_flow_actions_template *at;
 
 	if (!priv->dr_ctx)
 		return;
@@ -241,6 +351,10 @@ flow_hw_resource_release(struct rte_eth_dev *dev)
 		it = LIST_FIRST(&priv->flow_hw_itt);
 		flow_hw_pattern_template_destroy(dev, it, NULL);
 	}
+	while (!LIST_EMPTY(&priv->flow_hw_at)) {
+		at = LIST_FIRST(&priv->flow_hw_at);
+		flow_hw_actions_template_destroy(dev, at, NULL);
+	}
 	mlx5_free(priv->hw_q);
 	priv->hw_q = NULL;
 	claim_zero(mlx5dr_context_close(priv->dr_ctx));
@@ -253,6 +367,8 @@ const struct mlx5_flow_driver_ops mlx5_flow_hw_drv_ops = {
 	.configure = flow_hw_configure,
 	.pattern_template_create = flow_hw_pattern_template_create,
 	.pattern_template_destroy = flow_hw_pattern_template_destroy,
+	.actions_template_create = flow_hw_actions_template_create,
+	.actions_template_destroy = flow_hw_actions_template_destroy,
 };
 
 #endif
