Last updated on 4th April 2026

Using the CLI in CI/CD Pipelines

The DeployHQ CLI works seamlessly in CI/CD pipelines. Authentication uses environment variables, so no interactive login is needed.

Setup

1. Set Secrets

Add these secrets to your CI provider:

Secret Description
DEPLOYHQ_ACCOUNT Your account subdomain (e.g. mycompany)
DEPLOYHQ_EMAIL API user email
DEPLOYHQ_API_KEY API key (found in Settings > Security)

2. Install the CLI

Use the install script in your pipeline:

curl -fsSL https://raw.githubusercontent.com/deployhq/deployhq-cli/main/install.sh | sh

3. Deploy

dhq deploy --server production --revision $COMMIT_SHA --wait --json

The --wait flag blocks until the deployment finishes, and --json provides machine-readable output for parsing results.

GitHub Actions Examples

Basic Deployment

Deploy to production on push to main:

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

env:
  DEPLOYHQ_ACCOUNT: ${{ secrets.DEPLOYHQ_ACCOUNT }}
  DEPLOYHQ_EMAIL: ${{ secrets.DEPLOYHQ_EMAIL }}
  DEPLOYHQ_API_KEY: ${{ secrets.DEPLOYHQ_API_KEY }}
  DEPLOYHQ_PROJECT: my-app

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Install DeployHQ CLI
        run: curl -fsSL https://raw.githubusercontent.com/deployhq/deployhq-cli/main/install.sh | sh

      - name: Deploy to production
        run: dhq deploy --server production --revision ${{ github.sha }} --wait --json

      - name: Show deployment logs (on failure)
        if: failure()
        run: |
          DEP_ID=$(dhq deployments list --json | jq -r '.data.records[0].identifier')
          dhq deployments logs "$DEP_ID"

Multi-Environment Deployment

Deploy to staging on push, production on release, with auto-rollback:

# .github/workflows/deploy-multi-env.yml
name: Deploy (multi-environment)

on:
  push:
    branches: [main]
  release:
    types: [published]

env:
  DEPLOYHQ_ACCOUNT: ${{ secrets.DEPLOYHQ_ACCOUNT }}
  DEPLOYHQ_EMAIL: ${{ secrets.DEPLOYHQ_EMAIL }}
  DEPLOYHQ_API_KEY: ${{ secrets.DEPLOYHQ_API_KEY }}
  DEPLOYHQ_PROJECT: my-app

jobs:
  deploy-staging:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - name: Install DeployHQ CLI
        run: curl -fsSL https://raw.githubusercontent.com/deployhq/deployhq-cli/main/install.sh | sh

      - name: Deploy to staging
        run: dhq deploy --server staging --revision ${{ github.sha }} --wait --json

  deploy-production:
    if: github.event_name == 'release'
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Install DeployHQ CLI
        run: curl -fsSL https://raw.githubusercontent.com/deployhq/deployhq-cli/main/install.sh | sh

      - name: Deploy to production
        run: dhq deploy --server production --revision ${{ github.sha }} --wait --json

      - name: Rollback on failure
        if: failure()
        run: |
          PREV=$(dhq deployments list --json | jq -r '.data.records[1].identifier')
          dhq rollback "$PREV" --json

Deploy on PR Merge with Status Comment

Deploy when a PR is merged, then post a deployment summary as a PR comment:

# .github/workflows/deploy-on-pr-merge.yml
name: Deploy on PR merge

on:
  pull_request:
    types: [closed]
    branches: [main]

env:
  DEPLOYHQ_ACCOUNT: ${{ secrets.DEPLOYHQ_ACCOUNT }}
  DEPLOYHQ_EMAIL: ${{ secrets.DEPLOYHQ_EMAIL }}
  DEPLOYHQ_API_KEY: ${{ secrets.DEPLOYHQ_API_KEY }}
  DEPLOYHQ_PROJECT: my-app

