Skip to content

Build Openclaw Docker Images #747

Build Openclaw Docker Images

Build Openclaw Docker Images #747

Workflow file for this run

name: Build Openclaw Docker Images
on:
schedule:
# Check for new openclaw releases every hour
- cron: '0 * * * *'
workflow_dispatch:
inputs:
version:
description: 'Openclaw version to build (e.g., 2026.1.29). Leave empty to build latest.'
required: false
type: string
force_rebuild:
description: 'Force rebuild even if image already exists'
required: false
type: boolean
default: false
skip_latest_tag:
description: 'Skip tagging as latest (useful for building older versions)'
required: false
type: boolean
default: false
env:
GITHUB_REGISTRY: ghcr.io
DOCKER_REGISTRY: docker.io
IMAGE_NAME: coollabsio/openclaw
BASE_IMAGE_NAME: coollabsio/openclaw-base
jobs:
check-release:
runs-on: ubuntu-24.04
outputs:
version: ${{ steps.openclaw-release.outputs.version }}
should_build: ${{ steps.check-image.outputs.exists == 'false' }}
steps:
- name: Get latest Openclaw release
id: openclaw-release
run: |
if [ -n "${{ github.event.inputs.version }}" ]; then
VERSION="${{ github.event.inputs.version }}"
echo "Using manually specified version: $VERSION"
else
LATEST_RELEASE=$(curl -s https://api.github.com/repos/openclaw/openclaw/releases/latest | jq -r '.tag_name')
# Strip leading 'v' if present (openclaw uses v2026.1.29 format)
VERSION=$(echo "$LATEST_RELEASE" | sed 's/^v//')
echo "Latest Openclaw release: $VERSION"
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Check if image already exists
id: check-image
run: |
VERSION="${{ steps.openclaw-release.outputs.version }}"
GHCR_IMAGE="${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}"
FORCE_REBUILD="${{ github.event.inputs.force_rebuild }}"
if [ "$FORCE_REBUILD" = "true" ]; then
echo "Force rebuild enabled, skipping existence check"
echo "exists=false" >> $GITHUB_OUTPUT
elif docker manifest inspect "${GHCR_IMAGE}:${VERSION}" > /dev/null 2>&1; then
echo "Image ${GHCR_IMAGE}:${VERSION} already exists"
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "Image ${GHCR_IMAGE}:${VERSION} does not exist, will build"
echo "exists=false" >> $GITHUB_OUTPUT
fi
build-base:
strategy:
matrix:
include:
- arch: amd64
runner: ubuntu-24.04
- arch: arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
needs: check-release
if: needs.check-release.outputs.should_build == 'true'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to ${{ env.GITHUB_REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.GITHUB_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to ${{ env.DOCKER_REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and Push Base Image (${{ matrix.arch }})
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile.base
platforms: linux/${{ matrix.arch }}
push: true
build-args: |
OPENCLAW_GIT_REF=v${{ needs.check-release.outputs.version }}
tags: |
${{ env.DOCKER_REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:${{ needs.check-release.outputs.version }}-${{ matrix.arch }}
${{ env.GITHUB_REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:${{ needs.check-release.outputs.version }}-${{ matrix.arch }}
cache-from: type=gha,scope=base-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=base-${{ matrix.arch }}
merge-base-manifest:
runs-on: ubuntu-latest
needs: [check-release, build-base]
if: needs.check-release.outputs.should_build == 'true'
permissions:
contents: read
packages: write
steps:
- uses: docker/setup-buildx-action@v3
- name: Login to ${{ env.GITHUB_REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.GITHUB_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to ${{ env.DOCKER_REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Create & publish base manifest on ${{ env.GITHUB_REGISTRY }}
run: |
VERSION="${{ needs.check-release.outputs.version }}"
BASE="${{ env.GITHUB_REGISTRY }}/${{ env.BASE_IMAGE_NAME }}"
TAGS="-t ${BASE}:${VERSION}"
if [ "${{ github.event.inputs.skip_latest_tag }}" != "true" ]; then
TAGS="$TAGS -t ${BASE}:latest"
fi
docker buildx imagetools create \
$TAGS \
"${BASE}:${VERSION}-amd64" \
"${BASE}:${VERSION}-arm64"
- name: Create & publish base manifest on ${{ env.DOCKER_REGISTRY }}
run: |
VERSION="${{ needs.check-release.outputs.version }}"
BASE="${{ env.DOCKER_REGISTRY }}/${{ env.BASE_IMAGE_NAME }}"
TAGS="-t ${BASE}:${VERSION}"
if [ "${{ github.event.inputs.skip_latest_tag }}" != "true" ]; then
TAGS="$TAGS -t ${BASE}:latest"
fi
docker buildx imagetools create \
$TAGS \
"${BASE}:${VERSION}-amd64" \
"${BASE}:${VERSION}-arm64"
build-final:
strategy:
matrix:
include:
- arch: amd64
runner: ubuntu-24.04
- arch: arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
needs: [check-release, merge-base-manifest]
if: needs.check-release.outputs.should_build == 'true'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to ${{ env.GITHUB_REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.GITHUB_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to ${{ env.DOCKER_REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and Push Final Image (${{ matrix.arch }})
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
platforms: linux/${{ matrix.arch }}
push: true
build-args: |
BASE_IMAGE=${{ env.GITHUB_REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:${{ needs.check-release.outputs.version }}-${{ matrix.arch }}
tags: |
${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.check-release.outputs.version }}-${{ matrix.arch }}
${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.check-release.outputs.version }}-${{ matrix.arch }}
cache-from: type=gha,scope=final-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=final-${{ matrix.arch }}
merge-final-manifest:
runs-on: ubuntu-latest
needs: [check-release, build-final]
if: needs.check-release.outputs.should_build == 'true'
permissions:
contents: read
packages: write
steps:
- uses: docker/setup-buildx-action@v3
- name: Login to ${{ env.GITHUB_REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.GITHUB_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to ${{ env.DOCKER_REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Create & publish final manifest on ${{ env.GITHUB_REGISTRY }}
run: |
VERSION="${{ needs.check-release.outputs.version }}"
IMAGE="${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}"
TAGS="-t ${IMAGE}:${VERSION}"
if [ "${{ github.event.inputs.skip_latest_tag }}" != "true" ]; then
TAGS="$TAGS -t ${IMAGE}:latest"
fi
docker buildx imagetools create \
$TAGS \
"${IMAGE}:${VERSION}-amd64" \
"${IMAGE}:${VERSION}-arm64"
- name: Create & publish final manifest on ${{ env.DOCKER_REGISTRY }}
run: |
VERSION="${{ needs.check-release.outputs.version }}"
IMAGE="${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}"
TAGS="-t ${IMAGE}:${VERSION}"
if [ "${{ github.event.inputs.skip_latest_tag }}" != "true" ]; then
TAGS="$TAGS -t ${IMAGE}:latest"
fi
docker buildx imagetools create \
$TAGS \
"${IMAGE}:${VERSION}-amd64" \
"${IMAGE}:${VERSION}-arm64"
- name: Summary
run: |
VERSION="${{ needs.check-release.outputs.version }}"
echo "## Openclaw ${VERSION}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Successfully built and published:" >> $GITHUB_STEP_SUMMARY
echo "- \`${{ env.GITHUB_REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:${VERSION}\` (base)" >> $GITHUB_STEP_SUMMARY
echo "- \`${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${VERSION}\` (final)" >> $GITHUB_STEP_SUMMARY
echo "- \`${{ env.DOCKER_REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:${VERSION}\` (base)" >> $GITHUB_STEP_SUMMARY
echo "- \`${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${VERSION}\` (final)" >> $GITHUB_STEP_SUMMARY