Skip to content

Instantly share code, notes, and snippets.

@Saik0s
Last active June 13, 2026 01:33
Show Gist options
  • Select an option

  • Save Saik0s/540fa755f4a992039d184eb20291c119 to your computer and use it in GitHub Desktop.

Select an option

Save Saik0s/540fa755f4a992039d184eb20291c119 to your computer and use it in GitHub Desktop.
Qwen-Image 2512 ComfyUI provisioning script
#!/usr/bin/env bash
# Qwen-Image 2512 ComfyUI Provisioning Script
# Downloads Qwen-Image models, LoRAs, writes workflow, installs comfyui-kjnodes.
#
# Required: HF_TOKEN (for HuggingFace), CIVITAI_TOKEN (optional)
# Run as Vast.ai --onstart-cmd or standalone on a ComfyUI instance.
set -euo pipefail
# ── Configuration ───────────────────────────────────────────────
WORKSPACE_DIR="/workspace"
COMFYUI_DIR="${WORKSPACE_DIR}/ComfyUI"
MODELS_DIR="${COMFYUI_DIR}/models"
WORKFLOW_DIR="${COMFYUI_DIR}/user/default/workflows"
PROVISIONING_LOG="/var/log/portal/provisioning.log"
# HF download parallelism
HF_SEMAPHORE_DIR="${WORKSPACE_DIR}/.qwen_hf_sem"
HF_MAX_PARALLEL=3
# Check required token
if [ -z "$HF_TOKEN" ]; then
echo "[qwen] ERROR: HF_TOKEN not set"
exit 1
fi
# ── Directory setup ────────────────────────────────────────────
mkdir -p "$MODELS_DIR"/{diffusion_models,text_encoders/qwen,vae/qwen-image,loras}
mkdir -p "$WORKFLOW_DIR"
mkdir -p "$(dirname "$PROVISIONING_LOG")" 2>/dev/null || true
rm -rf "$HF_SEMAPHORE_DIR"
mkdir -p "$HF_SEMAPHORE_DIR"
# ── Logging & Cleanup ──────────────────────────────────────────
script_error() {
local line_no=$1
echo "[qwen] ERROR at line ${line_no} (exit $?)" | tee -a /var/log/portal/comfyui.log
exit 1
}
trap 'script_error $LINENO' ERR
script_cleanup() {
rm -rf "$HF_SEMAPHORE_DIR"
echo "[qwen] cleanup done $(date -Iseconds)" | tee -a "$PROVISIONING_LOG"
}
trap script_cleanup EXIT
exec > >(tee -a "$PROVISIONING_LOG") 2>&1
echo "[qwen] provision start $(date -Iseconds)"
# ── Download helpers ───────────────────────────────────────────
acquire_slot() {
while true; do
local count
count=$(find "$HF_SEMAPHORE_DIR" -name 'slot_*' 2>/dev/null | wc -l | tr -d ' ')
if [ "$count" -lt "$HF_MAX_PARALLEL" ]; then
mktemp -p "$HF_SEMAPHORE_DIR" slot_XXXXXX
return 0
fi
sleep 2
done
}
release_slot() { rm -f "$1"; }
download_hf_file() {
local repo="$1" filename="$2" dest="$3"
local dest_dir; dest_dir=$(dirname "$dest")
if [ -f "$dest" ] && [ -s "$dest" ]; then
echo "[qwen] skip (exists): ${dest}"
return 0
fi
mkdir -p "$dest_dir"
local slot; slot=$(acquire_slot)
local attempt=1 max_retries=5 delay=2
while [ "$attempt" -le "$max_retries" ]; do
echo "[qwen] HF ${repo}/${filename} (${attempt}/${max_retries})"
if HF_XET_HIGH_PERFORMANCE=1 hf download "$repo" "$filename" \
--token "$HF_TOKEN" \
--local-dir "$dest_dir" \
; then
local downloaded="${dest_dir}/${filename}"
if [ -f "$downloaded" ] && [ "$downloaded" != "$dest" ]; then
mv "$downloaded" "$dest"
fi
release_slot "$slot"
return 0
fi
sleep "$delay"
delay=$((delay * 2))
attempt=$((attempt + 1))
done
release_slot "$slot"
echo "[qwen] FAILED: ${repo}/${filename}"
return 1
}
download_civitai_file() {
local version_id="$1" dest="$2"
if [ -f "$dest" ] && [ -s "$dest" ]; then
echo "[qwen] skip (exists): ${dest}"
return 0
fi
if [ -z "${CIVITAI_TOKEN:-}" ]; then
echo "[qwen] SKIP (no CIVITAI_TOKEN): ${dest}"
return 0
fi
mkdir -p "$(dirname "$dest")"
local url="https://civitai.com/api/download/models/${version_id}?token=${CIVITAI_TOKEN}"
echo "[qwen] civitai ${version_id} -> ${dest}"
for i in $(seq 1 3); do
if wget -q --tries=3 --retry-connrefused --timeout=120 -O "$dest" "$url"; then
if [ -s "$dest" ]; then return 0; fi
fi
sleep 5
done
echo "[qwen] FAILED civitai: ${version_id}"
return 1
}
# ── Model Downloads ────────────────────────────────────────────
echo "[qwen] downloading models..."
# 1. VAE: qwen_image_vae.safetensors
download_hf_file \
"Comfy-Org/Qwen-Image_ComfyUI" \
"split_files/vae/qwen_image_vae.safetensors" \
"${MODELS_DIR}/vae/qwen-image/qwen_image_vae.safetensors"
# 2. Text Encoder: qwen_2.5_vl_7b_fp8_scaled.safetensors
download_hf_file \
"Comfy-Org/Qwen-Image_ComfyUI" \
"split_files/text_encoders/qwen_2.5_vl_7b_fp8_scaled.safetensors" \
"${MODELS_DIR}/text_encoders/qwen/qwen_2.5_vl_7b_fp8_scaled.safetensors"
# 3. Lightning LoRA (8-step speedup)
download_hf_file \
"lightx2v/Qwen-Image-2512-Lightning" \
"Qwen-Image-2512-Lightning-8steps-V1.0-bf16.safetensors" \
"${MODELS_DIR}/loras/Qwen-Image-2512-Lightning-8steps-V1.0-bf16.safetensors"
echo "[qwen] HF models done"
# ── Civitai Models ─────────────────────────────────────────────
echo "[qwen] downloading Civitai models..."
# === EDIT THESE VERSION IDs BEFORE RUNNING ===
#
# Get version IDs:
# curl -s "https://civitai.com/api/v1/models/MODEL_ID" | jq '.modelVersions[0].id'
#
# absoluteRealismV01_qwenV10 — search "AbsoluteReality Qwen" on civitai
# amateurphotography_v1_qwen — civitai model 652699 (Amateur Photography)
# female-body-beauty_qwen — search "female body beauty qwen" on civitai
# samsung_qwen2512 — search "samsung qwen" on civitai
CIVITAI_MODELS=(
# version_id|dest_path
# "CHANGE_ME|${MODELS_DIR}/diffusion_models/absoluteRealismV01_qwenV10.safetensors"
# "CHANGE_ME|${MODELS_DIR}/loras/amateurphotography_v1_qwen.safetensors"
# "CHANGE_ME|${MODELS_DIR}/loras/female-body-beauty_qwen.safetensors"
# "CHANGE_ME|${MODELS_DIR}/loras/samsung_qwen2512.safetensors"
)
for entry in "${CIVITAI_MODELS[@]}"; do
version_id="${entry%%|*}"
dest="${entry##*|}"
download_civitai_file "$version_id" "$dest" || true
done
# ── Workflow ───────────────────────────────────────────────────
echo "[qwen] writing workflow JSON..."
cat <<'ENDOFB64' | base64 -d > "${WORKFLOW_DIR}/qwen_Image_2512_8steps_v2_api.json"
ewogICI2MCI6IHsKICAgICJpbnB1dHMiOiB7CiAgICAgICJmaWxlbmFtZV9wcmVmaXgiOiAiUXdlbi1JbWFnZS0yNTEyIiwKICAgICAgImltYWdlcyI6IFsKICAgICAgICAiMjM4OjIzMSIsCiAgICAgICAgMAogICAgICBdCiAgICB9LAogICAgImNsYXNzX3R5cGUiOiAiU2F2ZUltYWdlIiwKICAgICJfbWV0YSI6IHsKICAgICAgInRpdGxlIjogIlNhdmUgSW1hZ2UiCiAgICB9CiAgfSwKICAiNjEiOiB7CiAgICAiaW5wdXRzIjogewogICAgICAibG9yYV9uYW1lIjogIlF3ZW4tSW1hZ2UtMjUxMl9Mb1JhX2J5LUFJX0NoYXJhY3RlcnNfU1RZTEVfU21hcnRwaG9uZVNuYXBzaG90UGhvdG9SZWFsaXR5X3YxMF9UUklHR0VSJGFtdHIgc25hcHNob3QgcGhvdG8kLnNhZmV0ZW5zb3JzIiwKICAgICAgInN0cmVuZ3RoX21vZGVsIjogMC43LAogICAgICAibW9kZWwiOiBbCiAgICAgICAgIjIzODoyMjEiLAogICAgICAgIDAKICAgICAgXQogICAgfSwKICAgICJjbGFzc190eXBlIjogIkxvcmFMb2FkZXJNb2RlbE9ubHkiLAogICAgIl9tZXRhIjogewogICAgICAidGl0bGUiOiAiTG9hZCBMb1JBIgogICAgfQogIH0sCiAgIjYyIjogewogICAgImlucHV0cyI6IHsKICAgICAgImxvcmFfbmFtZSI6ICJhbWF0ZXVycGhvdG9ncmFwaHlfdjFfcXdlbi5zYWZldGVuc29ycyIsCiAgICAgICJzdHJlbmd0aF9tb2RlbCI6IDAuNSwKICAgICAgIm1vZGVsIjogWwogICAgICAgICI2MSIsCiAgICAgICAgMAogICAgICBdCiAgICB9LAogICAgImNsYXNzX3R5cGUiOiAiTG9yYUxvYWRlck1vZGVsT25seSIsCiAgICAiX21ldGEiOiB7CiAgICAgICJ0aXRsZSI6ICJMb2FkIExvUkEiCiAgICB9CiAgfSwKICAiNjMiOiB7CiAgICAiaW5wdXRzIjogewogICAgICAibG9yYV9uYW1lIjogInNhbXN1bmdfcXdlbjI1MTIuc2FmZXRlbnNvcnMiLAogICAgICAic3RyZW5ndGhfbW9kZWwiOiAwLjUsCiAgICAgICJtb2RlbCI6IFsKICAgICAgICAiNjIiLAogICAgICAgIDAKICAgICAgXQogICAgfSwKICAgICJjbGFzc190eXBlIjogIkxvcmFMb2FkZXJNb2RlbE9ubHkiLAogICAgIl9tZXRhIjogewogICAgICAidGl0bGUiOiAiTG9hZCBMb1JBIgogICAgfQogIH0sCiAgIjIzODoyMTkiOiB7CiAgICAiaW5wdXRzIjogewogICAgICAiY2xpcF9uYW1lIjogInF3ZW4vcXdlbl8yLjVfdmxfN2JfZnA4X3NjYWxlZC5zYWZldGVuc29ycyIsCiAgICAgICJ0eXBlIjogInF3ZW5faW1hZ2UiLAogICAgICAiZGV2aWNlIjogImRlZmF1bHQiCiAgICB9LAogICAgImNsYXNzX3R5cGUiOiAiQ0xJUExvYWRlciIsCiAgICAiX21ldGEiOiB7CiAgICAgICJ0aXRsZSI6ICJMb2FkIENMSVAiCiAgICB9CiAgfSwKICAiMjM4OjIyMCI6IHsKICAgICJpbnB1dHMiOiB7CiAgICAgICJ2YWVfbmFtZSI6ICJxd2VuLWltYWdlL3F3ZW5faW1hZ2VfdmFlLnNhZmV0ZW5zb3JzIgogICAgfSwKICAgICJjbGFzc190eXBlIjogIlZBRUxvYWRlciIsCiAgICAiX21ldGEiOiB7CiAgICAgICJ0aXRsZSI6ICJMb2FkIFZBRSIKICAgIH0KICB9LAogICIyMzg6MjI2IjogewogICAgImlucHV0cyI6IHsKICAgICAgInVuZXRfbmFtZSI6ICJhYnNvbHV0ZVJlYWxpc21WMDFfcXdlblYxMC5zYWZldGVuc29ycyIsCiAgICAgICJ3ZWlnaHRfZHR5cGUiOiAiZGVmYXVsdCIKICAgIH0sCiAgICAiY2xhc3NfdHlwZSI6ICJVTkVUTG9hZGVyIiwKICAgICJfbWV0YSI6IHsKICAgICAgInRpdGxlIjogIkxvYWQgRGlmZnVzaW9uIE1vZGVsIgogICAgfQogIH0sCiAgIjIzODoyMzIiOiB7CiAgICAiaW5wdXRzIjogewogICAgICAid2lkdGgiOiA5MjgsCiAgICAgICJoZWlnaHQiOiAxNjY0LAogICAgICAiYmF0Y2hfc2l6ZSI6IDEKICAgIH0sCiAgICAiY2xhc3NfdHlwZSI6ICJFbXB0eVNEM0xhdGVudEltYWdlIiwKICAgICJfbWV0YSI6IHsKICAgICAgInRpdGxlIjogIkVtcHR5U0QzTGF0ZW50SW1hZ2UiCiAgICB9CiAgfSwKICAiMjM4OjIyNyI6IHsKICAgICJpbnB1dHMiOiB7CiAgICAgICJ0ZXh0IjogIkEgcGhvdG9yZWFsaXN0aWMgaW1hZ2Ugb2YgYW4gMTgteWVhci1vbGQgZ2lybCB3aXRoIGRhcmsgY3VybHkgaGFpciwgd2VhcmluZyBhIHB1cnBsZSBib2R5c3VpdCB3aGlsZSBkb2luZyBjb29sLWRvd24gc3RyZXRjaGVzIGFmdGVyIGRhbmNlIGNsYXNzLiBTaGUncyBpbiBhIHNlYXRlZCBmb3J3YXJkIGZvbGQsIGNvbXBsZXRlbHkgZm9jdXNlZCBvbiBoZXIgYnJlYXRoaW5nLCB1bmF3YXJlIG9mIHRoZSBjYW1lcmEgcG9zaXRpb25lZCBiZWhpbmQgdGhlIGRhbmNlIHN0dWRpbyBtaXJyb3JzLiBUaGUgYm9keXN1aXQgaXMgcmlkaW5nIHVwIGludG8gaGVyIGFzcyBhbmQgcHVsbGluZyB0aWdodCBhY3Jvc3MgaGVyIHB1c3N5LCBjcmVhdGluZyBhIGRlZXAgY2FtZWx0b2UgdGhhdCdzIGltcG9zc2libGUgdG8gbWlzcy4gSGVyIGZhY2UgaXMgcmVsYXhlZCBhbmQgcGVhY2VmdWwsIHNsaWdodGx5IGZsdXNoZWQgZnJvbSBkYW5jaW5nLiBUaGUgY2FtZXJhIGNhcHR1cmVzIGhlciBmcm9tIGJlaGluZCBhbmQgc2xpZ2h0bHkgYmVsb3csIGVtcGhhc2l6aW5nIHRoZSBpbnRpbWF0ZSB2aWV3IG9mIGhlciBzdHJldGNoZWQgZmFicmljLiBTdHVkaW8gbGlnaHRpbmcgY3JlYXRlcyBkcmFtYXRpYyBzaGFkb3dzIGFjcm9zcyBoZXIgY3VydmVzLiBUaGUgYmFja2dyb3VuZCBzaG93cyBvdGhlciBkYW5jZXJzIHBhY2tpbmcgdXAgdGhlaXIgdGhpbmdzLiBUaGUgaW1hZ2UgaGFzIHRoYXQgZ3JhaW55LCBzdXJ2ZWlsbGFuY2UtY2FtZXJhIHF1YWxpdHkgd2l0aCBhdXRoZW50aWMgdGltaW5nIGFuZCBmcmFtaW5nLiBTdHlsZTogdm95ZXVyaXN0aWMsIHJlYWxpc3RpYywgNGsgcmVzb2x1dGlvbi5cbiIsCiAgICAgICJjbGlwIjogWwogICAgICAgICIyMzg6MjE5IiwKICAgICAgICAwCiAgICAgIF0KICAgIH0sCiAgICAiY2xhc3NfdHlwZSI6ICJDTElQVGV4dEVuY29kZSIsCiAgICAiX21ldGEiOiB7CiAgICAgICJ0aXRsZSI6ICJDTElQIFRleHQgRW5jb2RlIChQb3NpdGl2ZSBQcm9tcHQpIgogICAgfQogIH0sCiAgIjIzODoyMjgiOiB7CiAgICAiaW5wdXRzIjogewogICAgICAidGV4dCI6ICLkvY7op4bliLDnjoc444CB5L2O55S76LSo44CB6IKi5L2T5YW55b2i44CB5omL5oyH5YW55b2i44CB55S75Y2i6LaF6L+H5aSx5ZKM44CB6Jue6LyR5oSf44CB5Lq66IS45peg57uG6IqC44CB6YCJ5bqm5YWJ5ruR44CB55S75Y2i5YW35pyJQUnmhJ/jgILmnoTlm77mt7fkubHjgILmloflrZfmqKHns7PvvIzmmbTmm7IiLAogICAgICAiY2xpcCI6IFsKICAgICAgICAiMjM4OjIxOSIsCiAgICAgICAgMAogICAgICBdCiAgICB9LAogICAgImNsYXNzX3R5cGUiOiAiQ0xJUFRleHRFbmNvZGUiLAogICAgIl9tZXRhIjogewogICAgICAidGl0bGUiOiAiQ0xJUCBUZXh0IEVuY29kZSAoTmVnYXRpdmUgUHJvbXB0KSIKICAgIH0KICB9LAogICIyMzg6MjIxIjogewogICAgImlucHV0cyI6IHsKICAgICAgImxvcmFfbmFtZSI6ICJRd2VuLUltYWdlLTI1MTItTGlnaHRuaW5nLThzdGVwcy1WMS4wLWJmMTYuc2FmZXRlbnNvcnMiLAogICAgICAic3RyZW5ndGhfbW9kZWwiOiAxLAogICAgICAibW9kZWwiOiBbCiAgICAgICAgIjIzODoyMjYiLAogICAgICAgIDAKICAgICAgXQogICAgfSwKICAgICJjbGFzc190eXBlIjogIkxvcmFMb2FkZXJNb2RlbE9ubHkiLAogICAgIl9tZXRhIjogewogICAgICAidGl0bGUiOiAiTG9hZCBMb1JBIgogICAgfQogIH0sCiAgIjIzODoyMjIiOiB7CiAgICAiaW5wdXRzIjogewogICAgICAic2hpZnQiOiAzLjEsCiAgICAgICJtb2RlbCI6IFsKICAgICAgICAiNjMiLAogICAgICAgIDAKICAgICAgXQogICAgfSwKICAgICJjbGFzc190eXBlIjogIk1vZGVsU2FtcGxpbmdBdXJhRmxvdyIsCiAgICAiX21ldGEiOiB7CiAgICAgICJ0aXRsZSI6ICJNb2RlbFNhbXBsaW5nQXVyYUZsb3ciCiAgICB9CiAgfSwKICAiMjM4OjIzMCI6IHsKICAgICJpbnB1dHMiOiB7CiAgICAgICJzZWVkIjogNTI1Mjg3MjEzMTQ4NDg4LAogICAgICAic3RlcHMiOiA4LAogICAgICAiY2ZnIjogMSwKICAgICAgInNhbXBsZXJfbmFtZSI6ICJyZXNfMnMiLAogICAgICAic2NoZWR1bGVyIjogImJldGE1NyIsCiAgICAgICJkZW5vaXNlIjogMSwKICAgICAgIm1vZGVsIjogWwogICAgICAgICIyMzg6MjIyIiwKICAgICAgICAwCiAgICAgIF0sCiAgICAgICJwb3NpdGl2ZSI6IFsKICAgICAgICAiMjM4OjIyNyIsCiAgICAgICAgMAogICAgICBdLAogICAgICAibmVnYXRpdmUiOiBbCiAgICAgICAgIjIzODoyMjgiLAogICAgICAgIDAKICAgICAgXSwKICAgICAgImxhdGVudF9pbWFnZSI6IFsKICAgICAgICAiMjM4OjIzMiIsCiAgICAgICAgMAogICAgICBdCiAgICB9LAogICAgImNsYXNzX3R5cGUiOiAiS1NhbXBsZXIiLAogICAgIl9tZXRhIjogewogICAgICAidGl0bGUiOiAiS1NhbXBsZXIiCiAgICB9CiAgfSwKICAiMjM4OjIzMSI6IHsKICAgICJpbnB1dHMiOiB7CiAgICAgICJzYW1wbGVzIjogWwogICAgICAgICIyMzg6MjMwIiwKICAgICAgICAwCiAgICAgIF0sCiAgICAgICJ2YWUiOiBbCiAgICAgICAgIjIzODoyMjAiLAogICAgICAgIDAKICAgICAgXQogICAgfSwKICAgICJjbGFzc190eXBlIjogIlZBRURlY29kZSIsCiAgICAiX21ldGEiOiB7CiAgICAgICJ0aXRsZSI6ICJWQUUgRGVjb2RlIgogICAgfQogIH0KfQ==
ENDOFB64
echo "[qwen] workflow written"
# ── Custom Nodes ───────────────────────────────────────────────
echo "[qwen] waiting for ComfyUI Manager..."
for i in $(seq 1 120); do
if [ -f "${COMFYUI_DIR}/custom_nodes/ComfyUI-Manager/cm-cli.py" ] && [ -x /venv/main/bin/python3.12 ]; then
break
fi
sleep 2
done
echo "[qwen] installing comfyui-kjnodes..."
COMFYUI_PATH="$COMFYUI_DIR" /venv/main/bin/python3.12 \
"${COMFYUI_DIR}/custom_nodes/ComfyUI-Manager/cm-cli.py" install comfyui-kjnodes
echo "[qwen] restarting ComfyUI..."
supervisorctl restart comfyui || true
echo "[qwen] PROVISION COMPLETE $(date -Iseconds)"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment