---
url: /en/build/script-vs-plugin.md
description: >
  Compare the core differences between script tasks and plugin tasks in CNB
  pipelines to help developers choose the right task type for their scenarios.
---
## Overview

In CNB pipelines, a `Job` is the smallest execution unit, mainly divided into
**script tasks**, **plugin tasks**, and **built-in tasks**.

* **Script tasks**: Use an image as the execution environment
  and run user-specified Shell commands
* **Plugin tasks**: Run the `ENTRYPOINT` built into the plugin image,
  configured via parameters

Both support specifying `image`, which can be confusing for beginners.
This document explains the differences.

## Core Comparison

| Feature              | Script Task                            | Plugin Task                               |
|----------------------|----------------------------------------|-------------------------------------------|
| **Execution**        | Runs `script` / `commands`             | Image's built-in `ENTRYPOINT`             |
| **Environment**      | Pipeline or independent container      | Independent container only                |
| **Parameters**       | Via `env` / `imports`                  | Via `settings` (→ `PLUGIN_*`)             |
| **Custom env vars**  | ✅ Supported                            | ❌ Not passed                              |
| **Flexibility**      | High, any command                      | Preset by plugin developer                |
| **Reusability**      | Business-specific                      | Shareable via plugin marketplace          |

**Environment details**:

* **Script task**: Runs in the pipeline container without `image`;
  runs in an independent container with `image`.
* **Plugin task**: Must specify `image`, always runs in an independent container.

## Execution Environment & File Sharing {#file-sharing}

* Script tasks without `image` run in the **pipeline container**
* Script tasks with `image` and plugin tasks run in
  **independent containers**

Only the following directories are shared between tasks;
other directories are not visible to each other:

* `CNB_BUILD_WORKSPACE` (default `/workspace`)
* Directories declared in
  [`docker.volumes`](./grammar.md#pipeline-docker-volumes)

::: tip
If a file produced by a previous task cannot be found in the next task,
make sure the file was written to `CNB_BUILD_WORKSPACE`
or a declared shared volume.
:::

## Examples

### Script Task

```yaml
- name: install dependencies
  image: node:20
  script: npm install
  env:
    NODE_ENV: production
```

### Plugin Task

```yaml
- name: npm publish
  image: plugins/npm:1.2.3
  settings:
    username: $NPM_USER
    password: $NPM_PASS
    registry: https://registry.npmjs.org/
```

The `settings` above are injected as environment variables:
`PLUGIN_USERNAME`, `PLUGIN_PASSWORD`, `PLUGIN_REGISTRY`.

## Environment Variable Rules

**Script tasks**:

* ✅ Custom env vars declared via `env`
* ✅ Env vars imported via `imports`
* ✅ CNB built-in system env vars
* Priority: `Job env` > `Stage env` > `Pipeline env`

**Plugin tasks**:

* ✅ CNB built-in system env vars
* ✅ Reference env vars via `$VAR` in `settings` / `args`
* ❌ Custom env vars from `env` / `imports` (not passed)

```yaml
# ✅ Reference env vars in settings
- name: npm publish
  image: plugins/npm
  settings:
    username: $NPM_USER

# ❌ env is not passed to plugins
- name: npm publish
  image: plugins/npm
  env:
    PLUGIN_USERNAME: $NPM_USER
```

::: details Why can't plugin tasks receive env vars via env?
Plugin tasks are designed as modular components that explicitly define
accepted parameters via `settings`. This design:

* Keeps plugin interfaces clear and avoids parameter confusion
* Lets plugins explicitly declare required parameters
* Prevents env var naming conflicts, improving maintainability
* Avoids security issues caused by environment variable injection
  :::

## When to Use Which

| Scenario                              | Recommended   | Reason                                            |
|---------------------------------------|---------------|---------------------------------------------------|
| Code building and testing             | Script task   | Flexible, adapts to various build tools           |
| Custom business logic                 | Script task   | Full control over execution flow                  |
| Notifications (WeCom, Lark, etc.)     | Plugin task   | Cohesive, simple configuration                    |
| Common CI/CD operations               | Plugin task   | Mature solutions in plugin marketplace            |
| Reusable generic functionality        | Plugin task   | Shareable via plugin marketplace                  |

::: tip Quick Decision Guide

1. Existing plugin meets your needs? → Plugin task
2. Need to run custom commands flexibly? → Script task
3. Functionality is reusable and shareable? → Plugin task
4. Strongly tied to business logic? → Script task
   :::

## Best Practices

### Version Control

Whenever `image` is used, it's recommended to specify the image version.
Using `latest` means image updates may cause unexpected behavior changes.

```yaml
- name: npm publish
  image: plugins/npm:1.2.3
  settings:
    username: $NPM_USER

- name: build
  image: node:20
  script: npm run build
```

### Security

* Use official or trusted images
* Store sensitive data in the secrets vault, never hardcode
* Regularly check images for security updates

### Reusability

**Plugin tasks** are suitable for reusable functionality:

* Notifications (WeCom, Lark, DingTalk, etc.)
* Common CI/CD operations (publish, deploy, etc.)
* Code quality checks (lint, scanning, etc.)

These can be published to the CNB plugin marketplace for others to use.

**Script tasks** are suitable for business-related logic:

* Code building and testing
* Custom data processing
* Specific business workflows

## FAQ

### Where do script tasks run without specifying image?

In the pipeline container environment.
The pipeline container is specified by `Pipeline.docker.image`
or `Stage.image`. See
[Build Environment - Environment Scope](./build-env.md#environment-scope)
for details.

### How to use environment variables in plugin tasks?

Use variable substitution in `settings` or `args`:

```yaml
- name: npm publish
  image: plugins/npm
  settings:
    username: $NPM_USER
    password: $NPM_PASS
```

### Can plugin tasks execute custom scripts?

Plugin tasks can pass script content to the plugin via `settings`,
but only if the plugin itself supports receiving and executing scripts.
The execution logic is controlled by the plugin image, not by CNB.

Script tasks are different — `script` or `commands` are natively
supported by CNB syntax, and CNB directly executes them in the
script task's specified `image` or the pipeline container environment.
