diff --git a/lib/librte_pipeline/rte_pipeline_version.map b/lib/librte_pipeline/rte_pipeline_version.map
index a9ebd3b1f..88fd38ca8 100644
--- a/lib/librte_pipeline/rte_pipeline_version.map
+++ b/lib/librte_pipeline/rte_pipeline_version.map
@@ -58,6 +58,8 @@ EXPERIMENTAL {
 	rte_swx_pipeline_config;
 	rte_swx_pipeline_port_in_type_register;
 	rte_swx_pipeline_port_in_config;
+	rte_swx_pipeline_port_out_type_register;
+	rte_swx_pipeline_port_out_config;
 	rte_swx_pipeline_build;
 	rte_swx_pipeline_free;
 };
diff --git a/lib/librte_pipeline/rte_swx_pipeline.c b/lib/librte_pipeline/rte_swx_pipeline.c
index 5b1559209..7aeac8cc8 100644
--- a/lib/librte_pipeline/rte_swx_pipeline.c
+++ b/lib/librte_pipeline/rte_swx_pipeline.c
@@ -45,16 +45,46 @@ struct port_in_runtime {
 	void *obj;
 };
 
+/*
+ * Output port.
+ */
+struct port_out_type {
+	TAILQ_ENTRY(port_out_type) node;
+	char name[RTE_SWX_NAME_SIZE];
+	struct rte_swx_port_out_ops ops;
+};
+
+TAILQ_HEAD(port_out_type_tailq, port_out_type);
+
+struct port_out {
+	TAILQ_ENTRY(port_out) node;
+	struct port_out_type *type;
+	void *obj;
+	uint32_t id;
+};
+
+TAILQ_HEAD(port_out_tailq, port_out);
+
+struct port_out_runtime {
+	rte_swx_port_out_pkt_tx_t pkt_tx;
+	rte_swx_port_out_flush_t flush;
+	void *obj;
+};
+
 /*
  * Pipeline.
  */
 struct rte_swx_pipeline {
 	struct port_in_type_tailq port_in_types;
 	struct port_in_tailq ports_in;
+	struct port_out_type_tailq port_out_types;
+	struct port_out_tailq ports_out;
 
 	struct port_in_runtime *in;
+	struct port_out_runtime *out;
 
 	uint32_t n_ports_in;
+	uint32_t n_ports_out;
 	int build_done;
 	int numa_node;
 };
@@ -221,6 +251,168 @@ port_in_free(struct rte_swx_pipeline *p)
 	}
 }
 
