CI/CD Integration

Deploy with GitHub Actions, GitLab CI, and other CI/CD platforms

GitHub Actions

Pipe works seamlessly with GitHub Actions. Install Pipe in your workflow, configure SSH access, and deploy.

Prerequisites

  • SSH key with access to your deployment server(s)
  • Docker installed on the remote host
  • GitHub repository secrets configured

Basic Workflow

A simple workflow that deploys on push to the main branch.

.github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.21'

      - name: Install Pipe
        run: go install github.com/bjarneo/pipe@latest

      - name: Set up SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts

      - name: Deploy
        run: |
          pipe \
            --host ${{ secrets.DEPLOY_HOST }} \
            --user ${{ secrets.DEPLOY_USER }} \
            --ssh-key ~/.ssh/id_rsa \
            --tag ${{ github.sha }}
Tip: Use ${{ github.sha }} as the tag for traceability, or ${{ github.ref_name }} for branch/tag names.

Matrix Deploy (Multiple Servers)

Deploy to multiple servers in parallel using GitHub Actions matrix strategy. Perfect for deploying to a fleet of VMs or different environments.

Deploy to Multiple VMs

.github/workflows/deploy-matrix.yml
name: Deploy to Multiple Servers

on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        server:
          - host: server1.example.com
            name: server-1
          - host: server2.example.com
            name: server-2
          - host: server3.example.com
            name: server-3
      fail-fast: false
      max-parallel: 3

    steps:
      - uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.21'

      - name: Install Pipe
        run: go install github.com/bjarneo/pipe@latest

      - name: Set up SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan -H ${{ matrix.server.host }} >> ~/.ssh/known_hosts

      - name: Deploy to ${{ matrix.server.name }}
        run: |
          pipe \
            --host ${{ matrix.server.host }} \
            --user ${{ secrets.DEPLOY_USER }} \
            --ssh-key ~/.ssh/id_rsa \
            --tag ${{ github.sha }} \
            --container-name app-${{ matrix.server.name }}

Matrix with Different Configurations

Deploy different services or configurations to different servers:

.github/workflows/deploy-services.yml
name: Deploy Services

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        include:
          - host: api.example.com
            service: api
            port: 3000
            dockerfile: Dockerfile.api
          - host: web.example.com
            service: web
            port: 8080
            dockerfile: Dockerfile.web
          - host: worker.example.com
            service: worker
            port: 9000
            dockerfile: Dockerfile.worker
      fail-fast: false

    steps:
      - uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.21'

      - name: Install Pipe
        run: go install github.com/bjarneo/pipe@latest

      - name: Set up SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan -H ${{ matrix.host }} >> ~/.ssh/known_hosts

      - name: Deploy ${{ matrix.service }}
        run: |
          pipe \
            --host ${{ matrix.host }} \
            --user deploy \
            --ssh-key ~/.ssh/id_rsa \
            --dockerfile ${{ matrix.dockerfile }} \
            --image ${{ matrix.service }} \
            --container-name ${{ matrix.service }} \
            --container-port ${{ matrix.port }} \
            --host-port ${{ matrix.port }} \
            --tag ${{ github.sha }}
Tip: Set fail-fast: false to continue deploying to other servers even if one fails.

Environment-Based Deployment

Use GitHub Environments for staged deployments with approvals and environment-specific secrets.

.github/workflows/deploy-environments.yml
name: Deploy with Environments

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

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.21'

      - name: Install Pipe
        run: go install github.com/bjarneo/pipe@latest

      - name: Set up SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan -H ${{ vars.DEPLOY_HOST }} >> ~/.ssh/known_hosts

      - name: Deploy to Staging
        run: |
          pipe \
            --host ${{ vars.DEPLOY_HOST }} \
            --user ${{ secrets.DEPLOY_USER }} \
            --ssh-key ~/.ssh/id_rsa \
            --tag ${{ github.sha }} \
            --env NODE_ENV=staging

  deploy-production:
    runs-on: ubuntu-latest
    environment: production
    needs: deploy-staging
    if: github.event_name == 'release'
    steps:
      - uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.21'

      - name: Install Pipe
        run: go install github.com/bjarneo/pipe@latest

      - name: Set up SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan -H ${{ vars.DEPLOY_HOST }} >> ~/.ssh/known_hosts

      - name: Deploy to Production
        run: |
          pipe \
            --host ${{ vars.DEPLOY_HOST }} \
            --user ${{ secrets.DEPLOY_USER }} \
            --ssh-key ~/.ssh/id_rsa \
            --tag ${{ github.event.release.tag_name }} \
            --env NODE_ENV=production

