Skip to content

Commit 84b38e4

Browse files
feat: add multi-arch ComfyUI image
1 parent c8c627a commit 84b38e4

6 files changed

Lines changed: 527 additions & 0 deletions

File tree

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
name: Build ComfyUI Image
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
comfyuiVersion:
7+
description: "ComfyUI stable release (latest, v0.33.1, or 0.33.1)"
8+
default: "latest"
9+
required: true
10+
type: string
11+
pushLatest:
12+
description: "Push latest when this is the newest upstream stable release"
13+
default: true
14+
required: true
15+
type: boolean
16+
17+
permissions:
18+
contents: read
19+
20+
concurrency:
21+
group: comfyui-release
22+
cancel-in-progress: false
23+
24+
env:
25+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
26+
IMAGE_NAME: 1panel/comfyui
27+
28+
jobs:
29+
resolve:
30+
runs-on: ubuntu-24.04
31+
outputs:
32+
version: ${{ steps.release.outputs.version }}
33+
release_tag: ${{ steps.release.outputs.release_tag }}
34+
revision: ${{ steps.release.outputs.revision }}
35+
push_latest: ${{ steps.release.outputs.push_latest }}
36+
steps:
37+
- name: Checkout
38+
uses: actions/checkout@v6
39+
40+
- name: Resolve and verify ComfyUI release
41+
id: release
42+
shell: bash
43+
env:
44+
GH_TOKEN: ${{ github.token }}
45+
INPUT_COMFYUI_VERSION: ${{ inputs.comfyuiVersion }}
46+
INPUT_PUSH_LATEST: ${{ inputs.pushLatest }}
47+
run: |
48+
set -euo pipefail
49+
50+
requested="${INPUT_COMFYUI_VERSION}"
51+
if [ "${requested}" = "latest" ]; then
52+
release_url="https://api.github.com/repos/Comfy-Org/ComfyUI/releases/latest"
53+
else
54+
version="${requested#v}"
55+
if [[ ! "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
56+
echo "::error::Invalid ComfyUI release version: ${requested}"
57+
exit 1
58+
fi
59+
release_url="https://api.github.com/repos/Comfy-Org/ComfyUI/releases/tags/v${version}"
60+
fi
61+
62+
release_json="$(mktemp)"
63+
curl -fsSL --retry 5 --retry-delay 3 --retry-all-errors \
64+
-H "Accept: application/vnd.github+json" \
65+
-H "Authorization: Bearer ${GH_TOKEN}" \
66+
-H "X-GitHub-Api-Version: 2022-11-28" \
67+
"${release_url}" -o "${release_json}"
68+
69+
release_tag="$(jq -r '.tag_name' "${release_json}")"
70+
if [ "$(jq -r '.draft' "${release_json}")" != "false" ] || \
71+
[ "$(jq -r '.prerelease' "${release_json}")" != "false" ]; then
72+
echo "::error::${release_tag} is not a published stable release"
73+
exit 1
74+
fi
75+
76+
version="${release_tag#v}"
77+
if [[ "${release_tag}" != "v${version}" ]] || \
78+
[[ ! "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
79+
echo "::error::Unexpected upstream release tag: ${release_tag}"
80+
exit 1
81+
fi
82+
83+
direct_revision="$(git ls-remote https://github.com/Comfy-Org/ComfyUI.git "refs/tags/${release_tag}" | awk 'NR == 1 { print $1 }')"
84+
peeled_revision="$(git ls-remote https://github.com/Comfy-Org/ComfyUI.git "refs/tags/${release_tag}^{}" | awk 'NR == 1 { print $1 }')"
85+
revision="${peeled_revision:-${direct_revision}}"
86+
if [[ ! "${revision}" =~ ^[0-9a-f]{40}$ ]]; then
87+
echo "::error::Unable to resolve commit for ${release_tag}"
88+
exit 1
89+
fi
90+
91+
latest_json="$(mktemp)"
92+
curl -fsSL --retry 5 --retry-delay 3 --retry-all-errors \
93+
-H "Accept: application/vnd.github+json" \
94+
-H "Authorization: Bearer ${GH_TOKEN}" \
95+
-H "X-GitHub-Api-Version: 2022-11-28" \
96+
"https://api.github.com/repos/Comfy-Org/ComfyUI/releases/latest" \
97+
-o "${latest_json}"
98+
latest_release_tag="$(jq -r '.tag_name' "${latest_json}")"
99+
100+
{
101+
echo "version=${version}"
102+
echo "release_tag=${release_tag}"
103+
echo "revision=${revision}"
104+
if [ "${INPUT_PUSH_LATEST}" = "true" ] && [ "${release_tag}" = "${latest_release_tag}" ]; then
105+
echo "push_latest=true"
106+
elif [ "${INPUT_PUSH_LATEST}" = "true" ]; then
107+
echo "::warning::Skipping latest because ${release_tag} is not upstream latest (${latest_release_tag})" >&2
108+
echo "push_latest=false"
109+
else
110+
echo "push_latest=false"
111+
fi
112+
} >> "${GITHUB_OUTPUT}"
113+
114+
build-amd64:
115+
needs: resolve
116+
runs-on: ubuntu-24.04
117+
outputs:
118+
digest: ${{ steps.build.outputs.digest }}
119+
steps:
120+
- name: Checkout
121+
uses: actions/checkout@v6
122+
123+
- name: Set up Docker Buildx
124+
uses: docker/setup-buildx-action@v4
125+
126+
- name: Login to Docker Hub
127+
uses: docker/login-action@v4
128+
with:
129+
username: ${{ secrets.DOCKERHUB_USERNAME }}
130+
password: ${{ secrets.DOCKERHUB_PASSWORD }}
131+
132+
- name: Build and push amd64 image
133+
id: build
134+
uses: docker/build-push-action@v7
135+
with:
136+
context: comfyui
137+
file: comfyui/Dockerfile
138+
platforms: linux/amd64
139+
build-args: |
140+
COMFYUI_VERSION=${{ needs.resolve.outputs.version }}
141+
COMFYUI_REVISION=${{ needs.resolve.outputs.revision }}
142+
outputs: type=image,name=${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
143+
144+
build-arm64:
145+
needs: resolve
146+
runs-on: ubuntu-24.04-arm
147+
outputs:
148+
digest: ${{ steps.build.outputs.digest }}
149+
steps:
150+
- name: Checkout
151+
uses: actions/checkout@v6
152+
153+
- name: Set up Docker Buildx
154+
uses: docker/setup-buildx-action@v4
155+
156+
- name: Login to Docker Hub
157+
uses: docker/login-action@v4
158+
with:
159+
username: ${{ secrets.DOCKERHUB_USERNAME }}
160+
password: ${{ secrets.DOCKERHUB_PASSWORD }}
161+
162+
- name: Build and push arm64 image
163+
id: build
164+
uses: docker/build-push-action@v7
165+
with:
166+
context: comfyui
167+
file: comfyui/Dockerfile
168+
platforms: linux/arm64
169+
build-args: |
170+
COMFYUI_VERSION=${{ needs.resolve.outputs.version }}
171+
COMFYUI_REVISION=${{ needs.resolve.outputs.revision }}
172+
outputs: type=image,name=${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
173+
174+
create-manifest:
175+
needs: [resolve, build-amd64, build-arm64]
176+
runs-on: ubuntu-24.04
177+
steps:
178+
- name: Set up Docker Buildx
179+
uses: docker/setup-buildx-action@v4
180+
181+
- name: Login to Docker Hub
182+
uses: docker/login-action@v4
183+
with:
184+
username: ${{ secrets.DOCKERHUB_USERNAME }}
185+
password: ${{ secrets.DOCKERHUB_PASSWORD }}
186+
187+
- name: Create and verify multi-platform manifest
188+
shell: bash
189+
env:
190+
GH_TOKEN: ${{ github.token }}
191+
IMAGE: ${{ env.IMAGE_NAME }}
192+
VERSION: ${{ needs.resolve.outputs.version }}
193+
RELEASE_TAG: ${{ needs.resolve.outputs.release_tag }}
194+
PUSH_LATEST: ${{ needs.resolve.outputs.push_latest }}
195+
AMD64_DIGEST: ${{ needs.build-amd64.outputs.digest }}
196+
ARM64_DIGEST: ${{ needs.build-arm64.outputs.digest }}
197+
CANDIDATE_TAG: candidate-${{ github.run_id }}-${{ github.run_attempt }}
198+
run: |
199+
set -euo pipefail
200+
201+
for digest in "${AMD64_DIGEST}" "${ARM64_DIGEST}"; do
202+
if [[ ! "${digest}" =~ ^sha256:[0-9a-f]{64}$ ]]; then
203+
echo "::error::Invalid image digest: ${digest}"
204+
exit 1
205+
fi
206+
done
207+
208+
verify_manifest() {
209+
local image_ref="$1"
210+
local manifest_json
211+
manifest_json="$(docker buildx imagetools inspect --format '{{json .Manifest}}' "${image_ref}")"
212+
printf '%s\n' "${manifest_json}" | jq -e '
213+
(.digest | test("^sha256:[0-9a-f]{64}$")) and
214+
any(.manifests[]; .platform.os == "linux" and .platform.architecture == "amd64") and
215+
any(.manifests[]; .platform.os == "linux" and .platform.architecture == "arm64") and
216+
all(.manifests[];
217+
.platform.os != "linux" or
218+
.platform.architecture == "amd64" or
219+
.platform.architecture == "arm64"
220+
)
221+
' >/dev/null
222+
}
223+
224+
candidate="${IMAGE}:${CANDIDATE_TAG}"
225+
docker buildx imagetools create \
226+
-t "${candidate}" \
227+
"${IMAGE}@${AMD64_DIGEST}" \
228+
"${IMAGE}@${ARM64_DIGEST}"
229+
verify_manifest "${candidate}"
230+
231+
candidate_json="$(docker buildx imagetools inspect --format '{{json .Manifest}}' "${candidate}")"
232+
candidate_digest="$(printf '%s\n' "${candidate_json}" | jq -r '.digest')"
233+
publish_args=(
234+
--tag "${IMAGE}:${RELEASE_TAG}"
235+
--tag "${IMAGE}:${VERSION}"
236+
)
237+
publish_latest=false
238+
239+
if [ "${PUSH_LATEST}" = "true" ]; then
240+
current_latest="$(
241+
curl -fsSL --retry 5 --retry-delay 3 --retry-all-errors \
242+
-H "Accept: application/vnd.github+json" \
243+
-H "Authorization: Bearer ${GH_TOKEN}" \
244+
-H "X-GitHub-Api-Version: 2022-11-28" \
245+
"https://api.github.com/repos/Comfy-Org/ComfyUI/releases/latest" \
246+
| jq -r '.tag_name'
247+
)"
248+
if [ "${RELEASE_TAG}" = "${current_latest}" ]; then
249+
publish_args+=(--tag "${IMAGE}:latest")
250+
publish_latest=true
251+
else
252+
echo "::warning::Skipping latest because upstream advanced to ${current_latest} during the build"
253+
fi
254+
fi
255+
256+
docker buildx imagetools create \
257+
"${publish_args[@]}" \
258+
"${IMAGE}@${candidate_digest}"
259+
260+
verify_manifest "${IMAGE}:${RELEASE_TAG}"
261+
verify_manifest "${IMAGE}:${VERSION}"
262+
if [ "${publish_latest}" = "true" ]; then
263+
verify_manifest "${IMAGE}:latest"
264+
fi

‎README.md‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ This repository contains runtime images used by 1Panel.
66

77
Current images in this repository:
88

9+
- `1panel/comfyui`
910
- `1panel/hermes-agent`
1011
- `1panel/openclaw`
1112
- `1panel/node`
@@ -14,8 +15,32 @@ Current images in this repository:
1415

1516
## Images
1617

18+
- `1panel/comfyui`: ComfyUI runtime image for NVIDIA GPUs, built from matching upstream stable releases
1719
- `1panel/hermes-agent`: Hermes Agent runtime image maintained by 1Panel with bundled messaging channel dependencies
1820
- `1panel/openclaw`: OpenClaw runtime image built from official source with 1Panel-oriented customizations
1921
- `1panel/node`: Node.js runtime image
2022
- `1panel/php`: PHP-FPM runtime images for multiple versions
2123
- `1panel/java`: Java JDK runtime images based on Eclipse Temurin
24+
25+
## ComfyUI
26+
27+
The ComfyUI image supports `linux/amd64` and `linux/arm64` NVIDIA hosts and includes the same pinned PyTorch 2.13.0 CUDA 13.0 stack on both architectures. GPU operation requires a compatible NVIDIA driver (Linux 580.65.06 or newer for the current CUDA stack) and NVIDIA Container Toolkit. Each upstream stable release is published as one multi-platform image under its exact release tag plus a tag without the leading `v` (for example, `1panel/comfyui:v0.33.1` and `1panel/comfyui:0.33.1`). Docker automatically selects the matching architecture.
28+
29+
Start the container with Docker:
30+
31+
```bash
32+
docker run -d \
33+
--name comfyui \
34+
--gpus all \
35+
-p 8188:8188 \
36+
-v comfyui-data:/data \
37+
1panel/comfyui:v0.33.1
38+
```
39+
40+
This example exposes port `8188`, stores persistent data in the `comfyui-data` volume, and requests all NVIDIA GPUs. Change the image tag, host port, or volume name directly in the command when needed.
41+
42+
Models, custom nodes, inputs, outputs, user settings, and runtime-installed Python packages are stored under `/data`. ComfyUI-Manager dependencies are included; enable the manager when needed by appending `--enable-manager` to the command.
43+
44+
For CPU-only operation, omit `--gpus all` and append `--cpu` after the image name. CPU mode works on both architectures but is significantly slower.
45+
46+
The amd64 CUDA variant is intended primarily for RTX 20 series and newer GPUs. The arm64 variant targets NVIDIA's server/SBSA CUDA platforms; it must not be assumed to support every ARM device or Jetson model, which follows NVIDIA's JetPack-specific PyTorch installation path.

‎README_zh.md‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
当前仓库主要维护以下镜像:
88

9+
- `1panel/comfyui`
910
- `1panel/hermes-agent`
1011
- `1panel/openclaw`
1112
- `1panel/node`
@@ -14,8 +15,32 @@
1415

1516
## 镜像说明
1617

18+
- `1panel/comfyui`:面向 NVIDIA GPU、严格基于上游稳定 Release 构建的 ComfyUI 运行环境镜像
1719
- `1panel/hermes-agent`:1Panel 维护的 Hermes Agent 运行环境镜像,内置消息通道相关依赖
1820
- `1panel/openclaw`:基于 OpenClaw 官方源码构建的运行环境镜像,包含面向 1Panel 场景的定制
1921
- `1panel/node`:Node.js 运行环境镜像
2022
- `1panel/php`:多个版本的 PHP-FPM 运行环境镜像
2123
- `1panel/java`:基于 Eclipse Temurin 的 Java JDK 运行环境镜像
24+
25+
## ComfyUI
26+
27+
ComfyUI 镜像支持 `linux/amd64` 和 `linux/arm64` NVIDIA 主机,两种架构均内置相同版本并锁定的 PyTorch 2.13.0 CUDA 13.0 依赖。GPU 模式要求宿主机安装兼容的 NVIDIA 驱动(当前 CUDA 依赖在 Linux 上要求 580.65.06 或更新版本)和 NVIDIA Container Toolkit。每个上游稳定 Release 都会以同一个多架构镜像发布同名标签和去掉 `v` 前缀的兼容标签(例如 `1panel/comfyui:v0.33.1` 与 `1panel/comfyui:0.33.1`),Docker 会自动选择与宿主机匹配的架构。
28+
29+
使用 Docker 启动容器:
30+
31+
```bash
32+
docker run -d \
33+
--name comfyui \
34+
--gpus all \
35+
-p 8188:8188 \
36+
-v comfyui-data:/data \
37+
1panel/comfyui:v0.33.1
38+
```
39+
40+
该示例对外提供 `8188` 端口,将持久化数据保存在 `comfyui-data` 卷中,并申请宿主机上的全部 NVIDIA GPU。需要时可直接修改命令中的镜像标签、宿主机端口或卷名。
41+
42+
模型、自定义节点、输入输出、用户配置以及运行时安装的 Python 包都会保存在 `/data`。镜像已安装 ComfyUI-Manager 依赖,如需启用,可在命令末尾追加 `--enable-manager`。
43+
44+
如需在纯 CPU 主机运行,请移除 `--gpus all`,并在镜像名称后追加 `--cpu`。两种架构都可使用 CPU 模式,但运行速度会明显降低。
45+
46+
amd64 CUDA 镜像主要面向 RTX 20 系列及更新显卡;arm64 镜像面向 NVIDIA 服务器/SBSA CUDA 平台,不能据此认为所有 ARM 设备或 Jetson 型号都受支持,Jetson 需要按 NVIDIA 的 JetPack 专用 PyTorch 路线部署。

‎comfyui/.dockerignore‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*
2+
!Dockerfile
3+
!docker-entrypoint.sh

0 commit comments

Comments
 (0)