+/*
+ * Output port.
+ */
+static struct port_out_type *
+port_out_type_find(struct rte_swx_pipeline *p, const char *name)
+{
+	struct port_out_type *elem;
+
+	if (!name)
+		return NULL;
+
+	TAILQ_FOREACH(elem, &p->port_out_types, node)
+		if (!strcmp(elem->name, name))
+			return elem;
+
+	return NULL;
+}
+
+int
+rte_swx_pipeline_port_out_type_register(struct rte_swx_pipeline *p,
+					const char *name,
+					struct rte_swx_port_out_ops *ops)
+{
+	struct port_out_type *elem;
+
+	CHECK(p, EINVAL);
+	CHECK_NAME(name, EINVAL);
+	CHECK(ops, EINVAL);
+	CHECK(ops->create, EINVAL);
+	CHECK(ops->free, EINVAL);
+	CHECK(ops->pkt_tx, EINVAL);
+	CHECK(ops->stats_read, EINVAL);
+
+	CHECK(!port_out_type_find(p, name), EEXIST);
+
+	/* Node allocation. */
+	elem = calloc(1, sizeof(struct port_out_type));
+	CHECK(elem, ENOMEM);
+
+	/* Node initialization. */
+	strcpy(elem->name, name);
+	memcpy(&elem->ops, ops, sizeof(*ops));
+
+	/* Node add to tailq. */
+	TAILQ_INSERT_TAIL(&p->port_out_types, elem, node);
+
+	return 0;
+}
+
+static struct port_out *
+port_out_find(struct rte_swx_pipeline *p, uint32_t port_id)
+{
+	struct port_out *port;
+
+	TAILQ_FOREACH(port, &p->ports_out, node)
+		if (port->id == port_id)
+			return port;
+
+	return NULL;
+}
+
+int
+rte_swx_pipeline_port_out_config(struct rte_swx_pipeline *p,
+				 uint32_t port_id,
+				 const char *port_type_name,
+				 void *args)
+{
+	struct port_out_type *type = NULL;
+	struct port_out *port = NULL;
+	void *obj = NULL;
+
+	CHECK(p, EINVAL);
+
+	CHECK(!port_out_find(p, port_id), EINVAL);
+
+	CHECK_NAME(port_type_name, EINVAL);
+	type = port_out_type_find(p, port_type_name);
+	CHECK(type, EINVAL);
+
+	obj = type->ops.create(args);
+	CHECK(obj, ENODEV);
+
+	/* Node allocation. */
+	port = calloc(1, sizeof(struct port_out));
+	CHECK(port, ENOMEM);
+
+	/* Node initialization. */
+	port->type = type;
+	port->obj = obj;
+	port->id = port_id;
+
+	/* Node add to tailq. */
+	TAILQ_INSERT_TAIL(&p->ports_out, port, node);
+	if (p->n_ports_out < port_id + 1)
+		p->n_ports_out = port_id + 1;
+
+	return 0;
+}
+
+static int
+port_out_build(struct rte_swx_pipeline *p)
+{
+	struct port_out *port;
+	uint32_t i;
+
+	CHECK(p->n_ports_out, EINVAL);
+
+	for (i = 0; i < p->n_ports_out; i++)
+		CHECK(port_out_find(p, i), EINVAL);
+
+	p->out = calloc(p->n_ports_out, sizeof(struct port_out_runtime));
+	CHECK(p->out, ENOMEM);
+
+	TAILQ_FOREACH(port, &p->ports_out, node) {
+		struct port_out_runtime *out = &p->out[port->id];
+
+		out->pkt_tx = port->type->ops.pkt_tx;
+		out->flush = port->type->ops.flush;
+		out->obj = port->obj;
+	}
+
+	return 0;
+}
+
+static void
+port_out_build_free(struct rte_swx_pipeline *p)
+{
+	free(p->out);
+	p->out = NULL;
+}
+
+static void
+port_out_free(struct rte_swx_pipeline *p)
+{
+	port_out_build_free(p);
+
+	/* Output ports. */
+	for ( ; ; ) {
+		struct port_out *port;
+
+		port = TAILQ_FIRST(&p->ports_out);
+		if (!port)
+			break;
+
+		TAILQ_REMOVE(&p->ports_out, port, node);
+		port->type->ops.free(port->obj);
+		free(port);
+	}
+
+	/* Output port types. */
+	for ( ; ; ) {
+		struct port_out_type *elem;
+
+		elem = TAILQ_FIRST(&p->port_out_types);
+		if (!elem)
+			break;
+
+		TAILQ_REMOVE(&p->port_out_types, elem, node);
+		free(elem);
+	}
+}
+
 /*
  * Pipeline.
  */
@@ -239,6 +431,8 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
 	/* Initialization. */
 	TAILQ_INIT(&pipeline->port_in_types);
 	TAILQ_INIT(&pipeline->ports_in);
+	TAILQ_INIT(&pipeline->port_out_types);
+	TAILQ_INIT(&pipeline->ports_out);
 
 	pipeline->numa_node = numa_node;
 
@@ -252,6 +446,7 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p)
 	if (!p)
 		return;
 
+	port_out_free(p);
 	port_in_free(p);
 
 	free(p);
@@ -269,10 +464,15 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
 	if (status)
 		goto error;
 
+	status = port_out_build(p);
+	if (status)
+		goto error;
+
 	p->build_done = 1;
 	return 0;
 
 error:
+	port_out_build_free(p);
 	port_in_build_free(p);
 
 	return status;
