Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 196 additions & 0 deletions .github/workflows/docker-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
name: Build & Push to Docker Hub

on:
push:
branches:
- main
tags:
- 'v*'
paths:
- 'Dockerfile'
- 'Dockerfile.alpine'
- 'OPENCLAW_VERSION'
- '.github/workflows/docker-release.yml'
pull_request:
branches:
- main
paths:
- 'Dockerfile'
- 'Dockerfile.alpine'
- 'OPENCLAW_VERSION'
- '.github/workflows/docker-release.yml'
workflow_dispatch:

concurrency:
group: docker-release-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
validate:
if: github.event_name == 'pull_request'
name: Build & Push Docker Image (${{ matrix.variant }})
runs-on: ubuntu-latest
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
include:
- variant: debian
dockerfile: Dockerfile
platforms: linux/amd64,linux/arm64
- variant: alpine
dockerfile: Dockerfile.alpine
platforms: linux/amd64

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Read OpenClaw version from OPENCLAW_VERSION
id: openclaw_version
run: |
VERSION=$(tr -d '[:space:]' < OPENCLAW_VERSION)
if [ -z "$VERSION" ]; then
echo "OPENCLAW_VERSION not found in OPENCLAW_VERSION"
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Extract Docker metadata (labels only)
id: meta
uses: docker/metadata-action@v5
with:
images: moltenai/openclaw

