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
218 changes: 176 additions & 42 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,33 @@ env:
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write

strategy:
fail-fast: false
matrix:
variant:
- name: cpu
include:
# CPU variants for both architectures
- variant: cpu
arch: x86_64-linux
nix_target: dockerImage
platform: linux/amd64
tag_suffix: ''
- name: cuda
- variant: cpu
arch: aarch64-linux
nix_target: dockerImage
platform: linux/arm64
tag_suffix: ''
# CUDA only for x86_64 (NVIDIA CUDA not available for ARM)
- variant: cuda
arch: x86_64-linux
nix_target: dockerImageCuda
platform: linux/amd64
tag_suffix: '-cuda'

steps:
Expand All @@ -51,13 +63,22 @@ jobs:
with:
fetch-depth: 0

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

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

- name: Install Nix
uses: cachix/install-nix-action@v31
with:
nix_path: nixpkgs=channel:nixos-unstable
extra_nix_config: |
experimental-features = nix-command flakes
accept-flake-config = true
extra-platforms = aarch64-linux

- name: Setup Cachix
uses: cachix/cachix-action@v15
Expand All @@ -81,80 +102,193 @@ jobs:
VERSION=$(grep -m1 'comfyuiVersion = ' flake.nix | sed 's/.*"\(.*\)".*/\1/')
echo "version=$VERSION" >> $GITHUB_OUTPUT

# Generate Docker tags
TAGS=""
# Determine architecture suffix for intermediate tags
if [[ "${{ matrix.arch }}" == "aarch64-linux" ]]; then
ARCH_SUFFIX="-arm64"
else
ARCH_SUFFIX="-amd64"
fi
echo "arch_suffix=$ARCH_SUFFIX" >> $GITHUB_OUTPUT

# Generate Docker tags for this specific architecture
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
TAGS="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest${{ matrix.variant.tag_suffix }}"
TAGS="$TAGS,${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${VERSION}${{ matrix.variant.tag_suffix }}"
TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest${{ matrix.tag_suffix }}${ARCH_SUFFIX}"
elif [[ "${{ github.ref }}" == refs/tags/v* ]]; then
TAG_VERSION=${GITHUB_REF#refs/tags/v}
TAGS="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG_VERSION}${{ matrix.variant.tag_suffix }}"
TAGS="$TAGS,${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest${{ matrix.variant.tag_suffix }}"
TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG_VERSION}${{ matrix.tag_suffix }}${ARCH_SUFFIX}"
else
TAGS="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:pr-${{ github.event.pull_request.number }}${{ matrix.variant.tag_suffix }}"
TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:pr-${{ github.event.pull_request.number }}${{ matrix.tag_suffix }}${ARCH_SUFFIX}"
fi
echo "tags=$TAGS" >> $GITHUB_OUTPUT

# Generate labels
echo "created=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
echo "revision=${{ github.sha }}" >> $GITHUB_OUTPUT
echo "tag=$TAG" >> $GITHUB_OUTPUT

- name: Build Docker image with Nix
run: |
echo "Building ${{ matrix.variant.name }} variant..."
nix build .#${{ matrix.variant.nix_target }} --print-build-logs
echo "Building ${{ matrix.variant }} variant for ${{ matrix.arch }}..."
nix build .#packages.${{ matrix.arch }}.${{ matrix.nix_target }} --print-build-logs

# Load the image into Docker
docker load < result

# Show loaded image
docker images | grep comfy-ui

- name: Tag and push Docker images
- name: Tag and push Docker image
if: github.event_name != 'pull_request'
run: |
# Get the image ID from the loaded image
if [[ "${{ matrix.variant.name }}" == "cuda" ]]; then
if [[ "${{ matrix.variant }}" == "cuda" ]]; then
IMAGE_ID=$(docker images comfy-ui:cuda -q | head -n1)
else
IMAGE_ID=$(docker images comfy-ui:latest -q | head -n1)
fi

