diff --git a/docker/apache-spark-sample/.env b/docker/apache-spark-sample/.env
new file mode 100644
index 000000000..a047df5ba
--- /dev/null
+++ b/docker/apache-spark-sample/.env
@@ -0,0 +1,4 @@
+MASTER_UI_PORT=8080
+MASTER_PORT=7077
+UI_PORT=4040
+PPL_JAR=../../ppl-spark-integration/target/scala-2.12/ppl-spark-integration-assembly-0.7.0-SNAPSHOT.jar
diff --git a/docker/apache-spark-sample/docker-compose.yml b/docker/apache-spark-sample/docker-compose.yml
new file mode 100644
index 000000000..df2da6d52
--- /dev/null
+++ b/docker/apache-spark-sample/docker-compose.yml
@@ -0,0 +1,41 @@
+services:
+ spark:
+ image: bitnami/spark:3.5.3
+ ports:
+ - "${MASTER_UI_PORT:-8080}:8080"
+ - "${MASTER_PORT:-7077}:7077"
+ - "${UI_PORT:-4040}:4040"
+ environment:
+ - SPARK_MODE=master
+ - SPARK_RPC_AUTHENTICATION_ENABLED=no
+ - SPARK_RPC_ENCRYPTION_ENABLED=no
+ - SPARK_LOCAL_STORAGE_ENCRYPTION_ENABLED=no
+ - SPARK_SSL_ENABLED=no
+ - SPARK_PUBLIC_DNS=localhost
+ volumes:
+ - type: bind
+ source: ./spark-defaults.conf
+ target: /opt/bitnami/spark/conf/spark-defaults.conf
+ - type: bind
+ source: $PPL_JAR
+ target: /opt/bitnami/spark/jars/ppl-spark-integration.jar
+
+ spark-worker:
+ image: bitnami/spark:3.5.3
+ environment:
+ - SPARK_MODE=worker
+ - SPARK_MASTER_URL=spark://spark:7077
+ - SPARK_WORKER_MEMORY=${WORKER_MEMORY:-1G}
+ - SPARK_WORKER_CORES=${WORKER_CORES:-1}
+ - SPARK_RPC_AUTHENTICATION_ENABLED=no
+ - SPARK_RPC_ENCRYPTION_ENABLED=no
+ - SPARK_LOCAL_STORAGE_ENCRYPTION_ENABLED=no
+ - SPARK_SSL_ENABLED=no
+ - SPARK_PUBLIC_DNS=localhost
+ volumes:
+ - type: bind
+ source: ./spark-defaults.conf
+ target: /opt/bitnami/spark/conf/spark-defaults.conf
+ - type: bind
+ source: $PPL_JAR
+ target: /opt/bitnami/spark/jars/ppl-spark-integration.jar
diff --git a/docker/apache-spark-sample/spark-defaults.conf b/docker/apache-spark-sample/spark-defaults.conf
new file mode 100644
index 000000000..47fdaae03
--- /dev/null
+++ b/docker/apache-spark-sample/spark-defaults.conf
@@ -0,0 +1,29 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Default system properties included when running spark-submit.
+# This is useful for setting default environmental settings.
+
+# Example:
+# spark.master spark://master:7077
+# spark.eventLog.enabled true
+# spark.eventLog.dir hdfs://namenode:8021/directory
+# spark.serializer org.apache.spark.serializer.KryoSerializer
+# spark.driver.memory 5g
+# spark.executor.extraJavaOptions -XX:+PrintGCDetails -Dkey=value -Dnumbers="one two three"
+spark.sql.extensions org.opensearch.flint.spark.FlintPPLSparkExtensions
+spark.sql.catalog.dev org.apache.spark.opensearch.catalog.OpenSearchCatalog
diff --git a/docker/spark-emr-sample/.env b/docker/spark-emr-sample/.env
new file mode 100644
index 000000000..a717532a4
--- /dev/null
+++ b/docker/spark-emr-sample/.env
@@ -0,0 +1 @@
+PPL_JAR=../../ppl-spark-integration/target/scala-2.12/ppl-spark-integration-assembly-0.7.0-SNAPSHOT.jar
diff --git a/docker/spark-emr-sample/docker-compose.yml b/docker/spark-emr-sample/docker-compose.yml
new file mode 100644
index 000000000..d0da9f166
--- /dev/null
+++ b/docker/spark-emr-sample/docker-compose.yml
@@ -0,0 +1,17 @@
+services:
+ spark-emr:
+ image: public.ecr.aws/emr-serverless/spark/emr-7.5.0:20241125
+ volumes:
+ - type: bind
+ source: ./logging-conf
+ target: /var/loggingConfiguration/spark
+ - type: bind
+ source: ../spark-sample-app/target/scala-2.12
+ target: /app
+ - type: bind
+ source: ./spark-conf
+ target: /etc/spark/conf
+ - type: bind
+ source: ${PPL_JAR}
+ target: /usr/lib/spark/jars/ppl-spark-integration.jar
+ command: driver --class MyApp /app/myapp_2.12-1.0.jar
diff --git a/docker/spark-emr-sample/logging-conf/run-adot-collector.sh b/docker/spark-emr-sample/logging-conf/run-adot-collector.sh
new file mode 100644
index 000000000..0873413aa
--- /dev/null
+++ b/docker/spark-emr-sample/logging-conf/run-adot-collector.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+# Do nothing as default logging is sufficient
diff --git a/docker/spark-emr-sample/logging-conf/run-fluentd-spark.sh b/docker/spark-emr-sample/logging-conf/run-fluentd-spark.sh
new file mode 100644
index 000000000..0873413aa
--- /dev/null
+++ b/docker/spark-emr-sample/logging-conf/run-fluentd-spark.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+# Do nothing as default logging is sufficient
diff --git a/docker/spark-emr-sample/spark-conf/hive-site.xml b/docker/spark-emr-sample/spark-conf/hive-site.xml
new file mode 100644
index 000000000..f0dc50e1e
--- /dev/null
+++ b/docker/spark-emr-sample/spark-conf/hive-site.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hive.metastore.connect.retries
+ 15
+
+
\ No newline at end of file
diff --git a/docker/spark-emr-sample/spark-conf/log4j2.properties b/docker/spark-emr-sample/spark-conf/log4j2.properties
new file mode 100644
index 000000000..27ff7047f
--- /dev/null
+++ b/docker/spark-emr-sample/spark-conf/log4j2.properties
@@ -0,0 +1,74 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# This property will be overridden for JVMs running inside YARN containers.
+# Other log4j configurations may reference the property, for example, in order to
+# cause a log file to appear in the usual log directory for the YARN container,
+# so that LogPusher will upload it to S3. The following provides a default value
+# to be used for this property such that logs are still written to a valid location
+# even for Spark processes run *outside* of a YARN container (e.g., a Spark
+# driver run in client deploy-mode).
+spark.yarn.app.container.log.dir=/var/log/spark/user/${user.name}
+
+# Set everything to be logged to the console
+rootLogger.level = info
+rootLogger.appenderRef.stdout.ref = console
+
+appender.console.type = Console
+appender.console.name = console
+appender.console.target = SYSTEM_ERR
+appender.console.layout.type = PatternLayout
+appender.console.layout.pattern = %d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n
+
+# Set the default spark-shell/spark-sql log level to WARN. When running the
+# spark-shell/spark-sql, the log level for these classes is used to overwrite
+# the root logger's log level, so that the user can have different defaults
+# for the shell and regular Spark apps.
+logger.repl.name = org.apache.spark.repl.Main
+logger.repl.level = warn
+
+logger.thriftserver.name = org.apache.spark.sql.hive.thriftserver.SparkSQLCLIDriver
+logger.thriftserver.level = warn
+
+# Settings to quiet third party logs that are too verbose
+logger.jetty1.name = org.sparkproject.jetty
+logger.jetty1.level = warn
+logger.jetty2.name = org.sparkproject.jetty.util.component.AbstractLifeCycle
+logger.jetty2.level = error
+logger.replexprTyper.name = org.apache.spark.repl.SparkIMain$exprTyper
+logger.replexprTyper.level = info
+logger.replSparkILoopInterpreter.name = org.apache.spark.repl.SparkILoop$SparkILoopInterpreter
+logger.replSparkILoopInterpreter.level = info
+logger.parquet1.name = org.apache.parquet
+logger.parquet1.level = error
+logger.parquet2.name = parquet
+logger.parquet2.level = error
+logger.hudi.name = org.apache.hudi
+logger.hudi.level = warn
+
+# SPARK-9183: Settings to avoid annoying messages when looking up nonexistent UDFs in SparkSQL with Hive support
+logger.RetryingHMSHandler.name = org.apache.hadoop.hive.metastore.RetryingHMSHandler
+logger.RetryingHMSHandler.level = fatal
+logger.FunctionRegistry.name = org.apache.hadoop.hive.ql.exec.FunctionRegistry
+logger.FunctionRegistry.level = error
+
+# For deploying Spark ThriftServer
+# SPARK-34128: Suppress undesirable TTransportException warnings involved in THRIFT-4805
+appender.console.filter.1.type = RegexFilter
+appender.console.filter.1.regex = .*Thrift error occurred during processing of message.*
+appender.console.filter.1.onMatch = deny
+appender.console.filter.1.onMismatch = neutral
\ No newline at end of file
diff --git a/docker/spark-emr-sample/spark-conf/metrics.properties b/docker/spark-emr-sample/spark-conf/metrics.properties
new file mode 100644
index 000000000..e69de29bb
diff --git a/docker/spark-emr-sample/spark-conf/spark-defaults.conf b/docker/spark-emr-sample/spark-conf/spark-defaults.conf
new file mode 100644
index 000000000..0a5dabe7d
--- /dev/null
+++ b/docker/spark-emr-sample/spark-conf/spark-defaults.conf
@@ -0,0 +1,65 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+spark.driver.extraClassPath /usr/lib/livy/rsc-jars/*:/usr/lib/livy/repl_2.12-jars/*:/usr/lib/hadoop-lzo/lib/*:/usr/lib/hadoop/hadoop-aws.jar:/usr/share/aws/aws-java-sdk/*:/usr/share/aws/emr/emrfs/conf:/usr/share/aws/emr/emrfs/lib/*:/usr/share/aws/emr/emrfs/auxlib/*:/usr/share/aws/emr/goodies/lib/emr-spark-goodies.jar:/usr/share/aws/emr/goodies/lib/emr-serverless-spark-goodies.jar:/usr/share/aws/emr/security/conf:/usr/share/aws/emr/security/lib/*:/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar:/docker/usr/lib/hadoop-lzo/lib/*:/docker/usr/lib/hadoop/hadoop-aws.jar:/docker/usr/share/aws/aws-java-sdk/*:/docker/usr/share/aws/emr/emrfs/conf:/docker/usr/share/aws/emr/emrfs/lib/*:/docker/usr/share/aws/emr/emrfs/auxlib/*:/docker/usr/share/aws/emr/goodies/lib/emr-spark-goodies.jar:/docker/usr/share/aws/emr/security/conf:/docker/usr/share/aws/emr/security/lib/*:/docker/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/docker/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/docker/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/docker/usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar:/usr/share/aws/redshift/jdbc/RedshiftJDBC.jar:/usr/share/aws/redshift/spark-redshift/lib/*:/usr/share/aws/iceberg/lib/iceberg-emr-common.jar:/usr/share/aws/iceberg/lib/iceberg-spark3-runtime.jar
+spark.driver.extraLibraryPath /usr/lib/hadoop/lib/native:/usr/lib/hadoop-lzo/lib/native:/docker/usr/lib/hadoop/lib/native:/docker/usr/lib/hadoop-lzo/lib/native
+spark.executor.extraClassPath /usr/lib/hadoop-lzo/lib/*:/usr/lib/hadoop/hadoop-aws.jar:/usr/share/aws/aws-java-sdk/*:/usr/share/aws/emr/emrfs/conf:/usr/share/aws/emr/emrfs/lib/*:/usr/share/aws/emr/emrfs/auxlib/*:/usr/share/aws/emr/goodies/lib/emr-spark-goodies.jar:/usr/share/aws/emr/goodies/lib/emr-serverless-spark-goodies.jar:/usr/share/aws/emr/security/conf:/usr/share/aws/emr/security/lib/*:/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar:/docker/usr/lib/hadoop-lzo/lib/*:/docker/usr/lib/hadoop/hadoop-aws.jar:/docker/usr/share/aws/aws-java-sdk/*:/docker/usr/share/aws/emr/emrfs/conf:/docker/usr/share/aws/emr/emrfs/lib/*:/docker/usr/share/aws/emr/emrfs/auxlib/*:/docker/usr/share/aws/emr/goodies/lib/emr-spark-goodies.jar:/docker/usr/share/aws/emr/security/conf:/docker/usr/share/aws/emr/security/lib/*:/docker/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/docker/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/docker/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/docker/usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar:/usr/share/aws/redshift/jdbc/RedshiftJDBC.jar:/usr/share/aws/redshift/spark-redshift/lib/*:/usr/share/aws/iceberg/lib/iceberg-emr-common.jar:/usr/share/aws/iceberg/lib/iceberg-spark3-runtime.jar
+spark.executor.extraLibraryPath /usr/lib/hadoop/lib/native:/usr/lib/hadoop-lzo/lib/native:/docker/usr/lib/hadoop/lib/native:/docker/usr/lib/hadoop-lzo/lib/native
+spark.eventLog.enabled true
+spark.eventLog.dir file:///var/log/spark/apps
+spark.history.fs.logDirectory file:///var/log/spark/apps
+spark.history.ui.port 18080
+spark.blacklist.decommissioning.enabled true
+spark.blacklist.decommissioning.timeout 1h
+spark.resourceManager.cleanupExpiredHost true
+spark.stage.attempt.ignoreOnDecommissionFetchFailure true
+spark.decommissioning.timeout.threshold 20
+spark.files.fetchFailure.unRegisterOutputOnHost true
+spark.hadoop.mapreduce.fileoutputcommitter.algorithm.version.emr_internal_use_only.EmrFileSystem 2
+spark.hadoop.mapreduce.fileoutputcommitter.cleanup-failures.ignored.emr_internal_use_only.EmrFileSystem true
+spark.hadoop.fs.s3.getObject.initialSocketTimeoutMilliseconds 2000
+spark.sql.parquet.output.committer.class com.amazon.emr.committer.EmrOptimizedSparkSqlParquetOutputCommitter
+spark.sql.parquet.fs.optimized.committer.optimization-enabled true
+spark.sql.emr.internal.extensions com.amazonaws.emr.spark.EmrSparkSessionExtensions
+spark.executor.memory 14G
+spark.executor.cores 4
+spark.driver.memory 14G
+spark.driver.cores 4
+spark.executor.defaultJavaOptions -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseParallelGC -XX:InitiatingHeapOccupancyPercent=70 -XX:OnOutOfMemoryError='kill -9 %p'
+spark.driver.defaultJavaOptions -XX:OnOutOfMemoryError='kill -9 %p'
+spark.hadoop.mapreduce.output.fs.optimized.committer.enabled true
+
+spark.master custom:emr-serverless
+spark.submit.deployMode client
+spark.submit.customResourceManager.submit.class org.apache.spark.deploy.emrserverless.submit.EmrServerlessClientApplication
+spark.hadoop.fs.defaultFS file:///
+spark.dynamicAllocation.enabled true
+spark.dynamicAllocation.shuffleTracking.enabled true
+spark.hadoop.fs.s3.customAWSCredentialsProvider com.amazonaws.auth.DefaultAWSCredentialsProviderChain
+spark.authenticate true
+spark.ui.enabled false
+spark.ui.custom.executor.log.url /logs/{{CONTAINER_ID}}/{{FILE_NAME}}.gz
+
+spark.emr-serverless.client.create.batch.size 100
+spark.emr-serverless.client.describe.batch.size 100
+spark.emr-serverless.client.release.batch.size 100
+spark.dynamicAllocation.initialExecutors 3
+spark.dynamicAllocation.minExecutors 0
+spark.executor.instances 3
+spark.hadoop.fs.s3a.aws.credentials.provider software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider
+spark.sql.hive.metastore.sharedPrefixes software.amazon.awssdk.services.dynamodb
+spark.sql.legacy.createHiveTableByDefault false
+spark.sql.extensions org.opensearch.flint.spark.FlintPPLSparkExtensions
+spark.sql.catalog.dev org.apache.spark.opensearch.catalog.OpenSearchCatalog
diff --git a/docker/spark-emr-sample/spark-conf/spark-env.sh b/docker/spark-emr-sample/spark-conf/spark-env.sh
new file mode 100644
index 000000000..a40f294b6
--- /dev/null
+++ b/docker/spark-emr-sample/spark-conf/spark-env.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+export SPARK_HOME=${SPARK_HOME:-/usr/lib/spark}
+export SPARK_LOG_DIR=${SPARK_LOG_DIR:-/var/log/spark}
+export HADOOP_HOME=${HADOOP_HOME:-/usr/lib/hadoop}
+export HADOOP_CONF_DIR=${HADOOP_CONF_DIR:-/etc/hadoop/conf}
+export HIVE_CONF_DIR=${HIVE_CONF_DIR:-/etc/hive/conf}
+
+export SPARK_MASTER_PORT=7077
+export SPARK_MASTER_IP=$STANDALONE_SPARK_MASTER_HOST
+export SPARK_MASTER_WEBUI_PORT=8080
+
+export SPARK_WORKER_DIR=${SPARK_WORKER_DIR:-/var/run/spark/work}
+export SPARK_WORKER_PORT=7078
+export SPARK_WORKER_WEBUI_PORT=8081
+
+export HIVE_SERVER2_THRIFT_BIND_HOST=0.0.0.0
+export HIVE_SERVER2_THRIFT_PORT=10001
+
+
+export SPARK_DAEMON_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS -XX:OnOutOfMemoryError='kill -9 %p'"
+export PYSPARK_PYTHON=${PYSPARK_PYTHON:-/usr/bin/python3}
+export PYSPARK_DRIVER_PYTHON=${PYSPARK_DRIVER_PYTHON:-/usr/bin/python3}
+
+export AWS_STS_REGIONAL_ENDPOINTS=regional
diff --git a/docker/spark-sample-app/build.sbt b/docker/spark-sample-app/build.sbt
new file mode 100644
index 000000000..ea49bfd20
--- /dev/null
+++ b/docker/spark-sample-app/build.sbt
@@ -0,0 +1,8 @@
+name := "MyApp"
+
+version := "1.0"
+
+scalaVersion := "2.12.20"
+
+libraryDependencies += "org.apache.spark" %% "spark-sql" % "3.5.3"
+
diff --git a/docker/spark-sample-app/src/main/scala/MyApp.scala b/docker/spark-sample-app/src/main/scala/MyApp.scala
new file mode 100644
index 000000000..6e2171c41
--- /dev/null
+++ b/docker/spark-sample-app/src/main/scala/MyApp.scala
@@ -0,0 +1,27 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import org.apache.spark.sql.SparkSession
+
+object MyApp {
+ def main(args: Array[String]): Unit = {
+ var spark = SparkSession.builder()
+ .master("local[1]")
+ .appName("MyApp")
+ .getOrCreate();
+
+ println("APP Name :" + spark.sparkContext.appName);
+ println("Deploy Mode :" + spark.sparkContext.deployMode);
+ println("Master :" + spark.sparkContext.master);
+
+ spark.sql("CREATE table foo (id int, name varchar(100))").show()
+ println(">>> Table created")
+ spark.sql("SELECT * FROM foo").show()
+ println(">>> SQL query of table completed")
+
+ spark.sql("source=foo | fields id").show()
+ println(">>> PPL query of table completed")
+ }
+}
diff --git a/docs/spark-docker.md b/docs/spark-docker.md
new file mode 100644
index 000000000..d1200e2b3
--- /dev/null
+++ b/docs/spark-docker.md
@@ -0,0 +1,164 @@
+# Running Queries with Apache Spark in Docker
+
+There are [Bitnami Apache Spark docker images](https://hub.docker.com/r/bitnami/spark). These
+can be modified to be able to include the OpenSearch Spark PPL extension. With the OpenSearch
+Spark PPL extension, the docker image can be used to test PPL commands.
+
+The Bitnami Apache Spark image can be used to run a Spark cluster and also to run
+`spark-shell` for running queries.
+
+## Prepare OpenSearch Spark PPL Extension
+
+Create a local build or copy of the OpenSearch Spark PPL extension. Make a note of the
+location of the Jar file as well as the name of the Jar file.
+
+From the root of this repository, build the OpenSearch Spark PPL extension with:
+
+```
+sbt clean
+sbt assembly
+```
+
+Refer to the [Developer Guide](../DEVELOPER_GUIDE.md) for more information.
+
+## Using Docker Compose
+
+There are sample files in this repository at `docker/apache-spark-sample` They can be used to
+start up both nodes with the command:
+
+```
+docker compose up -d
+```
+
+The cluster can be stopped with:
+
+```
+docker compose down
+```
+
+### Configuration
+
+There is a file `docker/apache-spark-sample/.env` that can be edited to change some settings.
+
+| Variable Name | Description |
+|----------------|---------------------------------------------------|
+| MASTER_UI_PORT | Host port to bind to port 8080 of the master node |
+| MASTER_PORT | Host port to bind to port 7077 of the master node |
+| UI_PORT | Host port to bind to port 4040 of the master node |
+| PPL_JAR | Path to the PPL Jar file |
+
+## Running Spark Shell
+
+Can run `spark-shell` on the master node.
+
+```
+docker exec -it apache-spark-sample-spark-1 /opt/bitnami/spark/bin/spark-shell
+```
+
+Within the Spark Shell, you can submit queries, including PPL queries. For example a sample
+table can be created, populated and finally queried using PPL.
+
+```
+spark.sql("CREATE TABLE test_table(id int, name varchar(100))")
+spark.sql("INSERT INTO test_table (id, name) VALUES(1, 'Foo')")
+spark.sql("INSERT INTO test_table (id, name) VALUES(2, 'Bar')")
+spark.sql("source=test_table | eval x = id + 5 | fields x, name").show()
+```
+
+For further information, see the [Spark PPL Test Instructions](ppl-lang/local-spark-ppl-test-instruction.md)
+
+## Manual Setup
+
+### spark-conf
+
+Contains the Apache Spark configuration. Need to add three lines to the `spark-defaults.conf`
+file:
+```
+spark.sql.legacy.createHiveTableByDefault false
+spark.sql.extensions org.opensearch.flint.spark.FlintPPLSparkExtensions
+spark.sql.catalog.dev org.apache.spark.opensearch.catalog.OpenSearchCatalog
+```
+
+An example file available in this repository at `docker/apache-spark-sample/spark-defaults.conf`
+
+## Prepare OpenSearch Spark PPL Extension
+
+Create a local build or copy of the OpenSearch Spark PPL extension. Make a note of the
+location of the Jar file as well as the name of the Jar file.
+
+## Run the Spark Cluster
+
+Need to run a master node and a worker node. For these to communicate, first create a network
+for them to use.
+
+```
+docker network create spark-network
+```
+
+### Master Node
+
+The master node can be run with the following command:
+```
+docker run \
+ -d \
+ --name spark \
+ --network spark-network \
+ -p 8080:8080 \
+ -p 7077:7077 \
+ -p 4040:4040 \
+ -e SPARK_MODE=master \
+ -e SPARK_RPC_AUTHENTICATION_ENABLED=no \
+ -e SPARK_RPC_ENCRYPTION_ENABLED=no \
+ -e SPARK_LOCAL_STORAGE_ENCRYPTION_ENABLED=no \
+ -e SPARK_SSL_ENABLED=no \
+ -e SPARK_PUBLIC_DNS=localhost \
+ -v :/opt/bitnami/spark/conf/spark-defaults.conf \
+ -v /:/opt/bitnami/spark/jars/ \
+ bitnami/spark:3.5.3
+```
+
+* `-d`
+ Run the container in the background and return to the shell
+* `--name spark`
+ Name the docker container `spark`
+* ``
+ Replace with the path to the Spark configuration file.
+* ``
+ Replace with the path to the directory containing the OpenSearch Spark PPL extension
+ Jar file.
+* ``
+ Replace with the filename of the OpenSearch Spark PPL extension Jar file.
+
+### Worker Node
+
+The worker node can be run with the following command:
+```
+docker run \
+ -d \
+ --name spark-worker \
+ --network spark-network \
+ -e SPARK_MODE=worker \
+ -e SPARK_MASTER_URL=spark://spark:7077 \
+ -e SPARK_WORKER_MEMORY=1G \
+ -e SPARK_WORKER_CORES=1 \
+ -e SPARK_RPC_AUTHENTICATION_ENABLED=no \
+ -e SPARK_RPC_ENCRYPTION_ENABLED=no \
+ -e SPARK_LOCAL_STORAGE_ENCRYPTION_ENABLED=no \
+ -e SPARK_SSL_ENABLED=no \
+ -e SPARK_PUBLIC_DNS=localhost \
+ -v :/opt/bitnami/spark/conf/spark-defaults.conf \
+ -v /:/opt/bitnami/spark/jars/ \
+ bitnami/spark:3.5.3
+```
+
+* `-d`
+ Run the container in the background and return to the shell
+* `--name spark-worker`
+ Name the docker container `spark-worker`
+* ``
+ Replace with the path to the Spark configuration file.
+* ``
+ Replace with the path to the directory containing the OpenSearch Spark PPL extension
+ Jar file.
+* ``
+ Replace with the filename of the OpenSearch Spark PPL extension Jar file.
diff --git a/docs/spark-emr-docker.md b/docs/spark-emr-docker.md
new file mode 100644
index 000000000..7eef4d250
--- /dev/null
+++ b/docs/spark-emr-docker.md
@@ -0,0 +1,147 @@
+# Running Queries with Spark EMR in Docker
+
+Spark EMR images are available on the Amazon ECR Public Gallery. These can be modified to
+be able to include the OpenSearch Spark PPL extension. With the OpenSearch Spark PPL
+extension, the docker image can be used to test PPL commands.
+
+The Spark EMR image will run an Apache Spark app if one was specified and then shutdown.
+
+## Prepare OpenSearch Spark PPL Extension
+
+Create a local build or copy of the OpenSearch Spark PPL extension. Make a note of the
+location of the Jar file as well as the name of the Jar file.
+
+From the root of this repository, build the OpenSearch Spark PPL extension with:
+
+```
+sbt clean
+sbt assembly
+```
+
+Refer to the [Developer Guide](../DEVELOPER_GUIDE.md) for more information.
+
+## Using Docker Compose
+
+There are sample files in this repository at `docker/spark-emr-sample` They can be used to
+run the Spark EMR container:
+
+```
+docker compose up
+```
+
+Remove the docker resources afterwards with:
+
+```
+docker compose down
+```
+
+### Configuration
+
+There is a file `docker/spark-emr-sample/.env` that can be edited to change some settings.
+
+| Variable Name | Description |
+|----------------|---------------------------------------------------|
+| PPL_JAR | Path to the PPL Jar file |
+
+## Logs
+
+The logs are available in `/var/log/spark` in the docker container.
+
+STDERR for the app run is available in `/var/log/spark/user/stderr`.
+
+STDOUT for the app
+run is available in `/var/log/spark/user/stdout`.
+
+## Manual Setup
+
+Need to create two directories. These directories will be bound to the directories in the
+image.
+
+Look in `docker/spark-emr-sample` in this repository for samples of the directories
+described below.
+
+### logging-conf
+Contains two shell scripts that are run during startup to configure logging.
+* `run-adot-collector.sh`
+* `run-fluentd-spark.sh`
+
+Unless you need to make changes to the logging in the docker image, these can both be
+empty shell scripts.
+
+### spark-conf
+
+Contains the Apache Spark configuration. Need to add three lines to the `spark-defaults.conf`
+file:
+```
+spark.sql.legacy.createHiveTableByDefault false
+spark.sql.extensions org.opensearch.flint.spark.FlintPPLSparkExtensions
+spark.sql.catalog.dev org.apache.spark.opensearch.catalog.OpenSearchCatalog
+```
+
+## Create a Spark App
+
+An Apache Spark app is needed to provide queries to be run on the Spark EMR instance.
+The image has been tested with an app written in Scala.
+
+An example app is available in this repository in `docker/spark-sample--app`.
+
+### Bulid the Example App
+
+The example app can be built using [SBT](https://www.scala-sbt.org/).
+```
+cd docker/spark-sample-app
+sbt clean package
+```
+
+This will produce a Jar file in `docker/spark-sample-app/target/scala-2.12`
+that can be used with the Spark EMR image.
+
+## Prepare OpenSearch Spark PPL Extension
+
+Create a local build or copy of the OpenSearch Spark PPL extension. Make a note of the
+location of the Jar file as well as the name of the Jar file.
+
+## Run the Spark EMR Image
+
+The Spark EMR image can be run with the following command from the root of this repository:
+```
+docker run \
+ --name spark-emr \
+ -v ./docker/spark-emr-sample/logging-conf:/var/loggingConfiguration/spark \
+ -v ./docker/spark-sample-app/target/scala-2.12:/app \
+ -v ./docker/spark-emr-sample/spark-conf:/etc/spark/conf \
+ -v /:/usr/lib/spark/jars/ \
+ public.ecr.aws/emr-serverless/spark/emr-7.5.0:20241125 \
+ driver \
+ --class MyApp \
+ /app/myapp_2.12-1.0.jar
+```
+
+* `--name spark-emr`
+ Name the docker container `spark-emr`
+* `-v ./docker/spark-emr-sample/logging-conf:/var/loggingConfiguration/spark`
+
+ Bind the directory containing logging shell scripts to the docker image. Needs to bind
+ to `/var/loggingConfiguration/spark` in the image.
+* `-v ./docker/spark-sample-app/target/scala-2.12:/app`
+
+ Bind the directory containing the Apache Spark app Jar file to a location in the
+ docker image. The directory in the docker image must match the path used in the final
+ argument.
+* `-v ./docker/spark-emr-sample/spark-conf:/etc/spark/conf`
+
+ Bind the directory containing the Apache Spark configuration. Needs to bind to
+ `/etc/spark/conf` in the image.
+* ``
+ Replace with the path to the directory containing the OpenSearch Spark PPL extension
+ Jar file.
+* ``
+ Replace with the filename of the OpenSearch Spark PPL extension Jar file.
+* `driver`
+ Start the Spark EMR container as a driver. This will run `spark-submit` to run an
+ app.
+* `--class MyApp`
+ The main class of the Spark App to run.
+* `/app/myapp_2.12-1.0.jar`
+ The full path within the docker container where the Jar file of the Spark app is
+ located.