- name: Build Docker image (${{ matrix.variant }})
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.dockerfile }}
push: false
platforms: ${{ matrix.platforms }}
tags: moltenai/openclaw:validate-${{ steps.openclaw_version.outputs.version }}-${{ matrix.variant }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

build-and-push:
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
name: Build & Push Docker Image (${{ matrix.variant }})
runs-on: ubuntu-latest
timeout-minutes: 120
environment: default
strategy:
fail-fast: false
matrix:
include:
- variant: debian
dockerfile: Dockerfile
platforms: linux/amd64,linux/arm64
- variant: alpine
dockerfile: Dockerfile.alpine
platforms: linux/amd64

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Validate Docker Hub configuration
run: |
if [ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]; then
echo "secrets.DOCKERHUB_TOKEN is required."
exit 1
fi

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: moltenai
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Read OpenClaw version from OPENCLAW_VERSION
id: openclaw_version
run: |
VERSION=$(tr -d '[:space:]' < OPENCLAW_VERSION)
if [ -z "$VERSION" ]; then
echo "OPENCLAW_VERSION not found in OPENCLAW_VERSION"
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Compute tags for ${{ matrix.variant }}
id: image_tags
run: |
set -eu

VERSION="${{ steps.openclaw_version.outputs.version }}"
IMAGE="moltenai/openclaw"

if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
RAW_TAG="${GITHUB_REF#refs/tags/}"
STRIPPED_TAG="${RAW_TAG#v}"

if [[ "${{ matrix.variant }}" == "debian" ]]; then
TAGS=(
"${IMAGE}:${RAW_TAG}"
"${IMAGE}:${STRIPPED_TAG}"
"${IMAGE}:${RAW_TAG}-lts"
"${IMAGE}:${STRIPPED_TAG}-lts"
)
else
TAGS=(
"${IMAGE}:${RAW_TAG}-alpine"
"${IMAGE}:${STRIPPED_TAG}-alpine"
)
fi
else
if [[ "${{ matrix.variant }}" == "debian" ]]; then
TAGS=(
"${IMAGE}:latest"
"${IMAGE}:lts"
"${IMAGE}:${VERSION}"
"${IMAGE}:${VERSION}-lts"
)
else
TAGS=(
"${IMAGE}:alpine"
"${IMAGE}:${VERSION}-alpine"
)
fi
fi

{
echo 'tags<<EOF'
printf '%s\n' "${TAGS[@]}"
echo 'EOF'
} >> "${GITHUB_OUTPUT}"

- name: Extract Docker metadata (labels only)
id: meta
uses: docker/metadata-action@v5
with:
images: moltenai/openclaw

- name: Build and push Docker image (${{ matrix.variant }})
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.dockerfile }}
push: true
platforms: ${{ matrix.platforms }}
tags: ${{ steps.image_tags.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
34 changes: 34 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
FROM node:lts-trixie-slim AS builder

RUN apt-get update && apt-get install -y \
git \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /build

COPY OPENCLAW_VERSION /tmp/OPENCLAW_VERSION

# Install openclaw globally, and backfill Control UI assets if upstream npm
# package omitted dist/control-ui for this version.
RUN set -eux; \
OPENCLAW_VERSION="$(tr -d '[:space:]' < /tmp/OPENCLAW_VERSION)"; \
npm install -g "openclaw@${OPENCLAW_VERSION}"; \
OPENCLAW_ROOT="$(npm root -g)/openclaw"; \
CONTROL_UI_INDEX="${OPENCLAW_ROOT}/dist/control-ui/index.html"; \
if [ ! -f "${CONTROL_UI_INDEX}" ]; then \
echo "Control UI assets missing in openclaw@${OPENCLAW_VERSION}; building from source tag."; \
corepack enable; \
corepack prepare pnpm@10.23.0 --activate; \
rm -rf /tmp/openclaw-src; \
if ! git clone --depth 1 --branch "v${OPENCLAW_VERSION}" https://github.com/openclaw/openclaw.git /tmp/openclaw-src; then \
git clone --depth 1 --branch "v${OPENCLAW_VERSION}-1" https://github.com/openclaw/openclaw.git /tmp/openclaw-src; \
fi; \
cd /tmp/openclaw-src; \
CI=true pnpm --dir ui install --prod=false; \
pnpm --dir ui build; \
mkdir -p "${OPENCLAW_ROOT}/dist"; \
rm -rf "${OPENCLAW_ROOT}/dist/control-ui"; \
cp -R dist/control-ui "${OPENCLAW_ROOT}/dist/control-ui"; \
test -f "${CONTROL_UI_INDEX}"; \
rm -rf /tmp/openclaw-src /root/.local/share/pnpm /root/.cache/pnpm /root/.cache/node/corepack; \
fi
37 changes: 37 additions & 0 deletions Dockerfile.alpine
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
FROM node:lts-alpine AS builder

RUN apk add --no-cache \
git \
make \
g++ \
cmake \
linux-headers

WORKDIR /build

COPY OPENCLAW_VERSION /tmp/OPENCLAW_VERSION

# Install openclaw globally, and backfill Control UI assets if upstream npm
# package omitted dist/control-ui for this version.
RUN set -eux; \
OPENCLAW_VERSION="$(tr -d '[:space:]' < /tmp/OPENCLAW_VERSION)"; \
npm install -g "openclaw@${OPENCLAW_VERSION}"; \
OPENCLAW_ROOT="$(npm root -g)/openclaw"; \
CONTROL_UI_INDEX="${OPENCLAW_ROOT}/dist/control-ui/index.html"; \
if [ ! -f "${CONTROL_UI_INDEX}" ]; then \
echo "Control UI assets missing in openclaw@${OPENCLAW_VERSION}; building from source tag."; \
corepack enable; \
corepack prepare pnpm@10.23.0 --activate; \
rm -rf /tmp/openclaw-src; \
if ! git clone --depth 1 --branch "v${OPENCLAW_VERSION}" https://github.com/openclaw/openclaw.git /tmp/openclaw-src; then \
git clone --depth 1 --branch "v${OPENCLAW_VERSION}-1" https://github.com/openclaw/openclaw.git /tmp/openclaw-src; \
fi; \
cd /tmp/openclaw-src; \
CI=true pnpm --dir ui install --prod=false; \
pnpm --dir ui build; \
mkdir -p "${OPENCLAW_ROOT}/dist"; \
rm -rf "${OPENCLAW_ROOT}/dist/control-ui"; \
cp -R dist/control-ui "${OPENCLAW_ROOT}/dist/control-ui"; \
test -f "${CONTROL_UI_INDEX}"; \
rm -rf /tmp/openclaw-src /root/.local/share/pnpm /root/.cache/pnpm /root/.cache/node/corepack; \
fi
1 change: 1 addition & 0 deletions OPENCLAW_VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2026.5.7
119 changes: 116 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,123 @@
# openclaw

This repository no longer builds or publishes container images for openclaw.
Docker image for [openclaw](https://www.npmjs.com/package/openclaw), automatically built and published to [moltenbot/openclaw](https://hub.docker.com/r/moltenbot/openclaw) on Docker Hub.

The Dockerfiles, Alpine container build, and Docker Hub build/release pipeline have been removed.
Debian-based tags are published as multi-arch manifests for:
- `linux/amd64`
- `linux/arm64`

Use the upstream [openclaw npm package](https://www.npmjs.com/package/openclaw) directly.
Alpine tags are currently published for:
- `linux/amd64`

[![Build & Push to Docker Hub](https://github.com/Molten-Bot/openclaw/actions/workflows/docker-release.yml/badge.svg)](https://github.com/Molten-Bot/openclaw/actions/workflows/docker-release.yml)

Pull requests that change Dockerfiles, `OPENCLAW_VERSION`, or this workflow run the Docker build matrix as validation. Only `main` pushes and `v*` tags publish images to Docker Hub.

## Usage

```bash
docker pull moltenbot/openclaw:latest
```

### Tag Policy

| Tag | Base | Support level | Notes |
| --- | --- | --- | --- |
| `latest` | Debian slim (Node LTS) | Primary | Recommended default tag |
| `lts` | Debian slim (Node LTS) | Primary | Alias of `latest` |
| `alpine` | Alpine (Node LTS) | Best effort | `linux/amd64` only |
| `${OPENCLAW_VERSION}` | Debian slim (Node LTS) | Primary | Versioned Debian image |
| `${OPENCLAW_VERSION}-lts` | Debian slim (Node LTS) | Primary | Versioned LTS alias |
| `${OPENCLAW_VERSION}-alpine` | Alpine (Node LTS) | Best effort | `linux/amd64` only |

Pull examples:

```bash
docker pull moltenbot/openclaw:latest
docker pull moltenbot/openclaw:lts
docker pull moltenbot/openclaw:alpine
```

Local builds automatically use the version in `OPENCLAW_VERSION`:

```bash
docker build -f Dockerfile .
docker build -f Dockerfile.alpine .
```

### Compatibility Notes

- Alpine images use musl instead of glibc.
- Native modules and some tooling may behave differently on Alpine.
- Debian LTS variants are the primary support target.
- Alpine is best-effort and may briefly lag if upstream breakage occurs.
- Alpine arm64 is intentionally not published from CI at this time due build-time instability with native npm dependencies.
- If an upstream npm release omits `dist/control-ui`, Docker build falls back to building Control UI assets from the matching source tag and copies them into the installed package.

### Migration Note

Prior behavior:
- `latest` tracked a current-major Debian Node image.

Current behavior:
- `latest` tracks Debian slim on Node LTS.

If you require a specific runtime behavior, pin explicit image tags.

## Contributing

Contributions are welcome! Please follow the steps below.

### 1. Fork & clone the repository

```bash
git clone https://github.com/Molten-Bot/openclaw.git
cd openclaw
```

### 2. Create a branch

Use a short, descriptive branch name:

```bash
git checkout -b your-feature-or-fix
```

### 3. Make your changes

The main files to edit are:
- `OPENCLAW_VERSION` (single source of truth for the openclaw npm version)
- `Dockerfile`
- `Dockerfile.alpine`

Common changes include:
- Bumping the `OPENCLAW_VERSION` to a newer npm release
- Changing the base Node.js image
- Adding extra tooling or configuration

### 4. Open a Pull Request

Push your branch and open a PR against `main`:

```bash
git push origin your-feature-or-fix
```

Then go to the repository on GitHub and click **"Compare & pull request"**.

Please include in your PR description:
- **What** changed and **why**
- Any relevant links (e.g. npm release notes for a version bump)

### 5. Versioning & releases

Once a PR is merged into `main`, GitHub Actions builds and pushes:
- Debian LTS tags: `latest`, `lts`, `${OPENCLAW_VERSION}`, `${OPENCLAW_VERSION}-lts`
- Alpine tags: `alpine`, `${OPENCLAW_VERSION}-alpine`

To publish a versioned release, a maintainer pushes a tag in the format `vYYYY.M.P` (e.g. `v2026.3.1`), which publishes:
- Debian: `vYYYY.M.P`, `YYYY.M.P`, `vYYYY.M.P-lts`, `YYYY.M.P-lts`
- Alpine: `vYYYY.M.P-alpine`, `YYYY.M.P-alpine`

## License

Expand Down
Loading