Initial commit: Hermes Agent Skills collection
This commit is contained in:
+82
@@ -0,0 +1,82 @@
|
||||
# ComfyUI Model Directory Layout (CT 204 Reference)
|
||||
|
||||
Canonical path inside the LXC: `/opt/ComfyUI/models/`
|
||||
|
||||
## Directory-to-Node Mapping
|
||||
|
||||
| Directory | ComfyUI Node Type | Example Models |
|
||||
|---|---|---|
|
||||
| `checkpoints/` | Load Checkpoint | legacy `.ckpt`, `.safetensors` |
|
||||
| `diffusion_models/` | Load Diffusion Model | Flux, SD3, Ideogram, etc. |
|
||||
| `text_encoders/` | CLIP Text Encode | T5, Qwen-VL, CLIP-L, etc. |
|
||||
| `vae/` | Load VAE | SD VAE, Flux VAE |
|
||||
| `unet/` | deprecated alias for diffusion_models | — |
|
||||
| `clip/` | Load CLIP | CLIP models (some workflows use this instead of text_encoders) |
|
||||
| `clip_vision/` | CLIP Vision Encode | image-to-text encoders |
|
||||
| `controlnet/` | Load ControlNet | `.safetensors` control nets |
|
||||
| `loras/` | Load LoRA | `.safetensors` LoRAs |
|
||||
| `embeddings/` | Embedding | textual inversion files |
|
||||
| `upscale_models/` | Upscale Model | ESRGAN, RealESRGAN |
|
||||
| `vae_approx/` | VAE Decode (taesd) | TAESD approx decoder |
|
||||
|
||||
## Model Source URLs
|
||||
|
||||
### HuggingFace
|
||||
|
||||
Always use `resolve` URL + `-O` filename:
|
||||
|
||||
```bash
|
||||
cd /opt/ComfyUI/models/diffusion_models
|
||||
wget -c "https://huggingface.co/Comfy-Org/Ideogram-4/resolve/main/diffusion_models/ideogram4_fp8_scaled.safetensors" -O ideogram4_fp8_scaled.safetensors
|
||||
```
|
||||
|
||||
- `blob` URL → HTML preview page (wrong)
|
||||
- `resolve` URL → 302 to CDN, then binary (correct)
|
||||
- `-O` ensures predictable filename regardless of redirect
|
||||
|
||||
### CivitAI
|
||||
|
||||
CivitAI model page is NOT the download URL:
|
||||
|
||||
```bash
|
||||
# WRONG - downloads HTML page
|
||||
wget "https://civitai.com/models/12345/model-name"
|
||||
|
||||
# CORRECT - api download endpoint
|
||||
wget "https://civitai.com/api/download/models/67890?type=Model&format=SafeTensor" -O model.safetensors
|
||||
```
|
||||
|
||||
## Verifying Downloads
|
||||
|
||||
```bash
|
||||
ls -lh /opt/ComfyUI/models/diffusion_models/
|
||||
file /opt/ComfyUI/models/diffusion_models/*.safetensors
|
||||
```
|
||||
|
||||
`file` should report `data` or similar binary type, not `HTML document`.
|
||||
|
||||
## Parallel Downloads
|
||||
|
||||
When queueing multiple large models, run each in background and poll:
|
||||
|
||||
```bash
|
||||
# Start 3 downloads in parallel
|
||||
pct exec 204 -- bash -c 'cd /opt/ComfyUI/models/text_encoders && nohup wget -c --tries=0 --read-timeout=30 "..." -O qwen3vl_8b_fp8_scaled.safetensors > /tmp/qwen_dl.log 2>&1 &'
|
||||
pct exec 204 -- bash -c 'cd /opt/ComfyUI/models/diffusion_models && nohup wget -c --tries=0 --read-timeout=30 "..." -O ideogram4_fp8_scaled.safetensors > /tmp/ideogram_dl.log 2>&1 &'
|
||||
|
||||
# Poll
|
||||
pct exec 204 -- tail -5 /tmp/qwen_dl.log
|
||||
pct exec 204 -- ls -lh /opt/ComfyUI/models/
|
||||
```
|
||||
|
||||
## Disk Space Planning
|
||||
|
||||
| Model Type | Typical Size | Notes |
|
||||
|---|---|---|
|
||||
| FLUX/SD XL diffusion model | 7–17 GB | FP8/FP16 |
|
||||
| Text encoder (T5-XXL) | 10 GB | often largest single file |
|
||||
| VAE | 300–800 MB | small |
|
||||
| LoRA | 50–500 MB | small, many files |
|
||||
| Checkpoint (.ckpt) | 4–7 GB | deprecated for Flux |
|
||||
|
||||
Total for a single workflow: 20–35 GB minimum. Plan LXC rootfs accordingly.
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
# LXC GPU Autosetup — Background Script Pattern
|
||||
|
||||
## Problem
|
||||
|
||||
Provisioning GPU-accelerated LXC containers involves long-running operations:
|
||||
|
||||
1. `apt update && apt install` inside LXC (first setup)
|
||||
2. `amdgpu-install --usecase=rocm --no-dkms` (30-90 min, 100+ packages)
|
||||
3. `pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm6.2` (~4 GB download, 10-20 min)
|
||||
4. `pip install -r requirements.txt` for ComfyUI/Ollama/vLLM
|
||||
|
||||
Running any of these in the foreground via `pct exec` is brittle:
|
||||
- SSH timeout kills the session
|
||||
- `pct exec` double-shell-quoting breaks complex commands
|
||||
- No way to resume if interrupted
|
||||
|
||||
## Solution: Background Script on Host
|
||||
|
||||
Write a bash script on the Proxmox **host**, run it via `nohup`, and poll completion markers.
|
||||
|
||||
### Step 1: Write the Script
|
||||
|
||||
```bash
|
||||
# /tmp/ctNNN_autosetup.sh — runs ON the Proxmox host, orchestrates INSIDE the LXC
|
||||
CTID=${1:-204}
|
||||
LOG=/tmp/ct${CTID}_autosetup.log
|
||||
DONE=/tmp/ct${CTID}_autosetup_done
|
||||
|
||||
echo "$(date): Starting auto-setup for CT ${CTID}..." > "$LOG"
|
||||
|
||||
# --- Wait for dpkg lock inside CT ---
|
||||
while pct exec "$CTID" -- fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do
|
||||
echo "$(date): dpkg still locked..." >> "$LOG"
|
||||
sleep 30
|
||||
done
|
||||
echo "$(date): dpkg free." >> "$LOG"
|
||||
|
||||
# --- Install ROCm (inside CT) ---
|
||||
pct exec "$CTID" -- bash -c '
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
export PATH=/opt/rocm/bin:/usr/local/bin:$PATH
|
||||
dpkg --configure -a 2>&1
|
||||
apt-get -f install -y -qq 2>&1
|
||||
cd /tmp
|
||||
wget -q https://repo.radeon.com/amdgpu-install/6.3.3/ubuntu/noble/amdgpu-install_6.3.60303-1_all.deb
|
||||
dpkg -i amdgpu-install_6.3.60303-1_all.deb 2>&1 | tail -3
|
||||
apt-get update -qq
|
||||
amdgpu-install --usecase=rocm --no-dkms -y 2>&1 | tail -10
|
||||
' >> "$LOG" 2>&1
|
||||
|
||||
# --- Verify ROCm ---
|
||||
echo "$(date): Verifying ROCm..." >> "$LOG"
|
||||
pct exec "$CTID" -- bash -c 'export PATH=/opt/rocm/bin:$PATH; rocminfo 2>&1 | grep -E "Name:" | head -5' >> "$LOG" 2>&1
|
||||
pct exec "$CTID" -- bash -c 'export PATH=/opt/rocm/bin:$PATH; rocm-smi 2>&1 | head -5' >> "$LOG" 2>&1
|
||||
|
||||
# --- PyTorch ROCm ---
|
||||
echo "$(date): Installing PyTorch ROCm..." >> "$LOG"
|
||||
pct exec "$CTID" -- bash -c 'pip3 install --break-system-packages torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm6.2 2>&1 | tail -10' >> "$LOG" 2>&1
|
||||
|
||||
# --- ComfyUI / App ---
|
||||
echo "$(date): Installing app..." >> "$LOG"
|
||||
pct exec "$CTID" -- bash -c '
|
||||
cd /opt
|
||||
git clone https://github.com/comfyanonymous/ComfyUI.git 2>&1 | tail -3
|
||||
cd ComfyUI
|
||||
pip3 install --break-system-packages -r requirements.txt 2>&1 | tail -10
|
||||
' >> "$LOG" 2>&1
|
||||
|
||||
# --- systemd service ---
|
||||
pct exec "$CTID" -- bash -c 'cat > /etc/systemd/system/comfyui.service << "EOF"
|
||||
[Unit]
|
||||
Description=ComfyUI
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/opt/ComfyUI
|
||||
ExecStart=/usr/bin/python3 main.py --listen 0.0.0.0 --port 8188
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment=HIP_VISIBLE_DEVICES=0
|
||||
Environment=PATH=/opt/rocm/bin:/usr/local/bin:/usr/bin:/bin
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
systemctl enable comfyui
|
||||
' >> "$LOG" 2>&1
|
||||
|
||||
pct exec "$CTID" -- systemctl start comfyui >> "$LOG" 2>&1
|
||||
sleep 5
|
||||
pct exec "$CTID" -- ss -tlnp | grep 8188 >> "$LOG" 2>&1
|
||||
|
||||
echo "$(date): Setup complete." >> "$LOG"
|
||||
echo "DONE" >> "$DONE"
|
||||
```
|
||||
|
||||
### Step 2: Launch on Host
|
||||
|
||||
```bash
|
||||
chmod +x /tmp/ct204_autosetup.sh
|
||||
nohup bash /tmp/ct204_autosetup.sh > /dev/null 2>&1 &
|
||||
```
|
||||
|
||||
### Step 3: Poll from Client
|
||||
|
||||
```bash
|
||||
# Check completion
|
||||
cat /tmp/ct204_autosetup_done 2>/dev/null || echo NOT_YET
|
||||
|
||||
# Check progress
|
||||
tail -20 /tmp/ct204_autosetup.log
|
||||
```
|
||||
|
||||
## Python Paramiko Polling Pattern
|
||||
|
||||
When automating from a remote client (not the Proxmox host):
|
||||
|
||||
```python
|
||||
import paramiko, time
|
||||
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
client.connect('10.0.20.91', username='root', password='28acaneltO!#', timeout=15)
|
||||
|
||||
for attempt in range(20): # ~10 minutes
|
||||
stdin, stdout, stderr = client.exec_command(
|
||||
"cat /tmp/ct204_autosetup_done 2>/dev/null || echo NOT_YET"
|
||||
)
|
||||
status = stdout.read().decode().strip()
|
||||
if status == "DONE":
|
||||
print("Setup complete!")
|
||||
break
|
||||
|
||||
stdin2, stdout2, stderr2 = client.exec_command(
|
||||
"tail -5 /tmp/ct204_autosetup.log"
|
||||
)
|
||||
print(f"Progress: {stdout2.read().decode().strip()[-200:]}")
|
||||
time.sleep(30)
|
||||
|
||||
client.close()
|
||||
```
|
||||
|
||||
## PEP 668 Fix for Ubuntu 24.04+ Containers
|
||||
|
||||
Ubuntu 24.04 Noble enforces `externally-managed` Python environment. Inside LXC:
|
||||
|
||||
```bash
|
||||
# WRONG — fails with PEP 668
|
||||
pip3 install torch
|
||||
|
||||
# RIGHT — bypass inside disposable container
|
||||
pip3 install --break-system-packages torch torchvision torchaudio \
|
||||
--index-url https://download.pytorch.org/whl/rocm6.2
|
||||
```
|
||||
|
||||
Alternative: Create a venv inside the container (overkill for single-purpose inference boxes).
|
||||
|
||||
## ROCm PATH Gotcha
|
||||
|
||||
ROCm 6.3 does NOT add `/opt/rocm/bin` to PATH by default. Every `pct exec` ROCm call needs:
|
||||
|
||||
```bash
|
||||
export PATH=/opt/rocm/bin:/usr/local/bin:$PATH
|
||||
rocminfo
|
||||
rocm-smi
|
||||
```
|
||||
|
||||
Or set it globally:
|
||||
|
||||
```bash
|
||||
pct exec 204 -- bash -c 'echo "export PATH=/opt/rocm/bin:\$PATH" > /etc/profile.d/rocm.sh'
|
||||
```
|
||||
|
||||
## Quoting Rule for pct exec
|
||||
|
||||
`pct exec` always adds a shell layer. Complex commands should be written to a file inside the container, then executed:
|
||||
|
||||
```bash
|
||||
# BAD — quoting nightmare, easy breakage
|
||||
pct exec 204 -- python3 -c "import torch; print('HIP:', torch.cuda.is_available())"
|
||||
|
||||
# GOOD — write script, execute
|
||||
pct exec 204 -- bash -c 'cat > /tmp/verify.py << "EOF"
|
||||
import torch
|
||||
print("HIP available:", torch.cuda.is_available())
|
||||
print("Device:", torch.cuda.get_device_name(0))
|
||||
EOF
|
||||
python3 /tmp/verify.py'
|
||||
```
|
||||
|
||||
## Monitoring via Cronjob
|
||||
|
||||
Set up a cronjob that polls the `DONE` marker and reports status:
|
||||
|
||||
```bash
|
||||
# ~/.hermes/cron/ct204_monitor.sh
|
||||
if [ -f /tmp/ct204_autosetup_done ]; then
|
||||
echo "CT 204 SETUP COMPLETE"
|
||||
tail -10 /tmp/ct204_autosetup.log
|
||||
pct exec 204 -- python3 -c 'import torch; print(torch.cuda.is_available())'
|
||||
else
|
||||
echo "CT 204 SETUP RUNNING"
|
||||
tail -5 /tmp/ct204_autosetup.log
|
||||
fi
|
||||
```
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
# LXC GPU Device Bind Rules — Quick Reference
|
||||
|
||||
## AMD ROCm (APU / dGPU)
|
||||
|
||||
```conf
|
||||
# /etc/pve/lxc/NNN.conf additions
|
||||
lxc.cgroup2.devices.allow: c 226:* rwm
|
||||
lxc.cgroup2.devices.allow: c 511:0 rwm
|
||||
lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir
|
||||
lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file
|
||||
lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file
|
||||
lxc.mount.entry: /dev/kfd dev/kfd none bind,optional,create=file
|
||||
```
|
||||
|
||||
Verify host devices before adding:
|
||||
```bash
|
||||
ls -la /dev/dri/ /dev/kfd
|
||||
```
|
||||
|
||||
## NVIDIA CUDA
|
||||
|
||||
```conf
|
||||
lxc.cgroup2.devices.allow: c 195:* rwm
|
||||
lxc.cgroup2.devices.allow: c 243:* rwm
|
||||
lxc.cgroup2.devices.allow: c 235:* rwm
|
||||
lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file
|
||||
lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file
|
||||
lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file
|
||||
lxc.mount.entry: /dev/nvidia-modeset dev/nvidia-modeset none bind,optional,create=file
|
||||
```
|
||||
|
||||
## Intel Arc / OneAPI
|
||||
|
||||
```conf
|
||||
lxc.cgroup2.devices.allow: c 226:* rwm
|
||||
lxc.cgroup2.devices.allow: c 10:122 rwm # /dev/dri/renderD128 via misc major
|
||||
lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir
|
||||
lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file
|
||||
```
|
||||
|
||||
## Device Number Reference
|
||||
|
||||
| Path | Major | Minor | Group |
|
||||
|------|-------|-------|-------|
|
||||
| `/dev/dri/card0` | 226 | 0 | video |
|
||||
| `/dev/dri/renderD128` | 226 | 128 | render |
|
||||
| `/dev/dri/renderD129` | 226 | 129 | render |
|
||||
| `/dev/kfd` | 511 | 0 | compute |
|
||||
| `/dev/nvidia0` | 195 | 0 | nvidia |
|
||||
| `/dev/nvidiactl` | 195 | 255 | nvidia |
|
||||
| `/dev/nvidia-uvm` | 243 | 0 | nvidia |
|
||||
|
||||
## Dynamic Device Discovery Script
|
||||
|
||||
Run on host to auto-generate LXC mount entries:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# generate_lxc_gpu_mounts.sh
|
||||
DEV_ALLOW=""
|
||||
MOUNTS=""
|
||||
|
||||
# AMD kfd
|
||||
if [ -e /dev/kfd ]; then
|
||||
DEV_ALLOW+="lxc.cgroup2.devices.allow: c 511:0 rwm\n"
|
||||
MOUNTS+="lxc.mount.entry: /dev/kfd dev/kfd none bind,optional,create=file\n"
|
||||
fi
|
||||
|
||||
# DRI devices
|
||||
for dev in /dev/dri/card* /dev/dri/renderD*; do
|
||||
[ -e "$dev" ] || continue
|
||||
MAJ=$(stat -c '%t' "$dev")
|
||||
MIN=$(stat -c '%T' "$dev")
|
||||
MAJ_DEC=$((16#$MAJ))
|
||||
MIN_DEC=$((16#$MIN))
|
||||
DEV_ALLOW+="lxc.cgroup2.devices.allow: c ${MAJ_DEC}:${MIN_DEC} rwm\n"
|
||||
BASENAME=$(basename "$dev")
|
||||
MOUNTS+="lxc.mount.entry: $dev dev/dri/$BASENAME none bind,optional,create=file\n"
|
||||
done
|
||||
|
||||
echo "# Device allow rules:"
|
||||
echo -e "$DEV_ALLOW"
|
||||
echo "# Mount entries:"
|
||||
echo -e "$MOUNTS"
|
||||
```
|
||||
Reference in New Issue
Block a user