From 713b09a8c2a3ef9bb33a95b27072b5873017eabe Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 20 Mar 2026 09:49:02 -0400 Subject: [PATCH 1/2] feat: add ARM64/aarch64 multi-arch Docker image support Build amd64 and arm64 images in parallel on native GitHub runners (ubuntu-24.04 and ubuntu-24.04-arm) then combine them into a single multi-arch manifest. This enables running on Raspberry Pi and other ARM devices. Closes #416 --- .github/workflows/build-disk-collector.yml | 55 ++++++++++++++++++--- .github/workflows/build-primary-image.yml | 55 ++++++++++++++++++--- .github/workflows/build-sidecar-updater.yml | 55 ++++++++++++++++++--- 3 files changed, 147 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build-disk-collector.yml b/.github/workflows/build-disk-collector.yml index 7649ba5..27085fd 100644 --- a/.github/workflows/build-disk-collector.yml +++ b/.github/workflows/build-disk-collector.yml @@ -13,6 +13,9 @@ on: type: boolean default: false +env: + IMAGE: ghcr.io/crosstalk-solutions/project-nomad-disk-collector + jobs: check_authorization: name: Check authorization to publish new Docker image @@ -23,29 +26,69 @@ jobs: - name: check-auth id: check-auth run: echo "is_authorized=${{ contains(secrets.DEPLOYMENT_AUTHORIZED_USERS, github.triggering_actor) }}" >> $GITHUB_OUTPUT + build: - name: Build disk-collector image + name: Build Docker image (${{ matrix.platform }}) needs: check_authorization if: needs.check_authorization.outputs.isAuthorized == 'true' - runs-on: ubuntu-latest + runs-on: ${{ matrix.runner }} permissions: contents: read packages: write + strategy: + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-24.04 + suffix: amd64 + - platform: linux/arm64 + runner: ubuntu-24.04-arm + suffix: arm64 steps: - name: Checkout code uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Log in to GitHub Container Registry uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push uses: docker/build-push-action@v5 with: context: install/sidecar-disk-collector push: true - tags: | - ghcr.io/crosstalk-solutions/project-nomad-disk-collector:${{ inputs.version }} - ghcr.io/crosstalk-solutions/project-nomad-disk-collector:v${{ inputs.version }} - ${{ inputs.tag_latest && 'ghcr.io/crosstalk-solutions/project-nomad-disk-collector:latest' || '' }} + platforms: ${{ matrix.platform }} + tags: ${{ env.IMAGE }}:${{ inputs.version }}-${{ matrix.suffix }} + + manifest: + name: Create multi-arch manifest + needs: build + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Log in to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create and push manifest + run: | + TAGS="${{ env.IMAGE }}:${{ inputs.version }} ${{ env.IMAGE }}:v${{ inputs.version }}" + if [ "${{ inputs.tag_latest }}" = "true" ]; then + TAGS="$TAGS ${{ env.IMAGE }}:latest" + fi + for TAG in $TAGS; do + docker manifest create "$TAG" \ + "${{ env.IMAGE }}:${{ inputs.version }}-amd64" \ + "${{ env.IMAGE }}:${{ inputs.version }}-arm64" + docker manifest push "$TAG" + done diff --git a/.github/workflows/build-primary-image.yml b/.github/workflows/build-primary-image.yml index daf0e54..55f5721 100644 --- a/.github/workflows/build-primary-image.yml +++ b/.github/workflows/build-primary-image.yml @@ -13,6 +13,9 @@ on: type: boolean default: false +env: + IMAGE: ghcr.io/crosstalk-solutions/project-nomad + jobs: check_authorization: name: Check authorization to publish new Docker image @@ -23,32 +26,72 @@ jobs: - name: check-auth id: check-auth run: echo "is_authorized=${{ contains(secrets.DEPLOYMENT_AUTHORIZED_USERS, github.triggering_actor) }}" >> $GITHUB_OUTPUT + build: - name: Build Docker image + name: Build Docker image (${{ matrix.platform }}) needs: check_authorization if: needs.check_authorization.outputs.isAuthorized == 'true' - runs-on: ubuntu-latest + runs-on: ${{ matrix.runner }} permissions: contents: read packages: write + strategy: + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-24.04 + suffix: amd64 + - platform: linux/arm64 + runner: ubuntu-24.04-arm + suffix: arm64 steps: - name: Checkout code uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Log in to GitHub Container Registry uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push uses: docker/build-push-action@v5 with: push: true - tags: | - ghcr.io/crosstalk-solutions/project-nomad:${{ inputs.version }} - ghcr.io/crosstalk-solutions/project-nomad:v${{ inputs.version }} - ${{ inputs.tag_latest && 'ghcr.io/crosstalk-solutions/project-nomad:latest' || '' }} + platforms: ${{ matrix.platform }} + tags: ${{ env.IMAGE }}:${{ inputs.version }}-${{ matrix.suffix }} build-args: | VERSION=${{ inputs.version }} BUILD_DATE=${{ github.event.workflow_run.created_at }} VCS_REF=${{ github.sha }} + + manifest: + name: Create multi-arch manifest + needs: build + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Log in to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create and push manifest + run: | + TAGS="${{ env.IMAGE }}:${{ inputs.version }} ${{ env.IMAGE }}:v${{ inputs.version }}" + if [ "${{ inputs.tag_latest }}" = "true" ]; then + TAGS="$TAGS ${{ env.IMAGE }}:latest" + fi + for TAG in $TAGS; do + docker manifest create "$TAG" \ + "${{ env.IMAGE }}:${{ inputs.version }}-amd64" \ + "${{ env.IMAGE }}:${{ inputs.version }}-arm64" + docker manifest push "$TAG" + done diff --git a/.github/workflows/build-sidecar-updater.yml b/.github/workflows/build-sidecar-updater.yml index 822bc28..5ad28e8 100644 --- a/.github/workflows/build-sidecar-updater.yml +++ b/.github/workflows/build-sidecar-updater.yml @@ -13,6 +13,9 @@ on: type: boolean default: false +env: + IMAGE: ghcr.io/crosstalk-solutions/project-nomad-sidecar-updater + jobs: check_authorization: name: Check authorization to publish new Docker image @@ -23,29 +26,69 @@ jobs: - name: check-auth id: check-auth run: echo "is_authorized=${{ contains(secrets.DEPLOYMENT_AUTHORIZED_USERS, github.triggering_actor) }}" >> $GITHUB_OUTPUT + build: - name: Build sidecar-updater image + name: Build Docker image (${{ matrix.platform }}) needs: check_authorization if: needs.check_authorization.outputs.isAuthorized == 'true' - runs-on: ubuntu-latest + runs-on: ${{ matrix.runner }} permissions: contents: read packages: write + strategy: + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-24.04 + suffix: amd64 + - platform: linux/arm64 + runner: ubuntu-24.04-arm + suffix: arm64 steps: - name: Checkout code uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Log in to GitHub Container Registry uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push uses: docker/build-push-action@v5 with: context: install/sidecar-updater push: true - tags: | - ghcr.io/crosstalk-solutions/project-nomad-sidecar-updater:${{ inputs.version }} - ghcr.io/crosstalk-solutions/project-nomad-sidecar-updater:v${{ inputs.version }} - ${{ inputs.tag_latest && 'ghcr.io/crosstalk-solutions/project-nomad-sidecar-updater:latest' || '' }} + platforms: ${{ matrix.platform }} + tags: ${{ env.IMAGE }}:${{ inputs.version }}-${{ matrix.suffix }} + + manifest: + name: Create multi-arch manifest + needs: build + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Log in to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create and push manifest + run: | + TAGS="${{ env.IMAGE }}:${{ inputs.version }} ${{ env.IMAGE }}:v${{ inputs.version }}" + if [ "${{ inputs.tag_latest }}" = "true" ]; then + TAGS="$TAGS ${{ env.IMAGE }}:latest" + fi + for TAG in $TAGS; do + docker manifest create "$TAG" \ + "${{ env.IMAGE }}:${{ inputs.version }}-amd64" \ + "${{ env.IMAGE }}:${{ inputs.version }}-arm64" + docker manifest push "$TAG" + done From 239bd6307d074cb7198253f00844bdb36bc44a53 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Sat, 21 Mar 2026 19:22:13 -0400 Subject: [PATCH 2/2] chore: Update readme to add line about ARM images being community-contributed --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 89c2e17..9de76ee 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,8 @@ At it's core, however, N.O.M.A.D. is still very lightweight. For a barebones ins - OS: Debian-based (Ubuntu recommended) - Stable internet connection (required during install only) +*NOTE*: ARM images are community-contributed. Installing on ARM is not officially supported. + To run LLM's and other included AI tools: #### Optimal Specs @@ -165,4 +167,4 @@ sudo bash /opt/project-nomad/update_nomad.sh ###### Uninstall Script - Need to start fresh? Use the uninstall script to make your life easy. Note: this cannot be undone! ```bash curl -fsSL https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/main/install/uninstall_nomad.sh -o uninstall_nomad.sh && sudo bash uninstall_nomad.sh -``` \ No newline at end of file +```