# Tag and push each tag
IFS=',' read -ra TAG_ARRAY <<< "${{ steps.meta.outputs.tags }}"
for tag in "${TAG_ARRAY[@]}"; do
echo "Tagging and pushing: $tag"
docker tag "$IMAGE_ID" "$tag"
docker push "$tag"
echo "Tagging and pushing: ${{ steps.meta.outputs.tag }}"
docker tag "$IMAGE_ID" "${{ steps.meta.outputs.tag }}"
docker push "${{ steps.meta.outputs.tag }}"

- name: Generate build summary
run: |
echo "## Docker Image Built (${{ matrix.variant }} - ${{ matrix.arch }})" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version:** ${{ steps.meta.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "**Variant:** ${{ matrix.variant }}" >> $GITHUB_STEP_SUMMARY
echo "**Architecture:** ${{ matrix.arch }}" >> $GITHUB_STEP_SUMMARY
echo "**Platform:** ${{ matrix.platform }}" >> $GITHUB_STEP_SUMMARY

# Create and push multi-arch manifests after all builds complete
create-manifests:
needs: build
runs-on: ubuntu-latest
if: github.event_name != 'pull_request'
permissions:
packages: write

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

- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
run: |
VERSION=$(grep -m1 'comfyuiVersion = ' flake.nix | sed 's/.*"\(.*\)".*/\1/')
echo "version=$VERSION" >> $GITHUB_OUTPUT

- name: Create and push multi-arch manifest (CPU)
run: |
# Determine tags based on ref
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
MANIFEST_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest"
VERSION_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}"
AMD64_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-amd64"
ARM64_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-arm64"
elif [[ "${{ github.ref }}" == refs/tags/v* ]]; then
TAG_VERSION=${GITHUB_REF#refs/tags/v}
MANIFEST_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG_VERSION}"
VERSION_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest"
AMD64_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG_VERSION}-amd64"
ARM64_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG_VERSION}-arm64"
fi

# Verify both architecture images exist before creating manifest
echo "Verifying architecture-specific images exist..."
for arch_tag in "$AMD64_TAG" "$ARM64_TAG"; do
if ! docker manifest inspect "$arch_tag" &>/dev/null; then
echo "ERROR: Required image not found: $arch_tag"
echo "This likely means the build job for this architecture failed."
exit 1
fi
echo "Found: $arch_tag"
done

- name: Generate image summary
if: github.event_name != 'pull_request'
# Create multi-arch manifest for CPU variant
echo "Creating multi-arch manifest: $MANIFEST_TAG"
docker manifest create "$MANIFEST_TAG" \
"$AMD64_TAG" \
"$ARM64_TAG"

docker manifest annotate "$MANIFEST_TAG" "$AMD64_TAG" --arch amd64
docker manifest annotate "$MANIFEST_TAG" "$ARM64_TAG" --arch arm64

docker manifest push "$MANIFEST_TAG"

# Also push version tag if on main
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "Creating multi-arch manifest: $VERSION_TAG"
docker manifest create "$VERSION_TAG" "$AMD64_TAG" "$ARM64_TAG"

docker manifest annotate "$VERSION_TAG" "$AMD64_TAG" --arch amd64
docker manifest annotate "$VERSION_TAG" "$ARM64_TAG" --arch arm64

docker manifest push "$VERSION_TAG"
fi

- name: Create and push CUDA manifest (x86_64 only)
run: |
# CUDA is only available for x86_64, so no multi-arch manifest needed
# Just create alias tags for consistency
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
CUDA_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-cuda"
VERSION_CUDA_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}-cuda"
CUDA_AMD64_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-cuda-amd64"
elif [[ "${{ github.ref }}" == refs/tags/v* ]]; then
TAG_VERSION=${GITHUB_REF#refs/tags/v}
CUDA_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG_VERSION}-cuda"
VERSION_CUDA_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-cuda"
CUDA_AMD64_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG_VERSION}-cuda-amd64"
fi

# Verify CUDA image exists before creating alias tags
echo "Verifying CUDA image exists..."
if ! docker manifest inspect "$CUDA_AMD64_TAG" &>/dev/null; then
echo "ERROR: Required CUDA image not found: $CUDA_AMD64_TAG"
echo "This likely means the CUDA build job failed."
exit 1
fi
echo "Found: $CUDA_AMD64_TAG"

