Pre-baked Docker image to run ComfyUI on RunPod with fast cold start.
Everything heavy ships in the image; what changes per workflow lives in /workspace/config.json.
| Item | Location | Persistent? |
|---|---|---|
| ComfyUI, Python, torch, sage attention, ffmpeg | /opt/ComfyUI (in the image) | No (ships with the image) |
| Custom nodes | /workspace/custom_nodes | Yes (volume) |
| Models | /workspace/models/<category> | Yes (volume) |
| Workflows, outputs, ComfyUI configs | /workspace/user, /workspace/output | Yes (volume) |
| List of nodes + models to download | /workspace/config.json | Yes (volume) |
docker build -t tcpassos/comfyui-runpod:latest C:\dev\comfyui-docker
docker push tcpassos/comfyui-runpod:latest
Final size: ~16 GB. Build takes ~10–15 min on a good connection (the RunPod base image is already ~9 GB).
(Optional) Change ComfyUI version at build time:
docker build --build-arg COMFYUI_VERSION=v0.22.0 -t tcpassos/comfyui-runpod:v0.22 C:\dev\comfyui-docker
docker run --rm --entrypoint python3 tcpassos/comfyui-runpod:latest -c "import torch, torchvision, torchaudio, xformers, sageattention; print('torch', torch.__version__, 'cuda', torch.version.cuda, 'tv', torchvision.__version__, 'ta', torchaudio.__version__, 'xf', xformers.__version__)"
Expected output:
torch 2.12.0+cu130 cuda 13.0 tv 0.27.0+cu130 ta 2.11.0+cu130 xf 0.0.35
The
--entrypoint python3flag is required; otherwise the image's ENTRYPOINT runs the full node/model provisioning before your command.
The image installs xformers==0.0.35 but its official wheel was built against torch 2.10/cu128/py3.10 while this image uses torch 2.12/cu130/py3.12 — the module imports, but its CUDA extensions (memory-efficient attention, SwiGLU) stay disabled with a warning. Use sageattention as your main optimization path (installed and functional — pure Triton, no compiled extension).
RunPod Console → Templates → + New Template:
| Field | Value |
|---|---|
| Template Name | comfyui-runpod (any name) |
| Container Image | tcpassos/comfyui-runpod:latest |
| Container Disk | 25 GB |
| Volume Disk | 100 GB (size for your models) |
| Volume Mount Path | /workspace |
| Expose HTTP Ports | 8188 (RunPod gives an HTTPS URL via proxy.runpod.net) |
| Expose TCP Ports | 22 (SSH, optional) |
| Container Start Command | (empty — uses the image's ENTRYPOINT) |
Environment Variables:
| Name | Value |
|---|---|
HF_TOKEN | your HuggingFace token (read) |
CIVITAI_TOKEN | your Civitai token |
CONFIG_URL | required if /workspace/config.json does not yet exist in the volume. Public URL (Gist / raw GitHub) of a config.json. If missing and no config exists in the volume, the entrypoint aborts with a clear error (prevents accidental download of an unintended default set). |
UPDATE_NODES | false (default) — does not update existing custom nodes on boot, keeping deploys reproducible. true runs git pull --ff-only on every node without a pinned ref. |
PORT | 8188 (optional, default is 8188) |
Templates → your template → Deploy:
/workspace across stops.Deploy → wait for "Running".
Takes 5–15 min because the entrypoint will:
config.json from CONFIG_URL to /workspace/config.json (if not set, the pod aborts — protection against accidental deploys without a defined config).nodes[].pip install the requirements of each node.models[] (HF / Civitai using the tokens from the env vars).Follow along in Connect → Logs. When you see Starting ComfyUI on port 8188, you're ready.
Without
CONFIG_URLset and without a pre-populated volume: the entrypoint prints instructions and exits with code 1. Check the logs and either (a) setCONFIG_URLon the template, or (b) SSH into the pod andcp /opt/config.example.json /workspace/config.jsonto use the embedded example set.
Connect → HTTP Service Port 8188 (don't use https) → opens ComfyUI at http://<pod-id>-8188.proxy.runpod.net.
Stop + Start of the same pod (with volume) → boot in ~30s:
git pull existing nodes (env UPDATE_NODES=false) — keeps boots reproducible. Set UPDATE_NODES=true on the template if you want unpinned nodes to be updated on Stop+Start.pip install is a no-op if already satisfied).SSH into the pod and edit /workspace/config.json. Schema:
{
"nodes": [
"https://github.com/user/repo",
{ "url": "https://github.com/kijai/ComfyUI-KJNodes", "ref": "v1.0.0" }
],
"models": [
{ "category": "checkpoints", "url": "...", "filename": "model.safetensors" },
{ "path": "models/loras/character", "url": "...", "filename": "char.safetensors" },
{ "path": "custom_nodes/MyNode/models", "url": "...", "filename": "weights.pt" }
],
"workflows": [
{ "url": "https://gist.../wan_i2v.json", "filename": "wan_i2v.json" }
]
}
Fields:
nodes[] — Each item is a string (git URL, always HEAD) or an object {url, ref?} where ref is a sha / tag / branch (version pin for reproducibility). Clone + pip install requirements.models[] — Each item needs url. Destination:
path (relative to /workspace, or absolute) → takes precedence.category → fallback, equivalent to path: "models/<category>".filename optional (derived from the URL if missing).workflows[] — Each item: url + optional filename. Downloaded to /workspace/user/default/workflows/.Invalid JSON is detected on boot (
jq empty) and aborts with a clear message.
Valid categories (any subfolder under models/): diffusion_models, checkpoints, loras, vae, text_encoders, clip_vision, controlnet, upscale_models, frame_interpolation, embeddings, etc.
After editing, restart the pod (Stop + Start). The entrypoint will sync:
git pull, only if UPDATE_NODES=true)--continue-at -)Host each config in a public Gist (or raw GitHub) and create one RunPod template per workflow, all using the same image tcpassos/comfyui-runpod:latest. Differentiate via the CONFIG_URL env var:
| Template | CONFIG_URL |
|---|---|
comfyui-wan | https://gist.githubusercontent.com/USER/HASH/raw/wan.json |
comfyui-flux | https://gist.githubusercontent.com/USER/HASH/raw/flux.json |
comfyui-sdxl | https://gist.githubusercontent.com/USER/HASH/raw/sdxl.json |
On the first boot of each pod, if /workspace/config.json doesn't exist yet, the entrypoint fetches it from CONFIG_URL. Updated a config in the Gist? Stop + Start the pod and the entrypoint will pick up new nodes / models and sync.
cd /workspace/custom_nodes
git clone https://github.com/user/repo
[ -f repo/requirements.txt ] && pip install -r repo/requirements.txt
# restart just ComfyUI:
pkill -f "python3 main.py" ; cd /opt/ComfyUI && python3 main.py --listen 0.0.0.0 --port 8188 --base-directory /workspace
HEALTHCHECK that GETs 127.0.0.1:8188 every 30s (with start-period=600s to cover the first boot). It shows up as healthy in docker ps once ComfyUI is up. RunPod does not use this to restart the pod — it's informational.filename in the JSON matches exactly the file in /workspace/models/<category>/..safetensors: invalid / expired token, or the model requires additional login (early access). Check CIVITAI_TOKEN.xformers warning in logs: expected (see Build section above). It does not affect generation if you use sageattention in your nodes.config.json mounted. Edit /workspace/config.json via the Web Terminal and restart.Content type
Image
Digest
sha256:cd82427ff…
Size
12.1 GB
Last updated
4 months ago
docker pull tcpassos/comfyui-runpod