jobs:
  deploy:
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    steps:
      - name: Install DeployHQ CLI
        run: curl -fsSL https://raw.githubusercontent.com/deployhq/deployhq-cli/main/install.sh | sh

      - name: Deploy and wait
        id: deploy
        run: |
          RESULT=$(dhq deploy \
            --server production \
            --revision ${{ github.event.pull_request.merge_commit_sha }} \
            --wait --json)
          DEP_ID=$(echo "$RESULT" | jq -r '.data.identifier')
          STATUS=$(echo "$RESULT" | jq -r '.data.status')
          DURATION=$(echo "$RESULT" | jq -r '.data.timestamps.duration // "?"')
          SERVERS=$(echo "$RESULT" | jq -r '.data.servers | length')
          echo "deployment_id=$DEP_ID" >> "$GITHUB_OUTPUT"
          echo "status=$STATUS" >> "$GITHUB_OUTPUT"
          echo "duration=$DURATION" >> "$GITHUB_OUTPUT"
          echo "servers=$SERVERS" >> "$GITHUB_OUTPUT"

      - name: Comment on PR
        if: always()
        uses: actions/github-script@v7
        with:
          script: |
            const status = '${{ steps.deploy.outputs.status }}' || 'unknown';
            const depId = '${{ steps.deploy.outputs.deployment_id }}';
            const duration = '${{ steps.deploy.outputs.duration }}';
            const servers = '${{ steps.deploy.outputs.servers }}';
            const icon = status === 'completed' ? ':white_check_mark:' : ':x:';

            const body = `${icon} **Deployment ${status}**

            | Field | Value |
            |-------|-------|
            | ID | \`${depId}\` |
            | Duration | ${duration}s |
            | Servers | ${servers} |
            | Revision | \`${{ github.event.pull_request.merge_commit_sha }}\` |`;

            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body
            });

GitLab CI

# .gitlab-ci.yml
deploy:
  stage: deploy
  image: ubuntu:latest
  variables:
    DEPLOYHQ_ACCOUNT: $DEPLOYHQ_ACCOUNT
    DEPLOYHQ_EMAIL: $DEPLOYHQ_EMAIL
    DEPLOYHQ_API_KEY: $DEPLOYHQ_API_KEY
    DEPLOYHQ_PROJECT: my-app
  script:
    - curl -fsSL https://raw.githubusercontent.com/deployhq/deployhq-cli/main/install.sh | sh
    - dhq deploy --server production --revision $CI_COMMIT_SHA --wait --json
  only:
    - main

Bitbucket Pipelines

# bitbucket-pipelines.yml
pipelines:
  branches:
    main:
      - step:
          name: Deploy
          script:
            - curl -fsSL https://raw.githubusercontent.com/deployhq/deployhq-cli/main/install.sh | sh
            - dhq deploy --server production --revision $BITBUCKET_COMMIT --wait --json

Set DEPLOYHQ_ACCOUNT, DEPLOYHQ_EMAIL, and DEPLOYHQ_API_KEY as repository variables in Bitbucket.

Tips for CI/CD

Parsing JSON Output

Use jq to extract specific fields from JSON output:

# Get deployment status
dhq deployments show abc123 -p my-app --json | jq -r '.data.status'

# Get the latest deployment ID
dhq deployments list -p my-app --json | jq -r '.data.records[0].identifier'

Non-Zero Exit Codes

The CLI returns non-zero exit codes on failure, so your CI pipeline will automatically fail if a deployment fails when using --wait.

DEPLOYHQ_PROJECT Environment Variable

Set DEPLOYHQ_PROJECT to avoid passing -p on every command:

export DEPLOYHQ_PROJECT=my-app
dhq deploy --server production --wait

Security

  • Always store credentials as CI secrets, never in pipeline configuration files
  • Use the minimum required permissions for the API key
  • Consider using a dedicated service account for CI/CD deployments