# Pull and retag for alias
docker pull "$CUDA_AMD64_TAG"

docker tag "$CUDA_AMD64_TAG" "$CUDA_TAG"
docker push "$CUDA_TAG"

docker tag "$CUDA_AMD64_TAG" "$VERSION_CUDA_TAG"
docker push "$VERSION_CUDA_TAG"

- name: Generate manifest summary
run: |
echo "## Docker Image Published (${{ matrix.variant.name }})" >> $GITHUB_STEP_SUMMARY
echo "## Multi-Architecture Docker Images Published" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version:** ${{ steps.meta.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "**Variant:** ${{ matrix.variant.name }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Pull Commands:" >> $GITHUB_STEP_SUMMARY
echo "### CPU (Multi-arch: amd64 + arm64)" >> $GITHUB_STEP_SUMMARY
echo '```bash' >> $GITHUB_STEP_SUMMARY
IFS=',' read -ra TAG_ARRAY <<< "${{ steps.meta.outputs.tags }}"
for tag in "${TAG_ARRAY[@]}"; do
echo "docker pull $tag" >> $GITHUB_STEP_SUMMARY
done
echo "docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Run Command:" >> $GITHUB_STEP_SUMMARY
echo "### CUDA (amd64 only)" >> $GITHUB_STEP_SUMMARY
echo '```bash' >> $GITHUB_STEP_SUMMARY
if [[ "${{ matrix.variant.name }}" == "cuda" ]]; then
echo "docker run --gpus all -p 8188:8188 -v \$PWD/data:/data ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-cuda" >> $GITHUB_STEP_SUMMARY
else
echo "docker run -p 8188:8188 -v \$PWD/data:/data ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" >> $GITHUB_STEP_SUMMARY
fi
echo "docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-cuda" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Run Commands" >> $GITHUB_STEP_SUMMARY
echo '```bash' >> $GITHUB_STEP_SUMMARY
echo "# CPU (works on both amd64 and arm64)" >> $GITHUB_STEP_SUMMARY
echo "docker run -p 8188:8188 -v \$PWD/data:/data ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "# CUDA (amd64 only)" >> $GITHUB_STEP_SUMMARY
echo "docker run --gpus all -p 8188:8188 -v \$PWD/data:/data ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-cuda" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY

update-description:
needs: build-and-push
needs: create-manifests
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
permissions:
Expand Down
7 changes: 5 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,13 @@ custom_nodes/ - Persistent custom node installations
#### Docker Image Publishing (`.github/workflows/docker.yml`)
- **Purpose**: Automatically build and publish Docker images to GitHub Container Registry
- **Triggers**: Push to main, version tags (v*), pull requests
- **Build Matrix**: Both CPU and CUDA variants built in parallel
- **Multi-Architecture**: CPU images built for both amd64 and arm64 (via QEMU emulation)
- **Build Matrix**: CPU (multi-arch) and CUDA (x86_64 only) variants built in parallel
- **Outputs**: Images published to `ghcr.io/utensils/comfyui-nix`
- **Tags**:
- Main branch: `latest`, `X.Y.Z` (from flake.nix)
- Version tags: `vX.Y.Z`, `latest`
- Architecture-specific: `latest-amd64`, `latest-arm64`
- Pull requests: `pr-N` (build only, no push)
- **Caching**: Uses Cachix for Nix build caching (requires `CACHIX_AUTH_TOKEN` secret)

Expand All @@ -126,7 +128,8 @@ custom_nodes/ - Persistent custom node installations
- **Location**: GitHub Container Registry (ghcr.io)
- **Public Access**: All images are publicly readable
- **Namespace**: `ghcr.io/utensils/comfyui-nix`
- **Variants**: CPU (`:latest`) and CUDA (`:latest-cuda`)
- **Variants**: CPU (`:latest`, multi-arch) and CUDA (`:latest-cuda`, x86_64 only)
- **Architectures**: amd64 (x86_64) and arm64 (aarch64/Apple Silicon) for CPU images

## Code Style Guidelines

Expand Down
38 changes: 29 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,23 +246,23 @@ This structure ensures clear separation of concerns and makes the codebase easie

## Docker Support

This flake includes Docker support for running ComfyUI in a containerized environment while preserving all functionality. Both CPU and CUDA-enabled GPU images are available.
This flake includes Docker support for running ComfyUI in a containerized environment while preserving all functionality. Multi-architecture images are available for both x86_64 (amd64) and ARM64 (aarch64) platforms.

### Pre-built Images (GitHub Container Registry)

Pre-built Docker images are automatically published to GitHub Container Registry on every release. This is the easiest way to get started:

#### Pull and Run CPU Version
#### Pull and Run CPU Version (Multi-arch: amd64 + arm64)

```bash
# Pull the latest CPU version
# Pull the latest CPU version (automatically selects correct architecture)
docker pull ghcr.io/utensils/comfyui-nix:latest

# Run the container
docker run -p 8188:8188 -v "$PWD/data:/data" ghcr.io/utensils/comfyui-nix:latest
```

#### Pull and Run CUDA (GPU) Version
#### Pull and Run CUDA (GPU) Version (x86_64 only)

```bash
# Pull the latest CUDA version
Expand All @@ -274,13 +274,31 @@ docker run --gpus all -p 8188:8188 -v "$PWD/data:/data" ghcr.io/utensils/comfyui

#### Available Tags

- `latest` - Latest CPU version from main branch
- `latest-cuda` - Latest CUDA version from main branch
- `X.Y.Z` - Specific version (CPU)
- `X.Y.Z-cuda` - Specific version (CUDA)
- `latest` - Latest CPU version, multi-arch (amd64 + arm64)
- `latest-cuda` - Latest CUDA version (x86_64/amd64 only)
- `latest-amd64` - Latest CPU version for x86_64
- `latest-arm64` - Latest CPU version for ARM64
- `X.Y.Z` - Specific version (CPU, multi-arch)
- `X.Y.Z-cuda` - Specific version (CUDA, x86_64 only)

Visit the [packages page](https://github.com/utensils/comfyui-nix/pkgs/container/comfyui-nix) to see all available versions.

### Apple Silicon (M1/M2/M3) Support

The `latest` and `latest-arm64` tags work on Apple Silicon Macs via Docker Desktop:

```bash
# Works on Apple Silicon Macs
docker run -p 8188:8188 -v "$PWD/data:/data" ghcr.io/utensils/comfyui-nix:latest
```

**Important**: Docker containers on macOS cannot access the Metal GPU (MPS). The Docker image runs **CPU-only** on Apple Silicon. For GPU acceleration on Apple Silicon, use `nix run` directly instead of Docker:

```bash
# For GPU acceleration on Apple Silicon, use nix directly (not Docker)
nix run github:utensils/comfyui-nix
```

### Building the Docker Image Locally

#### CPU Version
Expand Down Expand Up @@ -374,11 +392,13 @@ The Docker image follows the same modular structure as the regular installation,
Docker images are automatically built and published to GitHub Container Registry via GitHub Actions:

- **Trigger events**: Push to main branch, version tags (v*), and pull requests
- **Build matrix**: Both CPU and CUDA variants are built in parallel
- **Multi-architecture**: CPU images built for both amd64 and arm64 (via QEMU emulation)
- **Build matrix**: CPU (multi-arch) and CUDA (x86_64 only) variants built in parallel
- **Tagging strategy**:
- Main branch pushes: `latest` and `X.Y.Z` (version from flake.nix)
- Version tags: `vX.Y.Z` and `latest`
- Pull requests: `pr-N` (for testing, not pushed to registry)
- Architecture-specific: `latest-amd64`, `latest-arm64`
- **Registry**: All images are publicly accessible at `ghcr.io/utensils/comfyui-nix`
- **Build cache**: Nix builds are cached using Cachix for faster CI runs

Expand Down
Loading