--- title: "Pipeline YAML" slug: "dev-dataengine-reference-pipeline-yaml" description: "Learn how to create and manage VAST pipelines with YAML or JSON files, including triggers, function deployments, and secrets for efficient deployment." tags: ["DataEngine"] updated: 2026-10-30T18:18:51Z published: 2026-10-30T18:18:51Z canonical: "docs.vastdata.com/dev-dataengine-reference-pipeline-yaml" --- > ## Documentation Index > Fetch the complete documentation index at: https://docs.vastdata.com/llms.txt > Use this file to discover all available pages before exploring further. # Pipeline YAML *Applies to VAST Cluster 5.5.* A pipeline is defined by one file: the file that `vastde pipelines create --config` and `vastde pipelines update --config` take. It names the pipeline, says which compute cluster and namespace it runs in, and holds the manifest: the triggers, the function deployments, and the links that connect them. Keep this file in Git. It's the source of truth for the pipeline. This page lists every field in the file, what the server fills in, what you can't change after creation, what a deploy needs, and how updates and revisions behave. For a walkthrough, see [pipelines as code](/develop/docs/dev-dataengine-guides-pipelines-as-code). For the trigger and function files, see [function and trigger files](/develop/docs/dev-dataengine-reference-function-and-trigger-files). ## The file at a glance ``` name: media-pipeline # required description: Thumbnails and index entries for new images tags: [media, dev] kubernetes_cluster_vrn: vast:dataengine:kubernetes-clusters:compute-1 # required namespace: dataengine-ns- # required: your tenant's DataEngine namespace manifest: config: # applies to every function deployment environment_variables: [] secrets: [] triggers: [] # triggers this pipeline listens to function_deployments: [] # functions this pipeline runs, and how links: [] # who invokes whom ``` The file is YAML or JSON. Keys are the API's field names, in snake_case, except the two keys of a conditional trigger label (`triggerLabelName`, `triggerLabelValue`). `--config` takes a file path, a path prefixed with `@`, or inline JSON (a value starting with `{` or `[`). A [complete example](/develop/docs/dev-dataengine-reference-pipeline-yaml#complete-example) is at the end of the page. ## Top-level fields | Field | Type | Required | Notes | | --- | --- | --- | --- | | `name` | string | Yes | The pipeline's name. Lowercase letters, digits and hyphens. You can't change it after creation. | | `description` | string | No | Printable ASCII. On `pipelines update`, leaving it out clears the stored description, so keep it in the file. | | `tags` | list of strings | No | Up to 32 tags. Each tag may contain only lowercase letters, digits and hyphens. | | `kubernetes_cluster_vrn` | VRN | Yes | The compute cluster the pipeline runs on: `vast:dataengine:kubernetes-clusters:<cluster-name>`. `vastde compute-clusters list` shows the clusters linked to your tenant. The CLI doesn't turn a cluster name into a VRN for you. You can't change it after creation. | | `namespace` | string | Yes | The Kubernetes namespace the pipeline's functions run in: your tenant's DataEngine namespace on that cluster, `dataengine-ns-<tenant GUID>`. It's created when the platform team attaches your tenant to the cluster. `vastde compute-clusters get <cluster> -o yaml` lists it under `namespaces`. It must be one of your tenant's namespaces on that cluster. You can't change it after creation. | | `manifest` | object | No | The pipeline's contents. A pipeline can be created with no manifest, but it can't be deployed until the manifest meets the [deploy requirements](/develop/docs/dev-dataengine-reference-pipeline-yaml#what-a-deploy-requires). | The API documents `kubernetes_cluster_vrn` and `namespace` as optional; the server requires both on create. On `pipelines create`, the `--name`, `--description` and `--tags` flags override the values in the file. ## manifest.config Settings for every function deployment in the pipeline. | Field | Type | Notes | | --- | --- | --- | | `environment_variables` | list of `{name, value}` | Both `name` and `value` are non-empty strings. A function deployment's own variable with the same name overrides the pipeline's. | | `secrets` | list of strings | Names of Kubernetes Secrets in the pipeline's namespace. Every function deployment gets them. | Environment variable names that start with `VAST_`, `VASTPIPELINE_` or `K_`, and a few other names the runtime uses (`HOSTNAME`, `PORT`, `PERFORMANCE`), are rejected. `VAST_FUNC_HANDLER` is allowed. Secrets: - A secret is checked for existence only when you deploy, not when you create or update the pipeline. - The same secret name can't be listed both here and in a function deployment's `config.secrets`. The server rejects the manifest. - You create secrets with `vastde pipelines create|update --secret-file <file>` (repeatable). The file holds `name`, `kubernetes_cluster_vrn`, `namespace` and `entries: [{key, value}]`. The CLI creates or replaces the secret before it sends the pipeline, but it doesn't add the secret's name to the manifest: list it in `secrets` yourself. The CLI warns about a name in the manifest that it didn't create. Don't commit a secret file to Git. See [security](/develop/docs/dev-dataengine-operations-security). ## manifest.triggers The triggers this pipeline listens to. A trigger is a separate object that you create first (see [function and trigger files](/develop/docs/dev-dataengine-reference-function-and-trigger-files)); the manifest refers to it. | Field | Type | Required | Notes | | --- | --- | --- | --- | | `name` | string | Yes | A local name, used only inside this manifest to refer to the trigger in `links`. It can be the trigger's own name. | | `vrn` | VRN | Yes | The trigger: `vast:dataengine:triggers:<trigger-name>`. It must exist when you create or update the pipeline. | ``` triggers: - name: uploads vrn: vast:dataengine:triggers:uploads-created ``` ## manifest.function_deployments One entry per function the pipeline runs. A function deployment is a function plus the settings it runs with in this pipeline. The same function can appear twice under different names. | Field | Type | Required | Notes | | --- | --- | --- | --- | | `name` | string | Yes | A local name, at most 40 characters, used in `links`. It's also part of the name of the Kubernetes service that runs the function. | | `function_vrn` | VRN | Yes | The function: `vast:dataengine:functions:<function-name>`. It must exist when you create or update the pipeline. | | `revision` | integer or null | No | The function revision to run. See [Pinning a revision](/develop/docs/dev-dataengine-reference-pipeline-yaml#pinning-a-revision). | | `config` | object | No | Environment, secrets, log level and batching. See [config](/develop/docs/dev-dataengine-reference-pipeline-yaml#functiondeploymentsconfig). | | `resources` | object | No | Replicas, CPU, memory, timeout, workers and autoscaling. See [resources](/develop/docs/dev-dataengine-reference-pipeline-yaml#functiondeploymentsresources). | ### Pinning a revision - **`revision: <n>`** runs revision *n* of the function on every deploy. - **`revision` omitted or `null`** runs the function's default revision. If the function has no default revision set, it runs the function's latest revision. The API documents this for `default_revision_number`. An unpinned deployment is resolved when you deploy, not when you save the pipeline. Every deploy picks up whatever the function's default or latest revision is at that moment. Creating a function revision doesn't change a running pipeline until the next deploy. Pin the revision in Git for anything you promote between environments, so that a deploy runs exactly what the file says. For how revisions are created, see [function and trigger files](/develop/docs/dev-dataengine-reference-function-and-trigger-files#revisions). ### function_deployments[].config | Field | Type | Notes | | --- | --- | --- | | `environment_variables` | list of `{name, value}` | As in `manifest.config`. These override pipeline-level variables with the same name. | | `secrets` | list of strings | Kubernetes Secret names for this deployment only. | | `log_level` | string | `NOTSET`, `DEBUG`, `INFO`, `WARNING`, `ERROR` or `CRITICAL`. The VAST Web UI's default is `INFO`. | | `event_batching` | object | Deliver events to the function in batches. See below. | `event_batching`: | Field | Type | Range | Notes | | --- | --- | --- | --- | | `batch_max_size` | integer | 1–512 | The most events in one batch. | | `batch_timeout_ms` | integer | 0–60000 | How long to wait, in milliseconds, before invoking the function with a partial batch. | - If you set `event_batching`, set both fields. The API requires them. - With `batch_max_size` above 1, `batch_timeout_ms` must be above 0. With `batch_max_size: 1`, it must be 0. Any other combination is rejected. - The function's handler must be written to take a batch. Turning batching on without a batch handler is liable to cause runtime failures. See [write a function](/develop/docs/dev-dataengine-guides-write-a-function). - Set the deployment's `resources.timeout` higher than the batch timeout. Duplicated batch-trace entries are a known issue in 5.5 when the two are combined badly. ### function_deployments[].resources | Field | Type | Default | Notes | | --- | --- | --- | --- | | `min_concurrency` | integer | 1 | The fewest instances (pods) of the function. | | `max_concurrency` | integer | 10 | The most instances (pods). | | `min_cpu` | string | `100m` | CPU guaranteed to each instance, as a Kubernetes quantity. | | `max_cpu` | string | `500m` | CPU limit for each instance. | | `min_memory` | string | `128Mi` | Memory guaranteed to each instance. | | `max_memory` | string | `256Mi` | Memory limit for each instance. | | `storage` | string | Not set | Ephemeral disk for each instance, for example `512Mi`. | | `timeout` | integer | Not stated | The function's timeout, in seconds. | | `num_workers` | integer | 20 | Worker threads in each instance, 1–100. With batching, an instance handles up to `num_workers × batch_max_size` events at once, because each worker takes one batch. | | `max_concurrent_requests_limit` | integer | Computed | The most requests one instance handles at once. If you leave it out, it's computed at deploy time as `max(num_workers, batch_max_size)`. `0` means unlimited, which stops the autoscaler from seeing queue pressure. Set it only if you know your function's capacity. | | `autoscaling` | object | See below | How the deployment scales between `min_concurrency` and `max_concurrency`. | | `autoscaling_rps_factor` | integer | | Deprecated and ignored. Don't use it. | | `autoscaling_concurrency_overload_factor` | string | | Deprecated. Use `autoscaling`. | There's no field for GPUs in 5.5. The server rejects a deployment where: - `autoscaling` is set together with either deprecated field. - `max_concurrent_requests_limit` is set (and not 0) but is lower than the concurrency autoscaling `target`, or lower than `num_workers × batch_max_size` when batching is on. #### resources.autoscaling `metric` is required. Provide the one configuration object that matches it. | Field | Type | Notes | | --- | --- | --- | | `metric` | string | `concurrency` (the default), `rps` or `resource`. | | `concurrency` | object | Scale on in-flight requests per instance. Required when `metric: concurrency`. Supports scale to zero. | | `rps` | object | Scale on requests per second per instance. Required when `metric: rps`. Supports scale to zero. | | `resource` | object | Scale on CPU or memory use. Required when `metric: resource`. Doesn't support scale to zero. | `concurrency` and `rps` take the same fields; `concurrency` adds `hard_limit`. | Field | Type | Default | Range | Notes | | --- | --- | --- | --- | --- | | `target` | integer | 20 | 1 or more | The level the autoscaler aims for per instance: in-flight requests for `concurrency`, requests per second for `rps`. | | `target_utilization_percentage` | integer | 70 | 1–100 | Scale up when the average reaches this percentage of `target`. With `target: 10` and `70`, a new instance starts at an average of 7. | | `stable_window_seconds` | integer | 60 | 6–3600 | The averaging window. With scale to zero, the last instance goes only after no traffic for this long. | | `burst_window_percentage` | integer | 10 | 1–100 | The shorter window, as a percentage of the stable window, used to detect bursts. | | `burst_threshold_percentage` | integer | 200 | 110–1000 | Burst mode starts when traffic exceeds this percentage of current capacity. | | `scale_down_delay_seconds` | integer | 300 | 0 or more | How long to wait after load drops before scaling down. | | `scale_to_zero_delay_seconds` | integer | 600 | 0 or more | How long to keep the last instance after the autoscaler decides to scale to zero. | | `hard_limit` | integer | 0 | 0 or more | `concurrency` only. A hard cap on in-flight requests per instance; the rest wait. 0 is unlimited. The API warns it can hurt throughput and latency. | `resource`: | Field | Type | Required | Notes | | --- | --- | --- | --- | | `resource_autoscaling_type` | string | Yes | `cpu` or `memory`. | | `target` | integer | Yes | For `cpu`, a percentage of the CPU request (80 means 80%). For `memory`, mebibytes (200 means 200Mi). | | `stable_window_seconds` | integer | No | 6–3600. The scale-down stabilization window. If not set, the Kubernetes default applies. | If a deployment has no `autoscaling` (and neither deprecated field), the server adds a `concurrency` block with `target` set to `num_workers × batch_max_size` before it stores the manifest. A deployment with no `resources` at all gets a complete block with the defaults above. You'll see them when you read the manifest back. ## manifest.links Links say who invokes whom. Each link connects one or more sources to one or more destinations. The API's rule: each source in `source` invokes every function deployment in `destination`, if all of the link's conditional trigger labels match. | Field | Type | Required | Notes | | --- | --- | --- | --- | | `source` | list of strings | Yes | Local names from `triggers` or `function_deployments`. | | `destination` | list of strings | Yes | Local names from `function_deployments`. | | `topic` | VRN | No | The topic that carries events along this link. See [Topics on links](/develop/docs/dev-dataengine-reference-pipeline-yaml#topics-on-links). | | `config` | object | No | `retries` and `events_order`. | | `conditional_trigger_labels` | list | No | Route only events that carry these labels. See [Conditional routing](/develop/docs/dev-dataengine-reference-pipeline-yaml#conditional-routing). | - Every name in `source` and `destination` must be defined in `triggers` or `function_deployments`. - You can't connect a trigger to a trigger, and you can't make a loop. ### Topics on links | Link | `topic` | | --- | --- | | Trigger → function | The link uses the trigger's own topic, the one the trigger publishes to. Leave `topic` out, or set it to that same topic; a different topic is rejected with `Topic mismatch between trigger … and link`. | | Function → function | Leave `topic` out to use your tenant's default topic, or set a topic VRN. All links out of the same function must use the same topic. | - A topic VRN is `vast:dataengine:topics:<broker>/<topic>`, where `<broker>` is the broker name a trigger uses (`broker.name`; for a VAST Event Broker, the bucket name of the broker's view). - The dead-letter topic can't be used on a link. - For topics themselves (partitions, retention), see [topics](/develop/docs/dev-event-broker-guides-topics) in the VAST Event Broker section. ### links[].config | Field | Type | Values | Notes | | --- | --- | --- | --- | | `retries` | integer | | How many times to retry delivering an event to the destination when the function fails or times out, after the first attempt: with `retries: 2`, the handler runs up to three times. After the last failed attempt, the event goes to the dead-letter topic. | | `events_order` | string | `ordered`, `unordered` | `ordered` processes one event per topic partition at a time, so concurrency is capped at the topic's partition count. `unordered` lets events be processed concurrently. | The API gives no defaults for these. If you leave `retries` out, a server default applies, and a failing event can be retried for a long time before it reaches the dead-letter topic. Set `retries` on every link. See [retries and the dead-letter topic](/develop/docs/dev-dataengine-concepts-architecture#retries-and-the-dead-letter-topic). The VAST Web UI shows Retries and Method of Delivery on the function deployment. In the file they belong to the link. See [coming from the Web UI](/develop/docs/dev-dataengine-reference-coming-from-the-ui). ### Conditional routing `conditional_trigger_labels` makes a function-to-function link fire only for events that carry all of the listed labels with the listed values. The source function sets the labels on the events it returns; see the [Runtime SDK reference](/develop/docs/dev-dataengine-reference-runtime-sdk). | Field | Type | Pattern | | --- | --- | --- | | `triggerLabelName` | string | `^[a-z][a-z0-9]{0,14}$`: a lowercase letter, then up to 14 lowercase letters or digits. | | `triggerLabelValue` | string | `^[a-zA-Z0-9]{1,64}$`: 1 to 64 letters or digits. | - Labels are ANDed: an event must match every label on the link. - Only links whose sources are function deployments can have labels. A link from a trigger with labels is rejected. - Label names must be unique within a link. - Underscores aren't allowed in names or values. ``` links: - source: [thumbnailer] destination: [indexer] conditional_trigger_labels: - triggerLabelName: kind triggerLabelValue: photo ``` ## What the server fills in You don't write these fields. `vastde pipelines get <name> -o yaml` shows them. | Field | Meaning | | --- | --- | | `id`, `guid` | The pipeline's identifiers. Most `vastde` commands accept the name or the GUID. | | `vrn` | `vast:dataengine:pipelines:<name>` | | `owner`, `tenant_guid` | Who created it, and the tenant. | | `created_at`, `updated_at` | Timestamps. | | `status` | `Draft` after create, `InProgress` during deploy, then `Ready` or `Error`; `Deleting` while being deleted. | | `reason` | Free text explaining `status`, for example the Kubernetes condition that failed. | | `last_revision_number` | The newest revision of the manifest. | In the stored manifest, the server also: - Fills in `resources`, with the defaults and an `autoscaling` block, where they're missing (see above). When you read the manifest back, unpinned function deployments show `revision: null`, and an empty `config` section isn't shown. At deploy time, without changing the stored manifest, it: - Resolves each unpinned `revision` to the function's default or latest revision. - Computes `max_concurrent_requests_limit` where it's not set. ## Fields you can't change | Field | What happens if you change it | | --- | --- | | `name` | `vastde pipelines update` refuses (`cannot change pipeline name from '…' to '…'`), and so does the server (`Cannot change pipeline name`). | | `kubernetes_cluster_vrn` | Rejected. It's also required on every update, and must equal the stored value. | | `namespace` | Rejected (`Cannot change pipeline namespace`). | To move a pipeline to another cluster or namespace, create a new pipeline from the same file with a new name. ## What a deploy requires `vastde pipelines deploy <name|GUID>` takes no file. It deploys the pipeline's latest revision. `vastde pipelines create --config <file> --deploy` creates and deploys in one step. A deploy fails unless: - `manifest.triggers` has at least one trigger. - `manifest.links` has at least one link, and at least one link has a trigger as a source. - Every secret the manifest names exists in the namespace. - Every function's container registry is linked to the pipeline's compute cluster and namespace. Otherwise the deploy fails with `Access to Container registry … is denied for the kubernetes cluster …`. That's a platform-team fix; see [setup prerequisites](/develop/docs/dev-dataengine-operations-setup-prerequisites). Create and update check the rest when you submit the file: that the cluster VRN is there and the namespace belongs to that cluster, that the triggers and functions exist, that link names resolve, that no environment variable has a reserved name, that no secret is named at both levels, and the batching, autoscaling, topic and label rules on this page. A failed deploy check doesn't undo the create: `vastde pipelines create --config <file> --deploy` that fails at the deploy step leaves the pipeline in `Draft`. Fix the file, then run `vastde pipelines update` and `vastde pipelines deploy`, or delete the pipeline. The messages for each check are in the [error reference](/develop/docs/dev-dataengine-troubleshooting-errors#pipelines). ## Updates and revisions `vastde pipelines update <name|GUID> --config <file>` replaces the pipeline's definition with the file. Each manifest is stored as a revision. **Send whole sections.** The four manifest sections (`config`, `triggers`, `function_deployments`, `links`) are each replaced as a whole when they're in the file. A section you leave out is kept from the latest revision; inside `config`, the same applies to `secrets` and `environment_variables` separately. **The top-level `description` isn't kept.** Unlike a manifest section, a `description` you leave out of the file is cleared on update, not kept. - To remove everything in a section, send an empty list (`links: []`). Leaving it out doesn't remove it. - To remove one link or one function deployment, send the section without it. - Always include `manifest`, and keep the file complete. A complete file in Git gives the same result whatever the cluster holds. **Revisions.** | Latest revision | What `update` does | | --- | --- | | Deployed | Adds a new revision with the next number. The running pipeline doesn't change. | | Not deployed | Overwrites the latest revision in place. No new number. | - `pipelines create` makes revision 1. - Updating doesn't redeploy. Run `vastde pipelines deploy` to put the new revision live. - An update adds a revision even when the file is identical to the latest one, if that one is deployed. - Deploy always takes the latest revision. There's no command to deploy an older revision. To roll back, check out the earlier file from Git, run `update`, and deploy. - `vastde pipelines get <name> --manifest` shows the latest revision's manifest; add `--deployed` to see the deployed one. **No version check.** Updates carry no version or ETag, so the last writer wins, section by section. An edit saved in the VAST Web UI and an update from an old copy of the file overwrite each other without a warning. Pick one place to edit each pipeline. For pipelines you manage from Git, treat the Web UI as read-only. See [coming from the Web UI](/develop/docs/dev-dataengine-reference-coming-from-the-ui#one-source-of-truth). ## Complete example One element trigger, `uploads-created`, watches the `media-uploads` bucket. Its events go to `thumbnailer`, which passes events on to `indexer`. `indexer` takes events in batches. The trigger and the two functions must exist before you create the pipeline; their files are in [function and trigger files](/develop/docs/dev-dataengine-reference-function-and-trigger-files). ![The complete example on this page drawn as a graph. The top-level fields name the pipeline, media-pipeline, on compute cluster compute-1 in the tenant's namespace dataengine-ns-tenant-guid. manifest.config gives every function deployment the OUTPUT_BUCKET environment variable and the media-s3-keys secret. manifest.triggers holds uploads, the element trigger uploads-created on the media-uploads bucket. manifest.function_deployments holds thumbnailer, revision 3, 1 to 8 instances, scaling on concurrency, and indexer, revision 1, 1 to 4 instances, in batches of up to 50 events. manifest.links holds two links: uploads to thumbnailer on the trigger's topic, and thumbnailer to indexer on the default topic, both with 3 retries, unordered.](https://cdn.us.document360.io/89b91113-5dc6-49db-86ec-57b16dc8ce56/Images/Documentation/de-pipeline-example.svg?sv=2026-02-06&spr=https&st=2026-11-08T08%3A55%3A13Z&se=2026-11-08T09%3A55%3A13Z&sr=c&sp=r&sig=0LtpWf96Dwf%2BNXSV9jjTHxEMKkKRIl1Kxk53m4jZKCA%3D) *The example file as the graph it describes, with the section of the file that defines each part.* ``` # media-pipeline.yaml name: media-pipeline description: Thumbnails and index entries for new images tags: [media, dev] kubernetes_cluster_vrn: vast:dataengine:kubernetes-clusters:compute-1 namespace: dataengine-ns- # your tenant's DataEngine namespace on compute-1 manifest: config: # every function deployment gets these environment_variables: - name: OUTPUT_BUCKET value: media-derived secrets: - media-s3-keys # Kubernetes Secret in the namespace; create it with --secret-file triggers: - name: uploads # local name, used in links below vrn: vast:dataengine:triggers:uploads-created function_deployments: - name: thumbnailer # local name, at most 40 characters function_vrn: vast:dataengine:functions:thumbnailer revision: 3 # pinned; omit to follow the default or latest revision config: log_level: INFO environment_variables: - name: THUMBNAIL_WIDTH value: "320" # values are strings resources: min_concurrency: 1 max_concurrency: 8 min_cpu: 250m max_cpu: "1" min_memory: 256Mi max_memory: 512Mi timeout: 120 # seconds autoscaling: metric: concurrency # scale on in-flight requests per instance concurrency: target: 20 target_utilization_percentage: 70 - name: indexer function_vrn: vast:dataengine:functions:indexer revision: 1 config: log_level: INFO event_batching: # the handler must accept a batch batch_max_size: 50 batch_timeout_ms: 2000 # must be > 0 when batch_max_size > 1 resources: min_concurrency: 1 max_concurrency: 4 num_workers: 4 # up to 4 x 50 events in flight per instance timeout: 60 # longer than the batch timeout # no autoscaling block: the server adds a concurrency default links: - source: [uploads] # trigger -> function: uses the trigger's topic destination: [thumbnailer] config: retries: 3 events_order: unordered - source: [thumbnailer] # function -> function: no topic, so the tenant default topic destination: [indexer] config: retries: 3 events_order: unordered ``` Create it and deploy it: ``` vastde pipelines create --config media-pipeline.yaml --secret-file media-s3-keys.yaml --deploy ``` After you edit the file: ``` vastde pipelines update media-pipeline --config media-pipeline.yaml vastde pipelines deploy media-pipeline ``` ## Exporting an existing pipeline Use this to bring a pipeline that was built in the VAST Web UI, or by someone else, under Git. **From the CLI.** `vastde pipelines get <name> --manifest -o yaml` prints the latest revision's manifest, and only the manifest: no `name`, `kubernetes_cluster_vrn` or `namespace`. It isn't a file `pipelines update` accepts as it stands. Wrap it: 1. Run `vastde pipelines get <name> -o yaml` and copy `name`, `kubernetes_cluster_vrn` and `namespace` (and `description` and `tags` if you use them). Leave out the read-only fields (`id`, `guid`, `vrn`, `status`, `reason`, `last_revision_number`, timestamps, owner). 2. Put the output of `vastde pipelines get <name> --manifest -o yaml` under a top-level `manifest:` key, indented one level. ``` vastde pipelines get media-pipeline -o yaml vastde pipelines get media-pipeline --manifest -o yaml ``` Add `--deployed` to the second command to export the deployed revision rather than the latest one. Without `-o yaml`, `--manifest` prints JSON. **From the Web UI.** The Visual Builder's YAML view has a Download button that saves `pipeline-manifest.yaml`, with `name`, `description`, `tags`, `namespace`, `kubernetes_cluster_vrn` and `manifest`: the full file. Either way, the exported manifest isn't exactly the file that created the pipeline. It includes the `resources` the server filled in, with their autoscaling blocks, and `revision: null` on unpinned deployments, and the CLI export leaves out an empty `config`. You can keep the filled-in values or delete them; the server adds them again where they're missing. Delete `revision: null` or replace it with a revision number to [pin](/develop/docs/dev-dataengine-reference-pipeline-yaml#pinning-a-revision) the deployment. The wrapped CLI export is accepted by `pipelines update` as it is: re-applying it unchanged left the exported manifest identical. Add `description` (and `tags`) to the wrapper if the pipeline has them, or the update clears them. Check the export into Git, run `vastde pipelines update <name> --config <file>` once to confirm it's accepted, and from then on change the pipeline only through the file. ## See also - [Pipelines as code](/develop/docs/dev-dataengine-guides-pipelines-as-code): the workflow end to end. - [Function and trigger files](/develop/docs/dev-dataengine-reference-function-and-trigger-files) - [Coming from the Web UI](/develop/docs/dev-dataengine-reference-coming-from-the-ui) - CLI reference - [Runtime SDK reference](/develop/docs/dev-dataengine-reference-runtime-sdk): batch handlers and trigger labels. - [Building and Deploying a Pipeline on VAST DataEngine](/docs-5-5/docs/building-and-deploying-a-pipeline-on-vast-dataengine) on the KB - [Create a new Pipeline](https://kb.vastdata.com/api-5-5/apidocs/create-a-new-pipeline) in the VAST DataEngine API reference