GitHub Actions Deployment

Two approaches for deploying with GitHub Actions: the gordon push CLI (recommended) or a Docker-based workflow using Gordon's official action.

Prerequisites

  1. Gordon server running with registry authentication enabled
  2. Deployment token generated with the required scopes
  3. GitHub repository secrets configured

Use the gordon push CLI for a lightweight, single-step build and deploy.

1. Generate Token

On your Gordon server:

gordon auth token generate \
  --subject github-actions \
  --scopes "push,pull,admin:routes:read,admin:config:write" \
  --expiry 0

Save the token output.

2. Add GitHub Secrets

In your repository: Settings → Secrets → Actions → New repository secret

Secret Value
GORDON_TOKEN The generated token
GORDON_REMOTE https://gordon.example.com

3. Setup Gordon CLI

Add this step to your workflow to install the Gordon binary:

- name: Setup Gordon
  uses: bnema/gordon/.github/actions/setup-gordon@main
  # with:
  #   version: latest  # default, or pin to e.g. v2.0.0

4. Workflow Examples

Deploy on Tag Push

Gordon auto-detects the version from $GITHUB_REF (e.g., refs/tags/v1.2.0v1.2.0).

name: Deploy to Gordon

on:
  push:
    tags: ['v*']

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

      - name: Setup Gordon
        uses: bnema/gordon/.github/actions/setup-gordon@main

      - name: Build and Deploy
        env:
          GORDON_TOKEN: ${{ secrets.GORDON_TOKEN }}
        run: |
          gordon push --build \
            --remote ${{ secrets.GORDON_REMOTE }} \
            --no-confirm

Continuous Deploy on Main

Deploy on every push to main. The version defaults to latest or the output of git describe.

name: Deploy to Gordon

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Gordon
        uses: bnema/gordon/.github/actions/setup-gordon@main

      - name: Build and Deploy
        env:
          GORDON_TOKEN: ${{ secrets.GORDON_TOKEN }}
        run: |
          gordon push --build \
            --remote ${{ secrets.GORDON_REMOTE }} \
            --tag latest \
            --no-confirm

Manual Dispatch

Allow manual deployments with an optional tag input:

name: Deploy to Gordon

on:
  workflow_dispatch:
    inputs:
      tag:
        description: 'Tag to deploy (leave empty to use git describe)'
        required: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.inputs.tag || github.ref }}
          fetch-depth: 0

      - name: Setup Gordon
        uses: bnema/gordon/.github/actions/setup-gordon@main

      - name: Build and Deploy
        env:
          GORDON_TOKEN: ${{ secrets.GORDON_TOKEN }}
        run: |
          gordon push --build \
            --remote ${{ secrets.GORDON_REMOTE }} \
            ${{ github.event.inputs.tag && format('--tag {0}', github.event.inputs.tag) || '' }} \
            --no-confirm

Monorepo

Deploy multiple services with separate gordon push calls:

name: Deploy Services

on:
  push:
    tags: ['v*']

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

      - name: Setup Gordon
        uses: bnema/gordon/.github/actions/setup-gordon@main

      - name: Deploy API
        env:
          GORDON_TOKEN: ${{ secrets.GORDON_TOKEN }}
        run: |
          gordon push --build \
            --remote ${{ secrets.GORDON_REMOTE }} \
            --file ./services/api/Dockerfile \
            myapp-api \
            --no-confirm

      - name: Deploy Web
        env:
          GORDON_TOKEN: ${{ secrets.GORDON_TOKEN }}
        run: |
          gordon push --build \
            --remote ${{ secrets.GORDON_REMOTE }} \
            --file ./services/web/Dockerfile \
            myapp-web \
            --no-confirm

With Build Args

Pass build arguments to the Docker build:

- name: Build and Deploy
  env:
    GORDON_TOKEN: ${{ secrets.GORDON_TOKEN }}
  run: |
    gordon push --build \
      --remote ${{ secrets.GORDON_REMOTE }} \
      --build-arg NODE_ENV=production \
      --build-arg API_URL=https://api.example.com \
      --build-arg BUILD_DATE=${{ github.event.head_commit.timestamp }} \
      --no-confirm

setup-gordon Action Reference

Input Required Default Description
version No latest Gordon version to install (v2.0.0 or latest)
Output Description
version The version actually installed
path Path to the installed binary

Alternative: Docker-based Workflow

For environments where installing the Gordon binary is not desired, use Docker directly to build and push to the Gordon registry. Gordon auto-deploys when it receives the image — no explicit deploy step is needed.

Setup

Add these secrets to your repository:

Secret Value
GORDON_REGISTRY registry.gordon.example.com
GORDON_USERNAME github-actions
GORDON_TOKEN The generated token

Workflow

name: Deploy to Gordon

on:
  push:
    tags: ['v*']

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

      - name: Login to Gordon Registry
        run: echo "${{ secrets.GORDON_TOKEN }}" | docker login ${{ secrets.GORDON_REGISTRY }} -u ${{ secrets.GORDON_USERNAME }} --password-stdin

      - name: Build and Push
        run: |
          docker build -t ${{ secrets.GORDON_REGISTRY }}/myapp:${{ github.ref_name }} .
          docker push ${{ secrets.GORDON_REGISTRY }}/myapp:${{ github.ref_name }}