Required Secrets

Configure these secrets in your GitHub repository settings (Settings → Secrets and variables → Actions):

Secret Description Example
SSH_PRIVATE_KEY Private SSH key for server access Contents of ~/.ssh/id_rsa
DEPLOY_HOST Target server hostname or IP server.example.com
DEPLOY_USER SSH username on the server deploy

Generating a Deploy Key

Terminal
# Generate a new SSH key pair for deployments
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/deploy_key -N ""

# Copy the public key to your server
ssh-copy-id -i ~/.ssh/deploy_key.pub user@server.example.com

# Add the private key contents to GitHub Secrets
cat ~/.ssh/deploy_key
Security: Use a dedicated deploy key with minimal permissions. Never use your personal SSH key.

GitLab CI

Pipe also works with GitLab CI/CD pipelines.

.gitlab-ci.yml
stages:
  - deploy

variables:
  GO_VERSION: '1.21'

deploy:
  stage: deploy
  image: golang:${GO_VERSION}
  only:
    - main
  before_script:
    - go install github.com/bjarneo/pipe@latest
    - mkdir -p ~/.ssh
    - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
    - ssh-keyscan -H $DEPLOY_HOST >> ~/.ssh/known_hosts
  script:
    - pipe
        --host $DEPLOY_HOST
        --user $DEPLOY_USER
        --ssh-key ~/.ssh/id_rsa
        --tag $CI_COMMIT_SHA

deploy-matrix:
  stage: deploy
  image: golang:${GO_VERSION}
  only:
    - main
  parallel:
    matrix:
      - SERVER: [server1.example.com, server2.example.com, server3.example.com]
  before_script:
    - go install github.com/bjarneo/pipe@latest
    - mkdir -p ~/.ssh
    - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
    - ssh-keyscan -H $SERVER >> ~/.ssh/known_hosts
  script:
    - pipe
        --host $SERVER
        --user $DEPLOY_USER
        --ssh-key ~/.ssh/id_rsa
        --tag $CI_COMMIT_SHA

JSON Output for CI

Use --json to get machine-readable output for CI/CD integration, notifications, or metrics.

Example: Post deployment stats to Slack
- name: Deploy and notify
  run: |
    RESULT=$(pipe --host ${{ secrets.DEPLOY_HOST }} --user deploy --json)
    DURATION=$(echo $RESULT | jq -r '.timing.duration')
    TRANSFERRED=$(echo $RESULT | jq -r '.transfer.transferred')

    curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
      -H "Content-Type: application/json" \
      -d "{\"text\": \"Deployed in ${DURATION} (transferred ${TRANSFERRED})\"}"
Example: Fail on slow deployments
- name: Deploy with timeout check
  run: |
    RESULT=$(pipe --host ${{ secrets.DEPLOY_HOST }} --user deploy --json)
    SECONDS=$(echo $RESULT | jq -r '.timing.total_seconds')

    if (( $(echo "$SECONDS > 300" | bc -l) )); then
      echo "Deployment took longer than 5 minutes!"
      exit 1
    fi

JSON Output Structure

Example output
{
  "timing": {
    "duration": "23.4s",
    "total_seconds": 23.4,
    "build": "8.2s",
    "transfer": "12.1s",
    "deploy": "3.1s"
  },
  "transfer": {
    "transferred": "47MB",
    "saved": "312MB",
    "layers_changed": 3,
    "layers_cached": 12
  },
  "image": {
    "name": "my-app",
    "tag": "abc1234"
  },
  "host": "server.example.com",
  "success": true
}