name: Create and publish Docker images with specific build args on: workflow_dispatch: push: branches: - main - dev tags: - v* concurrency: group: docker-${{ github.ref }} cancel-in-progress: true env: REGISTRY: ghcr.io jobs: build: runs-on: ${{ matrix.platform.runner }} permissions: contents: read packages: write strategy: fail-fast: false matrix: platform: - arch: linux/amd64 runner: ubuntu-latest - arch: linux/arm64 runner: ubuntu-24.04-arm variant: - name: main suffix: "" build_args: "" free_disk: false - name: cuda suffix: "-cuda" build_args: "USE_CUDA=true" free_disk: true - name: cuda126 suffix: "-cuda126" build_args: | USE_CUDA=true USE_CUDA_VER=cu126 free_disk: true - name: ollama suffix: "-ollama" build_args: "USE_OLLAMA=true" free_disk: false - name: slim suffix: "-slim" build_args: "USE_SLIM=true" free_disk: false steps: - name: Prepare environment run: | echo "IMAGE_NAME=${GITHUB_REPOSITORY,,}" >> ${GITHUB_ENV} echo "FULL_IMAGE_NAME=${REGISTRY}/${GITHUB_REPOSITORY,,}" >> ${GITHUB_ENV} platform=${{ matrix.platform.arch }} echo "PLATFORM_PAIR=${platform//\//-}" >> ${GITHUB_ENV} - name: Free disk space if: matrix.variant.free_disk run: rm -rf /opt/hostedtoolcache - name: Checkout repository uses: actions/checkout@v5 - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to the Container registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata for Docker images id: meta uses: docker/metadata-action@v5 with: images: ${{ env.FULL_IMAGE_NAME }} tags: | type=ref,event=branch type=ref,event=tag type=sha,prefix=git- type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} ${{ matrix.variant.suffix != '' && format('type=raw,enable={0},prefix=,suffix=,value={1}', github.ref == 'refs/heads/main', matrix.variant.name) || '' }} flavor: | latest=${{ github.ref == 'refs/heads/main' }} ${{ matrix.variant.suffix != '' && format('suffix={0},onlatest=true', matrix.variant.suffix) || '' }} - name: Extract metadata for Docker cache id: cache-meta uses: docker/metadata-action@v5 with: images: ${{ env.FULL_IMAGE_NAME }} tags: | type=ref,event=branch ${{ github.ref_type == 'tag' && 'type=raw,value=main' || '' }} flavor: | prefix=cache-${{ matrix.variant.name }}-${{ matrix.platform.arch }}- latest=false - name: Build Docker image uses: docker/build-push-action@v5 id: build with: context: . push: true platforms: ${{ matrix.platform.arch }} labels: ${{ steps.meta.outputs.labels }} outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true cache-from: type=registry,ref=${{ steps.cache-meta.outputs.tags }} cache-to: type=registry,ref=${{ steps.cache-meta.outputs.tags }},mode=max sbom: true build-args: | BUILD_HASH=${{ github.sha }} ${{ matrix.variant.build_args }} - name: Export digest run: | mkdir -p /tmp/digests digest="${{ steps.build.outputs.digest }}" touch "/tmp/digests/${digest#sha256:}" - name: Upload digest uses: actions/upload-artifact@v4 with: name: digests-${{ matrix.variant.name }}-${{ env.PLATFORM_PAIR }} path: /tmp/digests/* if-no-files-found: error retention-days: 1 merge: runs-on: ubuntu-latest needs: [build] permissions: contents: read packages: write strategy: fail-fast: false matrix: variant: - name: main suffix: "" - name: cuda suffix: "-cuda" - name: cuda126 suffix: "-cuda126" - name: ollama suffix: "-ollama" - name: slim suffix: "-slim" steps: - name: Prepare environment run: | echo "IMAGE_NAME=${GITHUB_REPOSITORY,,}" >> ${GITHUB_ENV} echo "FULL_IMAGE_NAME=${REGISTRY}/${GITHUB_REPOSITORY,,}" >> ${GITHUB_ENV} - name: Download digests uses: actions/download-artifact@v5 with: pattern: digests-${{ matrix.variant.name }}-* path: /tmp/digests merge-multiple: true - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to the Container registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata for Docker images id: meta uses: docker/metadata-action@v5 with: images: ${{ env.FULL_IMAGE_NAME }} tags: | type=ref,event=branch type=ref,event=tag type=sha,prefix=git- type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} ${{ matrix.variant.suffix != '' && format('type=raw,enable={0},prefix=,suffix=,value={1}', github.ref == 'refs/heads/main', matrix.variant.name) || '' }} flavor: | latest=${{ github.ref == 'refs/heads/main' }} ${{ matrix.variant.suffix != '' && format('suffix={0},onlatest=true', matrix.variant.suffix) || '' }} - name: Create manifest list and push working-directory: /tmp/digests run: | docker buildx imagetools create \ $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf '${{ env.FULL_IMAGE_NAME }}@sha256:%s ' *) - name: Inspect image run: | docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }} copy-to-dockerhub: runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') needs: [merge] continue-on-error: true strategy: fail-fast: false matrix: include: - variant: main suffix: "" - variant: cuda suffix: "-cuda" - variant: cuda126 suffix: "-cuda126" - variant: ollama suffix: "-ollama" - variant: slim suffix: "-slim" steps: - name: Prepare environment run: | echo "IMAGE_NAME=${GITHUB_REPOSITORY,,}" >> ${GITHUB_ENV} echo "FULL_IMAGE_NAME=${REGISTRY}/${GITHUB_REPOSITORY,,}" >> ${GITHUB_ENV} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to the Container registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Log in to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Determine source and destination tags id: tags run: | DOCKERHUB_IMAGE="openwebui/open-webui" SUFFIX="${{ matrix.suffix }}" if [[ "${{ github.ref }}" == refs/tags/v* ]]; then VERSION="${{ github.ref_name }}" VERSION="${VERSION#v}" MAJOR_MINOR="${VERSION%.*}" echo "tags<> $GITHUB_OUTPUT echo "${VERSION}${SUFFIX}" >> $GITHUB_OUTPUT echo "${MAJOR_MINOR}${SUFFIX}" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT else if [ -z "$SUFFIX" ]; then echo "tags=latest" >> $GITHUB_OUTPUT else VARIANT_NAME="${SUFFIX#-}" echo "tags<> $GITHUB_OUTPUT echo "latest${SUFFIX}" >> $GITHUB_OUTPUT echo "${VARIANT_NAME}" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT fi fi echo "dockerhub_image=${DOCKERHUB_IMAGE}" >> $GITHUB_OUTPUT - name: Copy images from GHCR to Docker Hub run: | DOCKERHUB_IMAGE="${{ steps.tags.outputs.dockerhub_image }}" SUFFIX="${{ matrix.suffix }}" if [[ "${{ github.ref }}" == refs/tags/v* ]]; then VERSION="${{ github.ref_name }}" VERSION="${VERSION#v}" SOURCE_TAG="${VERSION}${SUFFIX}" else if [ -z "$SUFFIX" ]; then SOURCE_TAG="latest" else SOURCE_TAG="latest${SUFFIX}" fi fi SOURCE="${{ env.FULL_IMAGE_NAME }}:${SOURCE_TAG}" echo "Copying from ${SOURCE} to Docker Hub..." while IFS= read -r TAG; do [ -z "$TAG" ] && continue DEST="${DOCKERHUB_IMAGE}:${TAG}" echo " -> ${DEST}" docker buildx imagetools create -t "${DEST}" "${SOURCE}" done <<< "${{ steps.tags.outputs.tags }}"