This guide walks you through deploying to Google Cloud Platform using DeployHQ's [Custom Action](Article: custom-action) protocol. Custom Actions run CLI tools inside Docker containers during your deployment pipeline, letting you use the `gcloud` CLI to deploy to App Engine, Cloud Run, GKE, and other GCP services.

## Prerequisites

Before you begin, ensure you have:

* **Beta Features Enabled:** Custom Actions require beta features to be enabled in your [account settings](Article: #682)
* **A GCP Project:** An active Google Cloud project with billing enabled
* **Service Account Credentials:** A service account JSON key file with permissions for your target services
* **Application Code in Repository:** Your application source code with any required configuration files (e.g., `app.yaml` for App Engine, `Dockerfile` for Cloud Run)

## Setting Up the Server

1. Open your project in DeployHQ and navigate to **Servers**.
2. Click **New Server** and select **Custom Action** as the protocol.
3. Under **Image Source**, select **Curated Template**.
4. Choose **Google Cloud SDK** from the template dropdown. This sets the Docker image to `google/cloud-sdk:slim`.
5. Enter your deployment commands in the **Commands** field:

   ```text
   echo "$GCP_SERVICE_ACCOUNT_KEY" > /tmp/gcp-key.json
   gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
   gcloud config set project my-gcp-project
   gcloud app deploy /data/app.yaml --quiet
   ```

6. Set **Halt on error** to stop the deployment if any command fails.
7. Click **Create Server**.

## Configuring Credentials

Google Cloud authentication uses a service account key. Create a service account in the GCP Console with the necessary roles, then download the JSON key file.

Add the following [environment variables](Article: #163) to your DeployHQ server:

| Variable | Value |
|----------|-------|
| `GCP_SERVICE_ACCOUNT_KEY` | The full contents of your service account JSON key file |
| `GCP_PROJECT_ID` | Your Google Cloud project ID |

Your commands should authenticate before running any `gcloud` operations:

```text
echo "$GCP_SERVICE_ACCOUNT_KEY" > /tmp/gcp-key.json
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
gcloud config set project $GCP_PROJECT_ID
```

### Required IAM Roles

The roles needed depend on the services you are deploying to:

| Service | Required Roles |
|---------|---------------|
| App Engine | `roles/appengine.deployer`, `roles/appengine.serviceAdmin` |
| Cloud Run | `roles/run.developer`, `roles/iam.serviceAccountUser` |
| GKE | `roles/container.developer` |
| Cloud Functions | `roles/cloudfunctions.developer` |
| Cloud Storage | `roles/storage.admin` |

## Example Commands

### Deploy to App Engine

Deploy a standard or flexible App Engine application:

```text
echo "$GCP_SERVICE_ACCOUNT_KEY" > /tmp/gcp-key.json
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
gcloud config set project $GCP_PROJECT_ID
gcloud app deploy /data/app.yaml --quiet
```

The `--quiet` flag suppresses interactive prompts, which is required for non-interactive environments.

### Deploy to Cloud Run

Deploy a container to Cloud Run from source:

```text
echo "$GCP_SERVICE_ACCOUNT_KEY" > /tmp/gcp-key.json
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
gcloud config set project $GCP_PROJECT_ID
gcloud run deploy my-service \
  --source /data \
  --region us-central1 \
  --platform managed \
  --allow-unauthenticated \
  --quiet
```

Or deploy a pre-built container image:

```text
echo "$GCP_SERVICE_ACCOUNT_KEY" > /tmp/gcp-key.json
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
gcloud config set project $GCP_PROJECT_ID
gcloud run deploy my-service \
  --image gcr.io/$GCP_PROJECT_ID/my-service:%revision% \
  --region us-central1 \
  --platform managed \
  --quiet
```

### Deploy to GKE

Connect to a GKE cluster and apply Kubernetes manifests:

```text
echo "$GCP_SERVICE_ACCOUNT_KEY" > /tmp/gcp-key.json
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
gcloud config set project $GCP_PROJECT_ID
gcloud container clusters get-credentials my-cluster --zone us-central1-a
kubectl apply -f /data/k8s/
kubectl rollout status deployment/my-app -n production --timeout=300s
```

Note that the `google/cloud-sdk:slim` image includes `kubectl`, so no additional image is needed for GKE deployments.

### Deploy Cloud Functions

Deploy a Cloud Function from your repository:

```text
echo "$GCP_SERVICE_ACCOUNT_KEY" > /tmp/gcp-key.json
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
gcloud config set project $GCP_PROJECT_ID
gcloud functions deploy my-function \
  --source /data \
  --runtime nodejs20 \
  --trigger-http \
  --entry-point handler \
  --region us-central1 \
  --quiet
```

### Sync to Cloud Storage

Upload static files to a Cloud Storage bucket:

```text
echo "$GCP_SERVICE_ACCOUNT_KEY" > /tmp/gcp-key.json
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
gcloud config set project $GCP_PROJECT_ID
gsutil -m rsync -r -d /data/public/ gs://my-bucket/
```

## Multi-Environment Usage

### Separate Projects per Environment

Use different GCP projects for staging and production by setting different `GCP_PROJECT_ID` values on each DeployHQ server:

**Staging server:**

| Variable | Value |
|----------|-------|
| `GCP_PROJECT_ID` | `my-app-staging` |
| `GCP_SERVICE_ACCOUNT_KEY` | Staging service account key |

**Production server:**

| Variable | Value |
|----------|-------|
| `GCP_PROJECT_ID` | `my-app-production` |
| `GCP_SERVICE_ACCOUNT_KEY` | Production service account key |

### Dynamic Configuration

Use DeployHQ [text variables](Article: #165) for environment-specific settings:

```text
echo "$GCP_SERVICE_ACCOUNT_KEY" > /tmp/gcp-key.json
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
gcloud config set project $GCP_PROJECT_ID
gcloud run deploy my-service-%server% \
  --source /data \
  --region us-central1 \
  --platform managed \
  --quiet
```

## Troubleshooting

**"ERROR: (gcloud.auth.activate-service-account) Could not read json file":**

* Ensure the `GCP_SERVICE_ACCOUNT_KEY` environment variable contains the full JSON key, not a file path
* Check that the value is not truncated. Service account keys are typically around 2,400 characters.

**"ERROR: (gcloud.app.deploy) Permissions error":**

* Verify the service account has the required IAM roles listed above
* Check that the service account belongs to the correct GCP project

**"Cloud Build has not been used in this project before":**

* Cloud Run source deployments require Cloud Build. Enable it in the GCP Console under APIs and Services.
* Also enable the Container Registry or Artifact Registry API

**"The user-provided container failed to start":**

* For Cloud Run, check that your application listens on the `PORT` environment variable
* Review Cloud Run logs in the GCP Console for startup errors

**"Unable to connect to the server" (GKE):**

* Ensure the GKE cluster is running and the API server is accessible
* Verify the cluster name and zone in your `get-credentials` command

**Timeout errors:**

* Custom Actions have a 30-minute timeout per command
* App Engine and Cloud Run deployments can take several minutes. If builds are slow, consider deploying a pre-built container image instead of building from source.
