> 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/3.0/use-harness-ci/use-harness-ci/set-up-build-infrastructure/vm-build-infrastructure/define-a-ci-build-infrastructure-in-google-cloud-platform.md).

# Set up a GCP VM build infrastructure

This topic describes how to set up a CI build infrastructure in Google Cloud Platform.

This topic describes how to set up a CI build infrastructure in Google Cloud Platform (GCP). To do this, you will create an Ubuntu VM and then install a Harness Delegate and Drone VM Runner on it. The runner creates VMs dynamically in response to CI build requests.

This is one of several CI build infrastructure options. For example, you can also [set up a Kubernetes cluster build infrastructure](/continuous-integration/3.0/use-harness-ci/use-harness-ci/set-up-build-infrastructure/k8s-build-infrastructure/set-up-a-kubernetes-cluster-build-infrastructure.md).

The following diagram illustrates a CI build farm. The [Harness Delegate](/harness-platform/3.0/harness-platform-resources/delegates/delegate-closed-beta/delegate-overview.md) communicates directly with your Harness instance. The [VM Runner](https://docs.drone.io/runner/vm/overview/) maintains a pool of VMs for running builds. When the delegate receives a build request, it forwards the request to the runner, which runs the build on an available VM.

### Prepare the Google Cloud VM <a href="#prepare-the-google-cloud-vm" id="prepare-the-google-cloud-vm"></a>

These are the requirements to configure the Google Cloud VM. This is the primary VM where you will host your Harness Delegate and runner.

1. Log into the [Google Cloud Console](https://console.cloud.google.com/) and launch a VM to host your Harness Delegate and runner.
   * Select a machine type with 4 vCPU and 16 GB memory or more. Harness recommends an Ubuntu 20.04 LTS machine image, such as [Focal](https://console.cloud.google.com/marketplace/product/ubuntu-os-cloud/ubuntu-focal) or [Jammy](https://console.cloud.google.com/marketplace/product/ubuntu-os-cloud/ubuntu-jammy).
   * To find images to use on Google Compute Engine, run `gcloud compute images list`. Valid image references follow the format of `projects/PROJECT/global/images/IMAGE`. For example: `projects/ubuntu-os-cloud/global/images/ubuntu-2204-jammy-v20250701`.
2. Configure the VM to allow ingress on ports 22 and 9079.
3. [SSH into the VM](https://cloud.google.com/compute/docs/connect/standard-ssh), if you haven't done so already.
4. [Install Docker](https://docs.docker.com/engine/install/ubuntu/).
5. [Install Docker Compose](https://docs.docker.com/compose/install/).
6. Run `gcloud auth application-default login` to create an `application_default_credentials.json` file at `/home/$(whoami)/.config/gcloud`.

### Configure the Drone pool on the Google Cloud VM <a href="#configure-the-drone-pool-on-the-google-cloud-vm" id="configure-the-drone-pool-on-the-google-cloud-vm"></a>

The `pool.yml` file defines the VM spec and pool size for the VM instances used to run the pipeline. A pool is a group of instantiated VMs that are immediately available to run CI pipelines. You can configure multiple pools in `pool.yml`, such as a Windows VM pool and a Linux VM pool.

1. [SSH into your Google Cloud VM](https://cloud.google.com/compute/docs/connect/standard-ssh).
2. Create a `/runner` folder on your Google Cloud VM and `cd` into it:

   ```
   mkdir /runner
   cd /runner
   ```
3. Copy your `application_default_credentials.json` file into the `/runner` folder. You created this file when you [prepared the Google Cloud VM](#prepare-the-google-cloud-vm).
4. In the `/runner` folder, create a `pool.yml` file.
5. Modify `pool.yml` as described in the following example and the [Pool settings reference](#pool-settings-reference).

#### Example pool.yml <a href="#example-poolyml" id="example-poolyml"></a>

```yaml
version: "1"
instances:
  - name: ubuntu-gcp
    default: true
    type: google
    pool: 1
    limit: 1
    platform:
      os: linux
      arch: amd64
    spec:
      account:
        project_id: ci-play ## Your Google project ID.
        json_path: /path/to/key.json ## Path to the application_default_credentials.json file.
      image: projects/ubuntu-os-cloud/global/images/ubuntu-2204-jammy-v20250701
      machine_type: e2-small
      zone: ## To minimize latency between delegate and build VMs, specify the same zone where your delegate VM is running.
        - us-central1-a
        - us-central1-b
        - us-central1-c
      disk:
        size: 100
        type: "pd-balanced"
      private_ip: true ## Ensures the instance is assigned only a private IP and prevents exposure to the public internet.
```

{% hint style="info" %}
With `private_ip: true`, the runner does not create an external IP.
{% endhint %}

#### Pool settings reference <a href="#pool-settings-reference" id="pool-settings-reference"></a>

You can configure the following settings in your `pool.yml` file. You can also learn more in the Drone documentation for the [Pool File](https://docs.drone.io/runner/vm/configuration/pool/) and [Google drivers](https://docs.drone.io/runner/vm/drivers/google/).

**user data example**

Provide [cloud-init data](https://docs.drone.io/runner/vm/configuration/cloud-init/) in either `user_data_path` or `user_data` if you need custom configuration. Refer to the [user data examples for supported runtime environments](https://github.com/drone-runners/drone-runner-aws/tree/master/app/cloudinit/user_data).

Below is a sample `pool.yml` for GCP with `user_data` configuration:

```yaml
version: "1"
instances:
  - name: linux-amd64
    type: google
    pool: 1
    limit: 10
    platform:
      os: linux
      arch: amd64
    spec:
      account:
        project_id: YOUR_PROJECT_ID
        json_path: PATH_TO_SERVICE_ACCOUNT_JSON
      image: IMAGE_NAME_OR_PATH
      machine_type: e2-medium
      zones:
        - YOUR_GCP_ZONE # e.g., us-central1-a
      disk:
        size: 100
      user_data: |
        #cloud-config
        {{ if and (.IsHosted) (eq .Platform.Arch "amd64") }}
        packages: []
        {{ else }}
        apt:
          sources:
            docker.list:
              source: deb [arch={{ .Platform.Arch }}] https://download.docker.com/linux/ubuntu $RELEASE stable
              keyid: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88
        packages: []
        {{ end }}
        write_files:
          - path: {{ .CaCertPath }}
            path: {{ .CertPath }}
            permissions: '0600'
            encoding: b64
            content: {{ .TLSCert | base64 }}
          - path: {{ .KeyPath }}
        runcmd:
          - 'set -x'
          - |
            if .ShouldUseGoogleDNS; then
              echo "DNS=8.8.8.8 8.8.4.4\nFallbackDNS=1.1.1.1 1.0.0.1\nDomains=~." | sudo tee -a /etc/systemd/resolved.conf
              systemctl restart systemd-resolved
            fi
          - ufw allow 9079
```

| Setting                                                                                                                                                                                                              | Type                                                                                          | Example                                     | Description                                                                                                                                                           |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`                                                                                                                                                                                                               | String                                                                                        | `name: windows_pool`                        | Unique identifier of the pool. You will need to specify this pool name in Harness when you [set up the CI stage build infrastructure](#specify-build-infrastructure). |
| `pool`                                                                                                                                                                                                               | Integer                                                                                       | `pool: 1`                                   | Warm pool size number. Denotes the number of VMs in ready state to be used by the runner.                                                                             |
| `limit`                                                                                                                                                                                                              | Integer                                                                                       | `limit: 3`                                  | Maximum number of VMs the runner can create at any time. `pool` indicates the number of warm VMs, and the runner can create more VMs on demand up to the `limit`.     |
| For example, assume `pool: 3` and `limit: 10`. If the runner gets a request for 5 VMs, it immediately provisions the 3 warm VMs (from `pool`) and provisions 2 more, which are not warm and take time to initialize. |                                                                                               |                                             |                                                                                                                                                                       |
| `platform`                                                                                                                                                                                                           | Key-value pairs, strings                                                                      | `platform:`                                 |                                                                                                                                                                       |
| `os: linux`                                                                                                                                                                                                          |                                                                                               |                                             |                                                                                                                                                                       |
| `arch: amd64`                                                                                                                                                                                                        | Specify VM platform operating system (`os`) and architecture (`arch`). `variant` is optional. |                                             |                                                                                                                                                                       |
| `spec`                                                                                                                                                                                                               | Key-value pairs, various                                                                      | Go to [Example pool.yml](#example-poolyml). | Configure settings for the build VMs.                                                                                                                                 |

* \`account\`: Provide \`project\_id\` and \`json\_path\`. \`project\_id\` is your GCP project ID, and \`json\_path\` is the full path and filename of your local Google credentials file.
* \`image\`: The image type to use for the build VM.
* \`machine\_type\`: The google machine type. See [About Machine Families](https://cloud.google.com/compute/docs/machine-types) in the Google Cloud docs.
* \`zone\`: To minimize latency, specify the zone where the delegate is running.
* \`disk\`: You can provide the \`size\` (as an integer representing GB) and \`type\` (as a string, such as \`"pd-balanced"\`)

|

### Start the runner <a href="#start-the-runner" id="start-the-runner"></a>

[SSH into your Google Cloud VM](https://cloud.google.com/compute/docs/connect/standard-ssh) and run the following command to start the runner:

```
docker run -v /runner:/runner -p 3000:3000 drone/drone-runner-aws:latest  delegate --pool /runner/pool.yml
```

This command mounts the volume to the Docker container providing access to `pool.yml` and JSON credentials to authenticate with GCP. It also exposes port 3000 and passes arguments to the container.

You might need to modify the command to use sudo and specify the runner directory path, for example:

```
sudo docker run -v ./runner:/runner -p 3000:3000 drone/drone-runner-aws:latest  delegate --pool /runner/pool.yml
```

{% hint style="info" %}
**WHAT DOES THE RUNNER DO?**

When a build starts, the delegate receives a request for VMs on which to run the build. The delegate forwards the request to the runner, which then allocates VMs from the warm pool (specified by `pool` in `pool.yml`) and, if necessary, spins up additional VMs (up to the `limit` specified in `pool.yml`).

The runner includes lite engine, and the lite engine process triggers VM startup through a cloud init script. This script downloads and installs Scoop package manager, Git, the Drone plugin, and lite engine on the build VMs. The plugin and lite engine are downloaded from GitHub releases. Scoop is downloaded from `get.scoop.sh` which redirects to `raw.githubusercontent.com`.

Firewall restrictions can prevent the script from downloading these dependencies. Make sure your images don't have firewall or anti-malware restrictions that are interfering with downloading the dependencies.
{% endhint %}

### Install the delegate <a href="#install-the-delegate" id="install-the-delegate"></a>

Install a Harness Docker Delegate on your Google Cloud VM.

1. In Harness, go to **Account Settings**, select **Account Resources**, and then select **Delegates**.

   You can also create delegates at the project scope. In your Harness project, select **Project Settings**, and then select **Delegates**.
2. Select **New Delegate** or **Install Delegate**.
3. Select **Docker**.
4. Enter a **Delegate Name**.
5. Copy the delegate install command and paste it in a text editor.
6. To the first line, add `--network host`, and, if required, `sudo`. For example:

   ```
   sudo docker run --cpus=1 --memory=2g --network host
   ```
7. [SSH into your Google Cloud VM](https://cloud.google.com/compute/docs/connect/standard-ssh) and run the delegate install command.

{% hint style="info" %}
The delegate install command uses the default authentication token for your Harness account. If you want to use a different token, you can create a token and then specify it in the delegate install command:

1. In Harness, go to **Account Settings**, then **Account Resources**, and then select **Delegates**.
2. Select **Tokens** in the header, and then select **New Token**.
3. Enter a token name and select **Apply** to generate a token.
4. Copy the token and paste it in the value for `DELEGATE_TOKEN`.
   {% endhint %}

For more information about delegates and delegate installation, go to [Delegate installation overview](/harness-platform/3.0/harness-platform-resources/delegates/delegate-closed-beta/install-a-delegate.md).

### Verify connectivity <a href="#verify-connectivity" id="verify-connectivity"></a>

1. Verify that the delegate and runner containers are running correctly. You might need to wait a few minutes for both processes to start. You can run the following commands to check the process status:

   ```
   $ docker ps
   $ docker logs DELEGATE_CONTAINER_ID
   $ docker logs RUNNER_CONTAINER_ID
   ```
2. In the Harness UI, verify that the delegate appears in the delegates list. It might take two or three minutes for the Delegates list to update. Make sure the **Connectivity Status** is **Connected**. If the **Connectivity Status** is **Not Connected**, make sure the Docker host can connect to `https://app.harness.io`.

The delegate and runner are now installed, registered, and connected.

### Specify build infrastructure <a href="#specify-build-infrastructure" id="specify-build-infrastructure"></a>

Configure your pipeline's **Build** (`CI`) stage to use your GCP VMs as build infrastructure.

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

1. In Harness, go to the CI pipeline that you want to use the GCP VM build infrastructure.
2. Select the **Build** stage, and then select the **Infrastructure** tab.
3. Select **VMs**.
4. Enter the **Pool Name** from your [pool.yml](#configure-the-drone-pool-on-the-google-vm).
5. Save the pipeline.
   {% endtab %}

{% tab title="YAML" %}

```yaml
    - stage:
        name: build
        identifier: build
        description: ""
        type: CI
        spec:
          cloneCodebase: true
          infrastructure:
            type: VM
            spec:
              type: Pool
              spec:
                poolName: POOL_NAME_FROM_POOL_YML
                os: Linux
          execution:
            steps:
            ...
```

{% endtab %}
{% endtabs %}

#### Delegate selectors with self-managed VM build infrastructures <a href="#delegate-selectors-with-self-managed-vm-build-infrastructures" id="delegate-selectors-with-self-managed-vm-build-infrastructures"></a>

{% hint style="info" %}
Currently, delegate selectors for self-managed VM build infrastructures is behind the feature flag `CI_ENABLE_VM_DELEGATE_SELECTOR`. Contact [Harness Support](mailto:support@harness.io) to enable the feature.
{% endhint %}

Although you must install a delegate to use a self-managed VM build infrastructure, you can choose to use a different delegate for executions and cleanups in individual pipelines or stages. To do this, use [pipeline-level delegate selectors](/harness-platform/use-harness-platform/delegates/delegate/manage-delegates/select-delegates-with-selectors.md#pipeline-delegate-selector) or [stage-level delegate selectors](/harness-platform/use-harness-platform/delegates/delegate/manage-delegates/select-delegates-with-selectors.md#stage-delegate-selector).

Delegate selections take precedence in the following order:

1. Stage
2. Pipeline
3. Platform (build machine delegate)

This means that if delegate selectors are present at the pipeline and stage levels, then these selections override the platform delegate, which is the delegate that you installed on your primary VM with the runner. If a stage has a stage-level delegate selector, then it uses that delegate. Stages that don't have stage-level delegate selectors use the pipeline-level selector, if present, or the platform delegate.

For example, assume you have a pipeline with three stages called `alpha`, `beta`, and `gamma`. If you specify a stage-level delegate selector on `alpha` and you don't specify a pipeline-level delegate selector, then `alpha` uses the stage-level delegate, and the other stages (`beta` and `gamma`) use the platform delegate.

<details>

<summary>Early access feature: Use delegate selectors for codebase tasks</summary>

{% hint style="info" %}
Currently, delegate selectors for CI codebase tasks is behind the feature flag `CI_CODEBASE_SELECTOR`. Contact [Harness Support](mailto:support@harness.io) to enable the feature.
{% endhint %}

By default, delegate selectors aren't applied to delegate-related CI codebase tasks.

With this feature flag enabled, Harness uses your [delegate selectors](/harness-platform/use-harness-platform/delegates/delegate/manage-delegates/select-delegates-with-selectors.md) for delegate-related codebase tasks. Delegate selection for these tasks takes precedence in order of [pipeline selectors](/harness-platform/use-harness-platform/delegates/delegate/manage-delegates/select-delegates-with-selectors.md#pipeline-delegate-selector) over [connector selectors](/harness-platform/use-harness-platform/delegates/delegate/manage-delegates/select-delegates-with-selectors.md#infrastructure-connector).

</details>

### Troubleshoot self-managed VM build infrastructure <a href="#troubleshoot-self-managed-vm-build-infrastructure" id="troubleshoot-self-managed-vm-build-infrastructure"></a>

* [Optimize Windows VM runner](/continuous-integration/3.0/troubleshooting-and-resources/troubleshoot-harness-ci/optimize-windows-vm-runner.md)
* [Can I use the same build VM for multiple CI stages?](/continuous-integration/3.0/troubleshooting-and-resources/ci-articles-and-faqs/continuous-integration-faqs.md#can-i-use-the-same-build-vm-for-multiple-ci-stages)
* [Why are build VMs running when there are no active builds?](/continuous-integration/3.0/troubleshooting-and-resources/ci-articles-and-faqs/continuous-integration-faqs.md#why-are-build-vms-running-when-there-are-no-active-builds)
* [How do I specify the disk size for a Windows instance in pool.yml?](/continuous-integration/3.0/troubleshooting-and-resources/ci-articles-and-faqs/continuous-integration-faqs.md#how-do-i-specify-the-disk-size-for-a-windows-instance-in-poolyml)
* [Clone codebase fails due to missing plugin](/continuous-integration/3.0/troubleshooting-and-resources/ci-articles-and-faqs/continuous-integration-faqs.md#clone-codebase-fails-due-to-missing-plugin)
* [Can I limit memory and CPU for Run Tests steps running on self-managed VM build infrastructure?](/continuous-integration/3.0/troubleshooting-and-resources/ci-articles-and-faqs/continuous-integration-faqs.md#can-i-limit-memory-and-cpu-for-run-tests-steps-running-on-harness-cloud)

Go to the [CI Knowledge Base](/continuous-integration/3.0/troubleshooting-and-resources/ci-articles-and-faqs/continuous-integration-faqs.md) for a broader list of frequently asked questions and answers.

{% @harness-feedback/feedback %}
