> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/continuous-integration/use-harness-ci/migrate-to-harness-ci/migrating-from-jenkins.md).

# Migrate from Jenkins CI to Harness CI

Learn how to migrate your existing Jenkins pipelines to Harness CI.

Harness CI and Jenkins both allow you to create pipelines that automatically build, test, publish, release, and deploy code.

This guide describes the important differences between Harness CI and Jenkins so that you can migrate your existing Jenkins pipelines to Harness CI.

Go to [What makes Harness CI unique](https://developer.harness.io/continuous-integration/use-harness-ci/migrate-to-harness-ci) for more information.

### Comparison: Pipeline architecture <a href="#comparison-pipeline-architecture" id="comparison-pipeline-architecture"></a>

Both Harness CI and Jenkins use pipelines to organize builds. In both products, pipelines are made up of one or more stages, which represent major segments of the workflow. Each stage includes one or more steps, which in turn contain individual commands.

In Jenkins, pipelines are defined using either Declarative or Scripted Pipeline syntax in a `Jenkinsfile`. In Harness CI, pipelines are defined in YAML and can be stored inline or in Git repositories.

The following truncated examples provide a simple comparison of stage and step structure in Jenkins and Harness CI.

{% tabs %}
{% tab title="Jenkins" %}

```groovy
pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                sh 'echo "Building on default agent"'
            }
        }
    }
}
```

{% endtab %}

{% tab title="Harness" %}

```yaml
    - stage:
        name: Build
        ...
            steps:
              - step:
                  type: Run
                  name: build
                  identifier: build
                  spec:
                    connectorRef: my-docker-hub-connector
                    image: maven:3.8-openjdk-17
                    shell: Bash
                    command: echo "Building on Harness"
```

{% endtab %}
{% endtabs %}

Go to the [CI key concepts](/continuous-integration/new-to-harness-ci/key-concepts.md) for Harness terminology, features, and pipeline components.

#### Stages and steps <a href="#stages-and-steps" id="stages-and-steps"></a>

Stages in Jenkins are similar to stages in Harness CI. In both products, stages contain steps with commands. An important difference is that Jenkins Declarative Pipeline runs stages sequentially by default (unless using `parallel` blocks), similar to Harness CI where stages and steps also run sequentially by default. However, you can enable [parallelism and other looping strategies](#comparison-matrix-and-parallelism) in Harness CI.

{% hint style="info" %}
**ROOT AND NON-ROOT USERS**

Steps run as the root user, generally. For example, with Harness Cloud build infrastructure, steps run directly on the host and, therefore, run as the root user.

For services running on containers (which are steps where you specify a **Container Registry** and **Image** to use to execute the step's commands), you can use the **Run as User** setting to specify a user to use for that container.

With Kubernetes cluster build infrastructure, you can use the **Run as User** setting to specify a user to use for individual steps, or you can [set a default user for all steps](/continuous-integration/use-harness-ci/use-harness-ci/set-up-build-infrastructure/k8s-build-infrastructure/set-up-a-kubernetes-cluster-build-infrastructure.md#run-as-non-root-or-a-specific-user) and then override the default user as needed for individual steps.
{% endhint %}

#### Scripts in pipelines <a href="#scripts-in-pipelines" id="scripts-in-pipelines"></a>

Both Harness CI and Jenkins support running scripts or shell commands in steps. In Jenkins, you use `sh`, `bat`, or `powershell` steps to run scripts.

In Harness CI, scripts are supplied in the `command` key, and can be supplied for any step type that supports `command`. You specify the shell type using the `shell` key.

#### Writing pipelines <a href="#writing-pipelines" id="writing-pipelines"></a>

Jenkins pipelines are written in Groovy-based DSL (either Declarative or Scripted syntax) and are stored in a `Jenkinsfile` in your code repository. Harness CI pipelines are written in YAML, and Harness provides you a choice of inline pipeline storage or [importing pipelines from Git](/harness-platform/use-harness-platform/git-experience/import-a-pipeline.md). Harness also provides both visual and code-based pipeline editors.

* The Harness YAML editor includes schema validation and auto-complete recommendations to simplify and expedite pipeline configuration.
* The Harness visual editor provides a guided experience that enables anyone to easily build, debug, and run pipelines.
* You can switch back and forth between editors.

### Complete pipeline comparison

Here are examples of complete pipelines in Jenkins and Harness CI.

{% tabs %}
{% tab title="Jenkins" %}
{% code overflow="wrap" expandable="true" %}

```yaml
pipeline {
    agent any
    environment {
        POSTGRES_USER = 'postgres'
        POSTGRES_PASSWORD = credentials('postgres-password')
        POSTGRES_DB = 'postgres'
    }
    stages {
        stage('Build') {
            agent {
                docker {
                    image 'openjdk:17.0-jdk'
                }
            }
            steps {
                sh 'echo "Building on openjdk"'
            }
        }
        stage('Test') {
            agent {
                docker {
                    image 'node:13.0.0'
                }
            }
            steps {
                sh '''
                    echo "project level var: ${PROJ_VAR}"
                    echo "pipeline var: ${PIPELINE_VAR}"
                    echo "secret example: ${DB_PASSWORD}"
                '''
            }
        }
        stage('Matrix Build') {
            matrix {
                axes {
                    axis {
                        name 'PLATFORM'
                        values 'windows', 'mac', 'linux'
                    }
                }
                stages {
                    stage('Build Platform') {
                        steps {
                            sh 'echo "Testing on ${PLATFORM}"'
                        }
                    }
                }
            }
        }
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Harness CI" %}
{% code overflow="wrap" expandable="true" %}

```yaml
pipeline:
  name: jenkins migration test
  identifier: jenkins_migration_test
  projectIdentifier: jenkins_test
  orgIdentifier: default
  tags: {}
  properties:
    ci:
      codebase:
        connectorRef: account.myscm
        repoName: test
        build: <+input>
  stages:
    - stage:
        name: Build
        identifier: build
        type: CI
        spec:
          cloneCodebase: true
          execution:
            steps:
              - step:
                  type: Background
                  name: Background_1
                  identifier: Background_1
                  spec:
                    connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
                    image: postgres:10.8
                    envVariables:
                      POSTGRES_USER: postgres
                      POSTGRES_PASSWORD: <+secrets.getValue("DbPasswordSecret")>
                      POSTGRES_DB: postgres
              - step:
                  type: Run
                  name: build
                  identifier: build
                  spec:
                    connectorRef: myDockerHubConnector
                    image: openjdk:17.0-jdk
                    shell: Bash
                    command: echo "Building on openjdk"
          platform:
            os: Linux
            arch: Amd64
          runtime:
            type: Cloud
            spec: {}
    - stage:
        name: Test
        identifier: test
        type: CI
        spec:
          cloneCodebase: true
          execution:
            steps:
              - step:
                  type: Run
                  name: test
                  identifier: test
                  spec:
                    connectorRef: myDockerHubConnector
                    image: node:13.0.0
                    shell: Bash
                    command: |-
                      echo "pipeline var:" <+pipeline.variables.pipelinevar1>
                      echo "project level var:" <+variable.proj_var>
                      echo "secret example:" <+secrets.getValue("DbPasswordSecret")>
          platform:
            os: Linux
            arch: Amd64
          runtime:
            type: Cloud
            spec: {}
        variables: []
    - stage:
        name: matrix stage
        identifier: matrix_stage
        type: CI
        spec:
          cloneCodebase: true
          execution:
            steps:
              - step:
                  type: Run
                  name: platform_test
                  identifier: platform_test
                  spec:
                    shell: Bash
                    command: echo "Testing on <+matrix.platform>"
                  failureStrategies: []
          platform:
            os: Linux
            arch: Amd64
          runtime:
            type: Cloud
            spec: {}
        strategy:
          matrix:
            platform:
              - windows
              - mac
              - linux
            maxConcurrency: 3
  variables:
    - name: pipelinevar1
      type: String
      description: ""
      value: someval
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Comparison: Specify a Docker image <a href="#comparison-specify-a-docker-image" id="comparison-specify-a-docker-image"></a>

In Jenkins, you can specify Docker images using the `agent` directive with a `docker` block, for example:

```groovy
pipeline {
    agent {
        docker {
            image 'openjdk:17.0-jdk'
        }
    }
    stages {
        stage('Build') {
            steps {
                sh 'echo "Running in Docker container"'
            }
        }
    }
}
```

In Harness CI, you specify *connectors* and images for each step, if necessary. Depending on the step type, the commands you are running, and your build infrastructure, you may not need to specify an image. For example, if a binary is already available on your build farm, you may not need to pull an image for that binary.

For example, the following step includes a `connectorRef` referencing a Docker connector. [Docker connectors](broken://spaces/3F2TpHXhur2QtQnORSM9/pages/LZPXMHJjbQyZXdwC5Omo) are platform-agnostic and can be used to connect to any Docker container registry.

```yaml
              - step:
                  type: Run
                  name: build
                  identifier: build
                  spec:
                    connectorRef: my-docker-hub-connector
                    image: openjdk:17.0-jdk
                    shell: Bash
                    command: echo "Running in Docker container"
```

{% hint style="info" %}
**WHAT ARE CONNECTORS?**

Harness integrates with many different types of repositories and providers. A connection from Harness to other platforms is called a [connector](/harness-platform/use-harness-platform/connectors.md). Connectors can connect to source control providers, cloud providers, container registries, and more.
{% endhint %}

### Comparison: Define a multi-stage pipeline <a href="#comparison-define-a-multi-stage-pipeline" id="comparison-define-a-multi-stage-pipeline"></a>

Both Jenkins and Harness CI execute pipeline stages in a specific order based on the configuration.

{% tabs %}
{% tab title="Jenkins" %}

```groovy
pipeline {
    agent any
    stages {
        stage('Test') {
            steps {
                sh 'run tests'
            }
        }
        stage('Build') {
            steps {
                sh 'run build'
            }
        }
        stage('Deploy') {
            steps {
                sh 'deploy application'
            }
        }
    }
}
```

{% endtab %}

{% tab title="Harness" %}

```yaml
  stages:
    - stage:
        name: Test
        ...
    - stage:
        name: Build
        ...
    - stage:
        name: Deploy
        ...
```

{% endtab %}
{% endtabs %}

The following examples compare Jenkins and Harness CI pipelines with multiple stages.

#### YAML examples: Multi-stage pipelines

{% tabs %}
{% tab title="Jenkins" %}
{% code lineNumbers="true" %}

```yaml
pipeline {
    agent any
    
    stages {
        stage('Test') {
            agent {
                docker {
                    image 'golang:latest'
                }
            }
            steps {
                sh 'go fmt $(go list ./... | grep -v /vendor/)'
                sh 'go vet $(go list ./... | grep -v /vendor/)'
                sh 'go test -race $(go list ./... | grep -v /vendor/)'
            }
        }
        
        stage('Build') {
            agent {
                docker {
                    image 'golang:latest'
                }
            }
            steps {
                sh 'mkdir -p mybinaries'
                sh 'go build -o mybinaries ./...'
            }
            post {
                success {
                    archiveArtifacts artifacts: 'mybinaries/**'
                }
            }
        }
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Harness" %}
{% code lineNumbers="true" %}

```yaml
 stages:
   - stage:
       name: test
       identifier: test
       description: ""
       type: CI
       spec:
         cloneCodebase: true
         platform:
           os: Linux
           arch: Amd64
         runtime:
           type: Cloud
           spec: {}
         execution:
           steps:
             - step:
                 type: Run
                 name: test-step
                 identifier: teststep
                 spec:
                   shell: Sh
                   command: |
                     go fmt $(go list ./... | grep -v /vendor/)
                     go vet $(go list ./... | grep -v /vendor/)
                     go test -race $(go list ./... | grep -v /vendor/)
   - stage:
       name: build
       identifier: build
       type: CI
       spec:
         cloneCodebase: true
         platform:
           os: Linux
           arch: Amd64
         runtime:
           type: Cloud
           spec: {}
         execution:
           steps:
             - step:
                 type: Run
                 name: build-step
                 identifier: buildstep
                 spec:
                   shell: Sh
                   command: |
                     mkdir -p mybinaries
                     go build -o mybinaries ./...
         sharedPaths:
           - mybinaries
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Comparison: Environment variables <a href="#comparison-environment-variables" id="comparison-environment-variables"></a>

In Jenkins, you can use environment variables to control the behavior of stages and pipelines, store values you want to reuse, or avoid hard-coding values in your `Jenkinsfile`. You can use predefined environment variables (like `BUILD_NUMBER`, `JOB_NAME`), define custom variables in the `environment` block, or use credentials.

This range of variable definition is also possible in Harness CI. In addition to built-in variables, you can define variables within individual pipelines, stages, and steps as well as at the project, organization, and account levels.

{% tabs %}
{% tab title="Jenkins" %}
This Jenkins example demonstrates use of pre-defined and custom variables.

```groovy
pipeline {
    agent any
    
    environment {
        MY_VAR = 'my-value'
        DB_PASSWORD = credentials('db-password-credential')
    }
    
    stages {
        stage('Test') {
            steps {
                sh 'echo "Build Number: ${BUILD_NUMBER}"'
                sh 'echo "Custom Var: ${MY_VAR}"'
                sh 'echo "Secret: ${DB_PASSWORD}"'
            }
        }
    }
}
```

{% endtab %}

{% tab title="Harness" %}
To reference project, organization, and account variables, you use variable expressions formatted as: `<+variable.[scope].[variable_id]>`. Here are the syntax formats for variables declared at different levels:

* Account-level variable reference: `<+variable.account.[var_id]>`
* Organization-level variable reference: `<+variable.org.[var_id]>`
* Project-level variable reference: `<+variable.[var_id]>`

In this example, a step references a project-level environment variable called `ENV_VAR`.

```yaml
              - step:
                  type: Run
                  name: test-variable
                  identifier: test_variable
                  spec:
                    command: echo "environment variable: " <+variable.env_var>
```

Within a single pipeline, you can define variables at the step, stage, and pipeline levels. For example, this step includes environment variable definitions.

```yaml
              - step:
                  type: Background
                  name: Background_1
                  identifier: Background_1
                  spec:
                    connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
                    image: postgres:10.8
                    envVariables:
                      POSTGRES_USER: postgres
                      POSTGRES_PASSWORD: <+secrets.getValue("DbPasswordSecret")>
                      POSTGRES_DB: postgres
```

Go to the following for defining and fetching variables:

* [Use Harness expressions](/harness-platform/use-harness-platform/variables-and-expressions/harness-variables.md)
* [Define variables](/harness-platform/use-harness-platform/variables-and-expressions/add-a-variable.md)
  {% endtab %}
  {% endtabs %}

### Comparison: Matrix and parallelism <a href="#comparison-matrix-and-parallelism" id="comparison-matrix-and-parallelism"></a>

In Jenkins, you can use the `matrix` directive for matrix builds and `parallel` blocks for parallel execution. In Harness CI, steps and stages run sequentially unless you enable parallelism. For example, this Harness CI YAML example shows a pipeline with two stages that run in parallel, as indicated by `- parallel`, and two stages that run sequentially after the two parallel stages.

```yaml
  stages:
    - parallel:
        - stage:
            name: Stage1
            ...
        - stage:
            name: Stage2
            ...
    - stage:
        name: Stage3
        ...
    - stage:
        name: Stage4
        ...
```

In addition to parallelism, you can use Harness CI's [looping strategies](/harness-platform/use-harness-platform/pipelines/looping-strategies/looping-strategies-matrix-repeat-and-parallelism.md) to make your pipelines more dynamic and versatile. These strategies allow your pipelines to execute the same set of tasks multiple times for several different configurations without requiring you to intentionally create unique steps or stages for each iteration. This is achieved by mentioning user-defined tags and referencing them in the pipeline using the following expression syntax: `<+matrix.usertag>`

As in Jenkins, you also can combine parallelism and matrix strategies in Harness CI, as shown in the following examples.

{% tabs %}
{% tab title="Jenkins" %}

```groovy
pipeline {
    agent any
    stages {
        stage('Matrix Build') {
            matrix {
                axes {
                    axis {
                        name 'PLATFORM'
                        values 'windows', 'mac', 'linux'
                    }
                }
                stages {
                    stage('Test') {
                        steps {
                            sh 'echo "Testing on ${PLATFORM}"'
                        }
                    }
                }
            }
        }
    }
}
```

{% endtab %}

{% tab title="Harness" %}
The following example describes a stage in a Harness CI pipeline that includes one step with matrix and parallelism strategies. The looping strategies are defined in `strategy` at the stage level. One matrix strategy, called `testparam`, is defined in `matrix` and parallelism is defined by `maxConcurrency: 3`. The script in the `Run` step calls the inputs from the matrix strategy by using the expression `<+matrix.testparam>`.

```yaml
  stages:
    - stage:
        name: Stage1
        identifier: Stage1
        type: CI
        spec:
          cloneCodebase: true
          execution:
            steps:
              - step:
                  type: Run
                  name: step1
                  identifier: step1
                  spec:
                    connectorRef: dockerconectorref
                    image: dockerimage:tag
                    shell: Bash
                    command: echo "Testing on  <+matrix.testparam>"
        strategy:
          matrix:
            testparam:
              - node
              - python
              - ubuntu
          maxConcurrency: 3
```

{% endtab %}
{% endtabs %}

### Comparison: Triggers <a href="#comparison-triggers" id="comparison-triggers"></a>

Both Jenkins and Harness CI support various methods to trigger pipeline execution.

In Jenkins, pipelines can be triggered through:

* SCM polling
* Webhooks
* Timer/cron triggers (using `triggers { cron(...) }`)
* Manual triggers
* Upstream/downstream project triggers

Harness CI supports webhook, artifact, manifest and schedule triggers. The two most commonly used triggers are webhook triggers based on Git events and scheduled triggers based on `cron` expressions. Go to [Triggers](/harness-platform/use-harness-platform/triggers.md) for creating a trigger.

{% tabs %}
{% tab title="Jenkins" %}

```groovy
pipeline {
    agent any
    
    triggers {
        cron('45 13 * * 2')
        pollSCM('H/5 * * * *')
    }
    
    stages {
        stage('Build') {
            steps {
                sh 'echo "Building..."'
            }
        }
    }
}
```

{% endtab %}

{% tab title="Harness" %}

```yaml
trigger:
  name: scheduled build
  identifier: scheduled_build
  enabled: true
  orgIdentifier: default
  projectIdentifier: JenkinsCI
  pipelineIdentifier: Demo
  source:
    type: Scheduled
    spec:
      type: Cron
      spec:
        expression: 45 13 * * 2
  inputYaml: |
    pipeline:
      identifier: Demo
      properties:
        ci:
          codebase:
            build:
              type: branch
              spec:
                branch: main
```

{% endtab %}
{% endtabs %}

### Comparison: Plugins and shared libraries <a href="#comparison-plugins-and-shared-libraries" id="comparison-plugins-and-shared-libraries"></a>

Jenkins uses plugins and shared libraries to extend functionality. Harness CI uses plugins to add functionality to pipelines.

In Jenkins, you can:

* Install plugins through the Jenkins Plugin Manager
* Use shared libraries for reusable pipeline code
* Use community-contributed plugins for various integrations

In Harness CI:

* You can [use plugins](/continuous-integration/use-harness-ci/use-harness-ci/use-drone-plugins/explore-ci-plugins.md) to include GitHub Actions, Bitrise Workflow Steps, Jira updates, and more
* Drone plugins are supported natively
* Custom plugins can be created using Docker containers

### Comparison: Agents and build infrastructure <a href="#comparison-agents-and-build-infrastructure" id="comparison-agents-and-build-infrastructure"></a>

In Jenkins, you define where jobs run using `agent` directives, which can specify:

* `any` - Run on any available agent
* `none` - No global agent; each stage must define its own
* `label` - Run on an agent with a specific label
* `docker` - Run inside a Docker container
* `kubernetes` - Run in a Kubernetes pod

In Harness CI, you configure the build infrastructure at the stage level using the `platform` and `runtime` settings. Harness supports several build infrastructure options:

* **Harness Cloud**: Harness-managed infrastructure
* **Kubernetes cluster**: Your own Kubernetes cluster
* **Local runner**: Docker runner on your own machine
* **Self-managed VMs**: AWS, GCP, or Azure VMs

{% tabs %}
{% tab title="Jenkins" %}

```groovy
pipeline {
    agent {
        kubernetes {
            yaml '''
                apiVersion: v1
                kind: Pod
                spec:
                  containers:
                  - name: maven
                    image: maven:3.8.1-jdk-11
                    command:
                    - cat
                    tty: true
            '''
        }
    }
    stages {
        stage('Build') {
            steps {
                container('maven') {
                    sh 'mvn clean install'
                }
            }
        }
    }
}
```

{% endtab %}

{% tab title="Harness" %}

```yaml
    - stage:
        name: Build
        identifier: build
        type: CI
        spec:
          cloneCodebase: true
          platform:
            os: Linux
            arch: Amd64
          runtime:
            type: Cloud
            spec: {}
          execution:
            steps:
              - step:
                  type: Run
                  name: maven-build
                  identifier: maven_build
                  spec:
                    connectorRef: myDockerHubConnector
                    image: maven:3.8.1-jdk-11
                    shell: Bash
                    command: mvn clean install
```

{% endtab %}
{% endtabs %}

### Related articles <a href="#see-also" id="see-also"></a>

* [Harness CI pipeline creation overview](/continuous-integration/use-harness-ci/use-harness-ci/prep-ci-pipeline-components.md)
* [Caching in Harness CI](/continuous-integration/use-harness-ci/use-harness-ci/caching-ci-data.md)
* [Split tests in Run steps](/continuous-integration/use-harness-ci/use-harness-ci/run-tests/speed-up-ci-test-pipelines-using-parallelism.md)
* [Harness Platform documentation](https://app.gitbook.com/s/3F2TpHXhur2QtQnORSM9/README)

{% @harness-feedback/feedback %}
