diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/v_l1_norm.md b/docs/reference/query-languages/esql/_snippets/functions/description/v_l1_norm.md
new file mode 100644
index 0000000000000..23697416312a7
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/v_l1_norm.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Calculates the l1 norm between two dense_vectors.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/v_l1_norm.md b/docs/reference/query-languages/esql/_snippets/functions/examples/v_l1_norm.md
new file mode 100644
index 0000000000000..b3efac50b7ae7
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/v_l1_norm.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+null
+```
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/v_l1_norm.md b/docs/reference/query-languages/esql/_snippets/functions/layout/v_l1_norm.md
new file mode 100644
index 0000000000000..b23e25ce17016
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/v_l1_norm.md
@@ -0,0 +1,27 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+## `V_L1_NORM` [esql-v_l1_norm]
+```{applies_to}
+stack: development
+serverless: preview
+```
+
+**Syntax**
+
+:::{image} ../../../images/functions/v_l1_norm.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/v_l1_norm.md
+:::
+
+:::{include} ../description/v_l1_norm.md
+:::
+
+:::{include} ../types/v_l1_norm.md
+:::
+
+:::{include} ../examples/v_l1_norm.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/v_l1_norm.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/v_l1_norm.md
new file mode 100644
index 0000000000000..2919df7816d41
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/v_l1_norm.md
@@ -0,0 +1,10 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`left`
+: first dense_vector to calculate l1 norm similarity
+
+`right`
+: second dense_vector to calculate l1 norm similarity
+
diff --git a/docs/reference/query-languages/esql/images/functions/v_l1_norm.svg b/docs/reference/query-languages/esql/images/functions/v_l1_norm.svg
new file mode 100644
index 0000000000000..58d5e03874662
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/v_l1_norm.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/v_l1_norm.json b/docs/reference/query-languages/esql/kibana/definition/functions/v_l1_norm.json
new file mode 100644
index 0000000000000..a46c2c8b0b62f
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/v_l1_norm.json
@@ -0,0 +1,12 @@
+{
+ "comment" : "This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "v_l1_norm",
+ "description" : "Calculates the l1 norm between two dense_vectors.",
+ "signatures" : [ ],
+ "examples" : [
+ null
+ ],
+ "preview" : true,
+ "snapshot_only" : true
+}
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/v_l1_norm.md b/docs/reference/query-languages/esql/kibana/docs/functions/v_l1_norm.md
new file mode 100644
index 0000000000000..40a1b5efbd625
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/v_l1_norm.md
@@ -0,0 +1,8 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+### V L1 NORM
+Calculates the l1 norm between two dense_vectors.
+
+```esql
+null
+```
diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/vector-l1-norm.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/vector-l1-norm.csv-spec
new file mode 100644
index 0000000000000..5c85a7e711982
--- /dev/null
+++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/vector-l1-norm.csv-spec
@@ -0,0 +1,90 @@
+ # Tests for l1_norm similarity function
+
+ similarityWithVectorField
+ required_capability: l1_norm_vector_similarity_function
+
+// tag::vector-l1-norm-similarity[]
+ from colors
+ | eval similarity = v_l1_norm(rgb_vector, [0, 255, 255])
+ | sort similarity desc, color asc
+// end::vector-l1-norm-similarity[]
+ | limit 10
+ | keep color, similarity
+ ;
+
+// tag::vector-l1-norm-similarity-result[]
+color:text | similarity:double
+red | 765.0
+crimson | 650.0
+maroon | 638.0
+firebrick | 620.0
+orange | 600.0
+tomato | 595.0
+brown | 591.0
+chocolate | 585.0
+coral | 558.0
+gold | 550.0
+// end::vector-l1-norm-similarity-result[]
+;
+
+ similarityAsPartOfExpression
+ required_capability: l1_norm_vector_similarity_function
+
+ from colors
+ | eval score = round((1 + v_l1_norm(rgb_vector, [0, 255, 255]) / 2), 3)
+ | sort score desc, color asc
+ | limit 10
+ | keep color, score
+ ;
+
+color:text | score:double
+red | 383.5
+crimson | 326.0
+maroon | 320.0
+firebrick | 311.0
+orange | 301.0
+tomato | 298.5
+brown | 296.5
+chocolate | 293.5
+coral | 280.0
+gold | 276.0
+;
+
+similarityWithLiteralVectors
+required_capability: l1_norm_vector_similarity_function
+
+row a = 1
+| eval similarity = round(v_l1_norm([1, 2, 3], [0, 1, 2]), 3)
+| keep similarity
+;
+
+similarity:double
+3.0
+;
+
+ similarityWithStats
+ required_capability: l1_norm_vector_similarity_function
+
+ from colors
+ | eval similarity = round(v_l1_norm(rgb_vector, [0, 255, 255]), 3)
+ | stats avg = round(avg(similarity), 3), min = min(similarity), max = max(similarity)
+ ;
+
+avg:double | min:double | max:double
+391.254 | 0.0 | 765.0
+;
+
+# TODO Need to implement a conversion function to convert a non-foldable row to a dense_vector
+similarityWithRow-Ignore
+required_capability: l1_norm_vector_similarity_function
+
+row vector = [1, 2, 3]
+| eval similarity = round(v_l1_norm(vector, [0, 1, 2]), 3)
+| sort similarity desc, color asc
+| limit 10
+| keep color, similarity
+;
+
+similarity:double
+0.978
+;
diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/vector/VectorSimilarityFunctionsIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/vector/VectorSimilarityFunctionsIT.java
index 17a1d5e945af3..debe448834f8d 100644
--- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/vector/VectorSimilarityFunctionsIT.java
+++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/vector/VectorSimilarityFunctionsIT.java
@@ -20,6 +20,8 @@
import org.elasticsearch.xpack.esql.EsqlTestUtils;
import org.elasticsearch.xpack.esql.action.AbstractEsqlIntegTestCase;
import org.elasticsearch.xpack.esql.action.EsqlCapabilities;
+import org.elasticsearch.xpack.esql.expression.function.vector.L1Norm;
+import org.elasticsearch.xpack.esql.expression.function.vector.VectorSimilarityFunction.SimilarityEvaluatorFunction;
import org.junit.Before;
import java.io.IOException;
@@ -37,22 +39,25 @@ public static Iterable