Paths and file systems on native Spark
Apache Hop uses two different path languages. They look similar (both use scheme://… URIs) but resolve through different stacks, credentials, and plugins. Mixing them is a common source of confusion when you move from the local engine to the native Spark pipeline engine.
Two dialects
| Dialect | Resolved by | Typical schemes | Used for |
|---|---|---|---|
Hop VFS URI | Apache Commons VFS via |
| Hop GUI file dialog, classic transforms (Text File Input/Output, Get File Names, …), loading pipeline/metadata on the Spark driver |
Spark URI (Hadoop FileSystem URI) | Spark SQL Dataset I/O → Hadoop |
| Spark File Input/Output, Lake Table PATH mode, Iceberg/Delta warehouse locations |
| A path that opens in the Hop File dialog (Hop VFS) is not automatically valid for Spark File Input. Spark never calls |
For the full list of Hop VFS schemes, see Virtual File Systems.
When each stack is used on native Spark
| Feature | Path dialect | Notes |
|---|---|---|
Spark File Input / Spark File Output | Spark URI |
|
Spark Lake Table (PATH mode) | Spark URI | |
Spark Catalog warehouse / Iceberg | Spark URI | Templates use |
Classic transforms via | Hop VFS URI | Each partition runs a small Hop pipeline; VFS plugins and named connections work if the executor classpath and credentials allow it |
| Hop VFS URI (driver only) | Driver must open the definition files; data paths still must work on executors |
Scheme collision matrix
| You write | Hop VFS meaning | Spark meaning | What to do on native Spark File/Lake |
|---|---|---|---|
| AWS S3 via Hop’s S3 VFS plugin (AWS SDK) | Often legacy or unused; production Spark expects S3A | Use |
| Not a Hop VFS scheme (unless you add a bridge plugin) | Hadoop S3A FileSystem | Correct for Spark; configure IAM / keys / endpoint for S3A |
| Named MinIO or other VFS connection metadata | Unknown scheme unless Hadoop is registered for that name | Do not use named VFS schemes in Spark File path. Use |
| Hop Azure VFS | Spark usually wants | Use Azure Hadoop schemes + connector conf for Dataset I/O |
| Hop Google Cloud Storage VFS | Spark GCS connector (same scheme, different jars/credentials) | Works only if the Spark GCS connector and credentials are on the cluster |
| Resolved, then Hop VFS or local rules | Resolved, then Hadoop default FS / local path | On cluster submit, set |
MinIO and S3-compatible storage
Hop’s Minio Connection metadata registers the connection name as a VFS scheme (for example connection minio → minio:///demo/…). That path works with classic Hop transforms and the file dialog.
On Spark File Input/Output and Lake PATH:
-
Put objects under a URI Spark’s S3A client can open, typically
s3a://bucket/prefix/…. -
Point S3A at your endpoint with run-configuration Spark config lines, for example:
spark.hadoop.fs.s3a.endpoint=https://minio.example.com:9000
spark.hadoop.fs.s3a.path.style.access=true
spark.hadoop.fs.s3a.access.key=...
spark.hadoop.fs.s3a.secret.key=... (Exact keys depend on your Hadoop/Spark version and security model; prefer IAM roles on cloud clusters over embedding keys when possible.)
Project package (nested pipelines / mappings)
Native Spark ships only the top-level pipeline XML plus metadata JSON by default. Transforms such as Simple Mapping and Pipeline Executor load child .hpl files at runtime via Hop VFS using paths like ${PROJECT_HOME}/mappings/child.hpl. On a cluster those files must exist where every executor can open them.
Export a Spark project package
This package is specifically for Native Spark execution (nested Simple Mapping / Pipeline Executor on the cluster). It is not the same as File → Export current project to zip.
GUI: Tools → Export project package for Native Spark… (requires an active project / PROJECT_HOME).
CLI:
# Preferred for scripts (no project registration required):
./hop-conf.sh --export-spark-project=/tmp/my-project-spark.zip \
--export-spark-project-home=/path/to/project
# Or enable a registered project first (-j, not --project/-p):
./hop-conf.sh -j my-project --export-spark-project=/tmp/my-project-spark.zip (Short option for the zip path: -esp.)
Zip layout:
-
metadata.json— Hop metadata (run configs, connections, catalogs) -
hop-spark-package.properties— marker (projectRoot=project) -
project/…— project home files (see filters below)
Package include / exclude filters
Export does not read .gitignore. Built-in skipped directory basenames (case-insensitive):
work, datasets, target, .git, node_modules, .idea, .settings
Configure extras with spark-package.json in the project home (Tools → Edit Native Spark package filters… in Hop GUI, or edit the file by hand):
{
"excludeDirs": [ "tmp", "screenshots" ],
"excludeGlobs": [ "**/*.jar", "**/*.zip" ],
"includePaths": [ "work/cluster-env.json" ],
"replaceDefaultExcludeDirs": false
} | Field | Meaning |
|---|---|
| Extra directory basenames to skip (merged with built-ins unless replace is true) |
| Globs relative to project home (e.g. |
| Relative paths force-included even under a skipped dir |
| If true, only the listed |
hop-conf (merged on top of spark-package.json):
./hop-conf.sh --export-spark-project=/tmp/pkg.zip \
--export-spark-project-home=/path/to/project \
--export-spark-project-exclude-dirs=tmp,screenshots \
--export-spark-project-exclude-globs='**/*.jar' \
--export-spark-project-include=work/cluster-env.json Databricks Deploy & run auto-export uses the same rules (reads spark-package.json when present).
Run with MainSpark
"${SPARK_HOME}/bin/spark-submit" \
--class org.apache.hop.spark.run.MainSpark \
/tmp/hop-native-spark4-submit.jar \
--HopProjectPackage=/tmp/my-project-spark.zip \
--HopPipelinePath=pipelines/run-on-spark.hpl \
--HopRunConfigurationName=YourNativeSparkRunConfig \
[--HopConfigFile=/path/cluster-env.json] Databricks Deploy & run: the Databricks Job Run action can upload this package (export from PROJECT_HOME or an existing zip) and an optional environment config file to the Volume base path, then pass the same named MainSpark arguments on the JAR task. Enable Upload Spark project package on the action when the pipeline needs nested definitions.
On Databricks, packages under /Volumes/… (or /dbfs/…) are treated as cluster-shared: Hop does not call SparkContext.addFile for them. Executors open the same Volume path (UC FUSE mount). That avoids failures such as Failed to fetch spark://…/files/hop-spark-package.zip / Stream '/files/…' was not found when the driver cannot serve SparkFiles to job-cluster workers.
Flow:
-
Driver materializes the package under
${java.io.tmpdir}/hop-spark-pkg-*/and setsPROJECT_HOME. -
When the Native Spark session starts, the engine calls
SparkContext.addFileso every executor receives the zip (SparkFiles). -
Each mini-pipeline init (
HopMapPartitionsFn) resolves the zip viaSparkFiles.get, extracts once per worker JVM, and setsPROJECT_HOMEfor Hop VFS loads.
Variables:
-
HOP_SPARK_PROJECT_PACKAGE— local path to the zip (after staging) -
HOP_SPARK_PROJECT_PACKAGE_SPARK_FILE— basename forSparkFiles.get -
PROJECT_HOME— extracted definition root on that JVM
| With a project package,
The path scheme map ( |
Limitations: Workflow Executor and dynamic Pipeline Executor filenames remain experimental. The package must be readable on the driver at submit time so addFile can stage it (local path or VFS URI Hop can open).
Path scheme map (run configuration)
The Native Spark pipeline run configuration includes an optional Path scheme map field: multi-line from=to entries applied only to Spark File Input/Output and Lake Table PATH URIs after variable resolution (not to classic Hop VFS / mapPartitions paths).
Example:
# Hop VFS / named schemes -> Spark/Hadoop schemes
s3=s3a
minio=s3a
azure=abfs -
Tokens may be written as
s3ors3://(normalized to bare scheme names). -
Lines starting with
#are comments. -
Mapping only rewrites the scheme; it does not inject credentials or endpoints — still set
spark.hadoop.fs.s3a.(or other FS config) for the *target scheme. -
Prefer putting the target URI in pipeline variables when you can; use the map when the same pipeline must keep Hop-style schemes in metadata.
Cluster environment variables
When you spark-submit with an environment configuration file, remap project variables to Spark URIs, not Hop-only VFS schemes:
{
"variables": [
{
"name": "PROJECT_HOME",
"value": "s3a://hop-project/",
"description": "Project root on object storage (Hadoop S3A URI for executors)"
},
{
"name": "OUTPUT_ROOT",
"value": "s3a://hop-project/output/",
"description": "Shared output prefix visible to all executors"
}
]
} See Getting started with the native Spark engine for the full submit flow.
Practical rules of thumb
-
Spark File / Lake path fields → use Spark URIs (
file:///,hdfs://,s3a://,abfs://, …). -
Classic Hop file transforms (even on the Spark engine) → use Hop VFS URIs and ensure plugins/credentials exist on executors.
-
Prefer Spark File Output for large distributed writes; classic Text File Output is for POCs and needs unique names per partition (
${Internal.Transform.ID}). -
Do not assume
s3://means the same thing in Hop and Spark — in Hop docs for object storage on Spark,s3a://is canonical.