diff --git a/lib/librte_pipeline/rte_swx_pipeline.h b/lib/librte_pipeline/rte_swx_pipeline.h
index 3dbe7ce0b..2be83bd35 100644
--- a/lib/librte_pipeline/rte_swx_pipeline.h
+++ b/lib/librte_pipeline/rte_swx_pipeline.h
@@ -97,6 +97,56 @@ rte_swx_pipeline_port_in_config(struct rte_swx_pipeline *p,
 				uint32_t port_id,
 				const char *port_type_name,
 				void *args);
+
+/*
+ * Pipeline output ports
+ */
+
+/**
+ * Pipeline output port type register
+ *
+ * @param[in] p
+ *   Pipeline handle.
+ * @param[in] name
+ *   Output port type name.
+ * @param[in] ops
+ *   Output port type operations.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Output port type with this name already exists.
+ */
+__rte_experimental
+int
+rte_swx_pipeline_port_out_type_register(struct rte_swx_pipeline *p,
+					const char *name,
+					struct rte_swx_port_out_ops *ops);
+
+/**
+ * Pipeline output port configure
+ *
+ * @param[in] p
+ *   Pipeline handle.
+ * @param[in] port_id
+ *   Output port ID.
+ * @param[in] port_type_name
+ *   Existing output port type name.
+ * @param[in] args
+ *   Output port creation arguments.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -ENODEV: Output port object creation error.
+ */
+__rte_experimental
+int
+rte_swx_pipeline_port_out_config(struct rte_swx_pipeline *p,
+				 uint32_t port_id,
+				 const char *port_type_name,
+				 void *args);
+
 /**
  * Pipeline build
  *
diff --git a/lib/librte_port/rte_swx_port.h b/lib/librte_port/rte_swx_port.h
index a6f80de9a..4beb59991 100644
--- a/lib/librte_port/rte_swx_port.h
+++ b/lib/librte_port/rte_swx_port.h
@@ -111,6 +111,90 @@ struct rte_swx_port_in_ops {
 	rte_swx_port_in_stats_read_t stats_read;
 };
 
+/*
+ * Output port
+ */
+
+/**
+ * Output port create
+ *
+ * @param[in] args
+ *   Arguments for output port creation. Format specific to each port type.
+ * @return
+ *   Handle to output port instance on success, NULL on error.
+ */
+typedef void *
+(*rte_swx_port_out_create_t)(void *args);
+
+/**
+ * Output port free
+ *
+ * @param[in] args
+ *   Output port handle.
+ */
+typedef void
+(*rte_swx_port_out_free_t)(void *port);
+
+/**
+ * Output port packet transmit
+ *
+ * @param[in] port
+ *   Output port handle.
+ * @param[in] pkt
+ *   Packet to be transmitted.
+ */
+typedef void
+(*rte_swx_port_out_pkt_tx_t)(void *port,
+			     struct rte_swx_pkt *pkt);
+
+/**
+ * Output port flush
+ *
+ * @param[in] port
+ *   Output port handle.
+ */
+typedef void
+(*rte_swx_port_out_flush_t)(void *port);
+
+/** Output port statistics counters. */
+struct rte_swx_port_out_stats {
+	/** Number of packets. */
+	uint64_t n_pkts;
+
+	/** Number of bytes. */
+	uint64_t n_bytes;
+};
+
+/**
+ * Output port statistics counters read
+ *
+ * @param[in] port
+ *   Output port handle.
+ * @param[out] stats
+ *   Output port statistics counters. Must point to valid memory.
+ */
+typedef void
+(*rte_swx_port_out_stats_read_t)(void *port,
+				 struct rte_swx_port_out_stats *stats);
+
+/** Output port operations. */
+struct rte_swx_port_out_ops {
+	/** Create. Must be non-NULL. */
+	rte_swx_port_out_create_t create;
+
+	/** Free. Must be non-NULL. */
+	rte_swx_port_out_free_t free;
+
+	/** Packet transmission. Must be non-NULL. */
+	rte_swx_port_out_pkt_tx_t pkt_tx;
+
+	/** Flush. May be NULL. */
+	rte_swx_port_out_flush_t flush;
+
+	/** Statistics counters read. Must be non-NULL. */
+	rte_swx_port_out_stats_read_t stats_read;
+};
+
 #ifdef __cplusplus
 }
 #endif
