CI/CD¶
The Docker image is built and pushed automatically by a GitHub Actions workflow on every qualifying push to main.
Workflow File¶
The workflow is defined in .github/workflows/build.yml.
Triggers¶
Push to main¶
The workflow runs on every push to the main branch, except when the push only modifies files in the ignored paths:
paths-ignore:
- 'workflows/**'
- 'backend/**'
- 'frontend/**'
- 'catalogs/**'
- 'chrome-extension/**'
- 'version.json'
- '*.md'
- 'docs/**'
- 'mkdocs.yml'
Safe to push without triggering a build:
| Path | Content |
|---|---|
backend/** |
Python backend code |
frontend/** |
HTML/JS/CSS frontend |
catalogs/** |
Model, LoRA, and LLM catalogs |
workflows/** |
Workflow definitions and manifests |
chrome-extension/** |
Chrome browser extension |
version.json |
Version metadata |
*.md |
Markdown files (README, etc.) |
docs/** |
Documentation site |
mkdocs.yml |
MkDocs configuration |
These paths are excluded because their content is delivered via the git-based update mechanism at runtime, not baked into the Docker image.
Will trigger a build:
| Path | Content |
|---|---|
docker/* |
Dockerfile, start.sh, bootstrap.py, nodes.txt, install_nodes.sh, configure.sh |
.github/workflows/* |
CI/CD workflow definitions |
Manual Dispatch¶
The workflow also supports workflow_dispatch, allowing manual triggers from the GitHub Actions UI. This is useful for rebuilding the image after upstream dependency changes (e.g., a new PyTorch release) without modifying any files.
Image Registry¶
Images are pushed to the GitHub Container Registry (GHCR):
Tags¶
Every build produces two tags:
| Tag | Format | Example | Purpose |
|---|---|---|---|
latest |
Fixed | ghcr.io/diego-devita/comfyui-studio:latest |
Always points to the most recent build from main |
sha-<commit> |
Per-commit | ghcr.io/diego-devita/comfyui-studio:sha-a1b2c3d |
Immutable reference to a specific build |
The latest tag is only applied when building from the default branch (main). The SHA tag uses the short commit hash.
Tags and labels are generated by the docker/metadata-action@v5 action:
Build Configuration¶
Build Arguments¶
The workflow passes build arguments to the Dockerfile, sourced from GitHub Actions repository variables with fallback defaults:
build-args: |
CUDA_VERSION=${{ vars.CUDA_VERSION || '12.8.1' }}
PYTORCH_INDEX=${{ vars.PYTORCH_INDEX || 'cu128' }}
PYTHON_VERSION=${{ vars.PYTHON_VERSION || '3.12' }}
ENABLE_SAGE_ATTENTION=${{ vars.ENABLE_SAGE_ATTENTION || 'true' }}
ENABLE_FLASH_ATTENTION=${{ vars.ENABLE_FLASH_ATTENTION || 'false' }}
| Variable | Default | Notes |
|---|---|---|
CUDA_VERSION |
12.8.1 |
Override via GitHub repo variable vars.CUDA_VERSION |
PYTORCH_INDEX |
cu128 |
Override via GitHub repo variable vars.PYTORCH_INDEX |
PYTHON_VERSION |
3.12 |
Override via GitHub repo variable vars.PYTHON_VERSION |
ENABLE_SAGE_ATTENTION |
true |
Override via GitHub repo variable vars.ENABLE_SAGE_ATTENTION |
ENABLE_FLASH_ATTENTION |
false |
Defaults to false in CI |
FlashAttention defaults to false in CI
The Dockerfile default for ENABLE_FLASH_ATTENTION is true, but the CI workflow overrides it to false. FlashAttention builds from source and requires a GPU during compilation for best results. GitHub Actions runners do not have GPUs, so the source build would either fail or produce suboptimal binaries. Users who need FlashAttention should either build locally or set the ENABLE_FLASH_ATTENTION repository variable to true.
ENABLE_LLM and LLAMA_CPP_VERSION
The ENABLE_LLM and LLAMA_CPP_VERSION build arguments are not explicitly set in the workflow, so they use the Dockerfile defaults (true and b8505 respectively). llama-server is compiled in every CI build.
Configuring via Repository Variables¶
To change the build configuration without modifying the workflow file:
- Go to the GitHub repository Settings
- Navigate to Secrets and variables > Actions > Variables
- Create or update a repository variable (e.g.,
CUDA_VERSION,PYTORCH_INDEX)
The workflow reads these at build time via ${{ vars.VARIABLE_NAME }}. If the variable is not set, the fallback default is used.
Cache Strategy¶
Registry Cache¶
The workflow uses Docker Buildx with registry-based caching:
cache-from: type=registry,ref=ghcr.io/diego-devita/comfyui-studio:cache
cache-to: type=registry,ref=ghcr.io/diego-devita/comfyui-studio:cache,mode=max
A dedicated :cache tag on GHCR stores the layer cache. This is used instead of GitHub Actions cache (GHA cache) for a specific reason:
Why registry cache instead of GHA cache
GitHub Actions cache has a 10 GB limit per repository. The ComfyUI Studio image has multiple large layers -- PyTorch wheels, custom node installations, and especially the llama-server compilation (~60 min). With GHA cache, the 10 GB limit was frequently exceeded, causing the llama-server layer to be evicted. Every subsequent build would recompile llama-server from scratch, adding ~60 minutes to every CI run.
Registry cache stores layers in the container registry (GHCR) which has no practical size limit. The :cache tag holds all layers with mode=max (cache every layer, not just the final image layers). This ensures the expensive llama-server compilation is cached and reused across builds.
Layer Ordering for Cache Efficiency¶
The Dockerfile is structured to maximize cache hits:
1. Base image + system packages (changes rarely)
2. Python venv + PyTorch (changes on CUDA/PyTorch version bump)
3. xformers (changes rarely)
4. SageAttention + Triton (changes rarely)
5. FlashAttention (changes rarely)
6. FastAPI + backend dependencies (changes rarely)
7. llama-server compilation (changes on LLAMA_CPP_VERSION bump)
8. COPY nodes.txt + install_nodes.sh (INVALIDATES all below on node change)
9. Custom node installation (rebuilds when nodes.txt changes)
10. COPY bootstrap.py + start.sh (changes rarely)
The key insight: COPY docker/production/nodes.txt at step 8 invalidates all subsequent layers. This is why llama-server (step 7) and all pip installs (steps 2-6) are placed above it. Adding a custom node to nodes.txt only rebuilds steps 8-10 (~15 minutes), not the full llama-server compilation (~60 minutes).
Workflow Steps¶
The complete workflow runs these steps:
- Checkout repository --
actions/checkout@v4 - Log in to GHCR -- authenticates with
GITHUB_TOKEN(automatic, no secrets to configure) - Extract metadata -- generates image tags and OCI labels
- Set up Docker Buildx -- enables BuildKit features (multi-platform, cache)
- Build and push -- builds the image, pushes to GHCR with both tags, updates cache
Permissions¶
The workflow requires:
The GITHUB_TOKEN secret is automatically provided by GitHub Actions and requires no manual configuration.
Build Times¶
Typical CI build times on ubuntu-latest runners:
| Scenario | Time | Notes |
|---|---|---|
| Full cold build | ~85-115 min | No cache, compiles everything including llama-server |
| Incremental (node change only) | ~15-25 min | llama-server and PyTorch cached |
| Incremental (no layer changes) | ~2-5 min | Everything cached, just tag update |
| llama-server recompile | +~60 min | Triggered by LLAMA_CPP_VERSION change |
| FlashAttention recompile | +~20-30 min | Triggered by enabling ENABLE_FLASH_ATTENTION |
Pulling the Image¶
# Latest build
docker pull ghcr.io/diego-devita/comfyui-studio:latest
# Specific commit
docker pull ghcr.io/diego-devita/comfyui-studio:sha-a1b2c3d
The image is public. No authentication is required to pull.