Alternative: Gordon GitHub Action

Use the official bnema/gordon action for a fully declarative workflow. This action handles login, build, and push in a single step.

Setup

Requires the same 3 secrets as the Docker-based workflow: GORDON_REGISTRY, GORDON_USERNAME, GORDON_TOKEN.

Workflow Examples

Deploy on Tag Push

name: Deploy to Gordon

on:
  push:
    tags:
      - 'v*'

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

      - name: Deploy to Gordon
        uses: bnema/gordon/.github/actions/deploy@main
        with:
          registry: ${{ secrets.GORDON_REGISTRY }}
          username: ${{ secrets.GORDON_USERNAME }}
          password: ${{ secrets.GORDON_TOKEN }}

Continuous Deployment

Deploy on every push to main:

name: Deploy to Gordon

on:
  push:
    branches:
      - main

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

      - name: Deploy to Gordon
        uses: bnema/gordon/.github/actions/deploy@main
        with:
          registry: ${{ secrets.GORDON_REGISTRY }}
          username: ${{ secrets.GORDON_USERNAME }}
          password: ${{ secrets.GORDON_TOKEN }}
          push-latest: 'true'

Manual Deployment

Allow manual deployments with a custom tag:

name: Deploy to Gordon

on:
  workflow_dispatch:
    inputs:
      tag:
        description: 'Tag to deploy'
        required: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.inputs.tag || github.ref }}

      - name: Deploy to Gordon
        uses: bnema/gordon/.github/actions/deploy@main
        with:
          registry: ${{ secrets.GORDON_REGISTRY }}
          username: ${{ secrets.GORDON_USERNAME }}
          password: ${{ secrets.GORDON_TOKEN }}
          tag: ${{ github.event.inputs.tag }}

Monorepo Deployment

Deploy multiple services:

name: Deploy Services

on:
  push:
    tags:
      - 'v*'

jobs:
  deploy-api:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: bnema/gordon/.github/actions/deploy@main
        with:
          registry: ${{ secrets.GORDON_REGISTRY }}
          username: ${{ secrets.GORDON_USERNAME }}
          password: ${{ secrets.GORDON_TOKEN }}
          image: myapp-api
          dockerfile: ./services/api/Dockerfile
          context: ./services/api

  deploy-web:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: bnema/gordon/.github/actions/deploy@main
        with:
          registry: ${{ secrets.GORDON_REGISTRY }}
          username: ${{ secrets.GORDON_USERNAME }}
          password: ${{ secrets.GORDON_TOKEN }}
          image: myapp-web
          dockerfile: ./services/web/Dockerfile
          context: ./services/web

With Build Arguments

- uses: bnema/gordon/.github/actions/deploy@main
  with:
    registry: ${{ secrets.GORDON_REGISTRY }}
    username: ${{ secrets.GORDON_USERNAME }}
    password: ${{ secrets.GORDON_TOKEN }}
    build-args: |
      NODE_ENV=production
      API_URL=https://api.example.com
      BUILD_DATE=${{ github.event.head_commit.timestamp }}

Multi-Platform Build

- uses: bnema/gordon/.github/actions/deploy@main
  with:
    registry: ${{ secrets.GORDON_REGISTRY }}
    username: ${{ secrets.GORDON_USERNAME }}
    password: ${{ secrets.GORDON_TOKEN }}
    platforms: linux/amd64,linux/arm64

Action Reference

Inputs

Input Required Default Description
registry Yes - Gordon registry URL
username Yes - Registry username
password Yes - Registry token
image No repo name Image name
tag No git tag/SHA Image tag
dockerfile No ./Dockerfile Dockerfile path
context No . Build context
push-latest No false Also push :latest tag
platforms No linux/amd64 Target platforms
build-args No - Build arguments

Outputs

Output Description
image Full image name
tag Image tag

Deployment Summary

Add a deployment summary to your workflow:

- name: Deploy to Gordon
  id: deploy
  uses: bnema/gordon/.github/actions/deploy@main
  with:
    registry: ${{ secrets.GORDON_REGISTRY }}
    username: ${{ secrets.GORDON_USERNAME }}
    password: ${{ secrets.GORDON_TOKEN }}

- name: Deployment Summary
  run: |
    echo "## Deployment Complete" >> $GITHUB_STEP_SUMMARY
    echo "" >> $GITHUB_STEP_SUMMARY
    echo "**Image:** \`${{ steps.deploy.outputs.image }}\`" >> $GITHUB_STEP_SUMMARY
    echo "**Tag:** \`${{ steps.deploy.outputs.tag }}\`" >> $GITHUB_STEP_SUMMARY

Troubleshooting

Authentication Failed

Error: unauthorized: authentication required

Solution: Verify secrets are correctly set and token has push scope.

Image Not Found After Push

Solution: Ensure Gordon config has a route for the pushed image.

Build Fails

Solution: Check Dockerfile path and build context settings.