Proxmox VE 9.1 sobre Hetzner EPYC 7502P — Setup y Operación
Proxmox VE 9.1 sobre Hetzner EPYC 7502P
⚠️ SERVIDOR DADO DE BAJA · s53 (08-05-2026)
pve-epyc-02fue wipe (NIST SP 800-88:nvme format --ses=1× 4 NVMe + verificacióndd) y cancelado en Hetzner Robot el 2026-05-08. Las IPs23.88.74.167/2a01:4f8:272:5f10::2y los hosts Tailscalepve-epyc-02/crearack-llama-epyc-02ya no son de CreaRack. Toda la stack AI corre ahora en Google AI Studio Paid Tier managed.Runbook conservado como referencia histórica del proceso de setup Proxmox + LXC + llama-server (útil si en el futuro se vuelve a evaluar self-host LLM).
Última actualización: 01-05-2026 (sesión 46 · afinado calidad Auto-Plan 70%→99% con flags vision Reddit + 32 cores + mmproj F32)
Estado: setup completado en su día · servidor dado de baja en s53
Hardware: Hetzner Server Auction · AMD EPYC 7502P 32c/64t · 256 GB DDR4-2933 ECC reg (8×32 GB, 8 canales) · 4× Toshiba KXD51RUE960G NVMe U.2 960 GB Datacenter
Hostname: pve-epyc-02
IP pública: 23.88.74.167 / 2a01:4f8:272:5f10::2
Tailscale host: pve-epyc-02 / 100.123.143.35
Tailscale LXC llama-server: crearack-llama-epyc-02 / 100.80.142.60
1. Por qué Proxmox
Decidido como patrón canónico para hospedar simultáneamente:
- llama-server (llama.cpp + Gemma 4 26B-A4B-it Q4_K_M) — workload CPU-puro con visión denso.
- Crearack PROD futuro — Django 6 + PG 18 + Valkey + VictoriaMetrics + Daphne ASGI (consolidación pendiente).
- Margen para staging, herramientas, etc.
Ventajas que aporta Proxmox vs Ubuntu directo
| Ventaja | Aplicación concreta |
|---|---|
| Snapshots en caliente | Antes de upgrade llama.cpp, Django, PG → snapshot de 5 s. Rollback en 30 s. |
| Backups por VM/LXC integrados | vzdump nightly schedule con retention. Restore = arrastrar fichero .vma o .tar.zst. |
| Aislamiento entre workloads | Crearack panic ≠ caída del llama-server, y viceversa. Reinicios independientes. |
| Migración futura | Si compramos un 2º Hetzner, cluster Proxmox + live migration entre nodos. |
| Recursos elásticos | Reasignación CPU/RAM por workload sin reinstalar. |
| Storage avanzado (ZFS) | Snapshots filesystem, deduplicación, compresión LZ4 transparente, scrubbing. |
Coste
- Overhead host: ~3–5 % CPU + ~1–2 GB RAM. En 64 threads / 256 GB es ruido.
- Overhead LXC: ~0 % (procesos sobre kernel host).
- Overhead KVM: ~5 % CPU + RAM dedicada (no shared).
2. Versiones y datos del stack
| Componente | Versión |
|---|---|
| Proxmox VE | 9.1.9 (sesión 45) |
| Base OS | Debian 13 “Trixie” |
| Kernel | proxmox-kernel-7.0.0-3-pve-signed |
| QEMU | 10.1 |
| LXC | 6.0 |
| OpenZFS | 2.4.1 (zfs-2.4.1-pve1) |
| Ceph | Squid (no usado en single-node) |
| Soporte EOL | ~2030 (sigue ciclo Debian 13) |
Mejoras destacadas PVE 9.1
- vTPM mejorado — útil para Windows 11 / cifrado guests (no aplica de momento).
- SDN avanzado — VRF, VXLAN multicast, BGP-EVPN. Reservado para cluster futuro.
- Ceph Squid — irrelevante en single-node.
- APT deb822 — formato moderno de sources.
3. Arquitectura final
┌──────────────────────────────────────────────────────────────────┐
│ HOST: pve-epyc-02 (Proxmox VE 9.1.9, kernel 7.0.0-3-pve) │
│ EPYC 7502P 32c/64t · 256 GB DDR4-2933 ECC · ~187 GB/s bandwidth │
├──────────────────────────────────────────────────────────────────┤
│ │
│ STORAGE (4× NVMe Toshiba KXD51RUE960G U.2 894 GB) │
│ ├─ md0/md1/md2 (mdadm RAID 1 sobre 2× NVMe) │
│ │ ├─ /boot 1G ext4 │
│ │ ├─ swap 8G │
│ │ └─ / 885G ext4 (host root + /var/lib/vz LXC rootfs) │
│ ├─ pool ZFS `llamacpp` (single NVMe 888 GB, by-id) │
│ │ └─ llamacpp/models → bind LXC 100 → /var/lib/llamacpp/models │
│ └─ pool ZFS `backup` (single NVMe 888 GB, by-id) │
│ ├─ backup/zfs-snapshots │
│ └─ backup/lxc-dumps → vzdump destination │
│ │
│ NETWORK │
│ ├─ enp195s0 → NIC pública (23.88.74.167/26) │
│ ├─ vmbr1 → bridge interno LXC (10.10.0.1/24, NAT MASQUERADE) │
│ └─ tailscale0 → VPN privada (CGNAT 100.64.0.0/10) │
│ │
│ GUESTS │
│ └─ LXC 100 crearack-llama-epyc-02 (24 cores físicos · 64 GB) │
│ · cgroup pin: lxc.cgroup2.cpuset.cpus = 8-31 │
│ · llama.cpp b8984 build from source (NATIVE Zen 2) │
│ · Gemma 4 26B-A4B-it Q4_K_M + mmproj-F16 │
│ · listening :8080 (vía Tailscale 100.80.142.60) │
│ │
│ MARGEN: 32+ vCPU + 192 GB RAM + 1.7 TB ZFS libres │
└──────────────────────────────────────────────────────────────────┘
Decisión LXC vs KVM por workload
| Guest | Tipo | Razón |
|---|---|---|
| llama-server | LXC unprivileged | CPU-puro, 0 % overhead, performance bare-metal. |
| crearack-prod (futuro) | VM KVM completa | Aislamiento total, kernel propio, Docker nativo sin quirks (nesting, keyctl, AppArmor profiles), migración limpia entre nodos, backup completo (kernel + estado). |
KVM es el patrón canónico para workloads stateful con base de datos. LXC con Docker funciona pero con fricciones que en producción crítica no merecen la pena. Para inference CPU sin BD ni estado persistente, LXC gana sin discusión.
Memoria — reparto explícito
| Componente | Reserva |
|---|---|
| Host Proxmox + servicios | 6 GB |
| ZFS ARC (cap explícito) | 8 GB |
| 1G hugepages reservadas (LLM) | 12 GB |
| LXC 100 llama-server | 64 GB |
| VM 200 crearack-prod (futuro) | 64-96 GB |
| Margen libre | 60-80 GB |
| Total | 256 GB |
⚠️ ARC ZFS por defecto se autotunea, pero fijamos manualmente 8 GB porque los datos calientes son los modelos LLM (que carga llama.cpp directamente en RAM con
--no-mmap) y queremos toda la RAM disponible para guests.
Bandwidth de RAM — el factor crítico
EPYC 7502P (Zen 2 Rome) tiene 8 canales DDR4 nativos. Con 8 DIMMs llenos a DDR4-2933 (frecuencia oficial Rome con 1 DPC × 2 ranks): ~187 GB/s teóricos, ~140-160 GB/s reales. Para inference LLM en CPU, el bandwidth de RAM es el cuello de botella dominante (cada token decode requiere leer todos los pesos activos del modelo). Con 4 canales DDR4-3200 estaríamos en ~102 GB/s teóricos: la elección de DIMMs llenos es lo que hace viable este host.
4. Runbook de instalación (sesión 45)
Paso 0 — Compra y entrega
- Hetzner Server Auction → buscar EPYC 7502P 256 GB en 8 DIMMs (verificar con support: “8× 32 GB DDR4 ECC reg, populated in all channels”). 4× SSD U.2 NVMe ≥ 960 GB.
- Tras pago, Hetzner deja el server en rescue Linux.
- Recibirás IPv4, IPv6, public key registrada en Hetzner Robot.
- Antes del provisionar: generar Tailscale auth-key reusable 24 h en
https://login.tailscale.com/admin/settings/keys.
Paso 1 — Inspección hardware en rescue
ssh -i ~/.ssh/hetzner_llama_ed25519 root@<IP>
uname -a
lsblk -o NAME,SIZE,TYPE,MODEL
grep -E 'model name|cpu cores' /proc/cpuinfo | head -2
free -h
dmidecode -t memory | grep -E 'Size:|Speed:' | grep -v 'No Module' | head -20
Verificar:
- 32 cores físicos / 64 threads.
- 251+ GiB RAM (256 GB ECC reportan ~251 GiB tras reserva BIOS).
- 4× NVMe del modelo esperado.
- DDR4-2933 (esperado con 8 DIMMs llenos en Rome — DDR4-3200 sería con menos DIMMs).
- Mapeo serial → device path por by-id, anotarlo.
Paso 2 — installimage Debian 13 Trixie + mdadm RAID 1 ext4 root
Hetzner installimage NO soporta ZFS root. Solo
simple-debian64-noraid|raid|raid-lvmcon ext4. Opciones reales: A) installimage con mdadm RAID 1 + ext4 root, ZFS solo en pools de datos; B) Proxmox VE ISO via vKVM (ZFS root nativo, pero requiere reservar slot vKVM). Aquí elegimos A — más rápido (~10 min) y los snapshots críticos están en los pools de datos, no en root.
# Autosetup config
cat > /tmp/autosetup.conf <<'EOF'
DRIVE1 /dev/nvme0n1
DRIVE2 /dev/nvme1n1
# nvme2n1 y nvme3n1 NO se incluyen — quedan libres para pools ZFS
SWRAID 1
SWRAIDLEVEL 1
BOOTLOADER grub
HOSTNAME pve-epyc-02
PART /boot ext4 1G
PART swap swap 8G
PART / ext4 all
IMAGE /root/images/Debian-1303-trixie-amd64-base.tar.gz
EOF
/root/.oldroot/nfs/install/installimage -a -c /tmp/autosetup.conf
reboot
Tras reboot, las letras
/dev/nvmeXnYpueden reordenarse — los NVMe del RAID los reidentificamdadmpor superblock UUID. Para los discos de DATOS (los 2 libres), siempre/dev/disk/by-id/(footgun confirmado).
Paso 3 — Install Proxmox VE 9.1 sobre Debian Trixie
ssh root@<IP>
apt update && DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Options::='--force-confold' upgrade -y
# Add repo Proxmox no-subscription
wget -O /etc/apt/trusted.gpg.d/proxmox-release-trixie.gpg \
https://enterprise.proxmox.com/debian/proxmox-release-trixie.gpg
echo 'deb http://download.proxmox.com/debian/pve trixie pve-no-subscription' \
> /etc/apt/sources.list.d/pve-install-repo.list
apt update
DEBIAN_FRONTEND=noninteractive apt-get install -y proxmox-default-kernel
reboot # arrancar con kernel PVE
# Tras reboot al kernel PVE
ssh root@<IP>
uname -r # debe decir 7.0.0-3-pve
# postfix mail config Local-only
echo 'postfix postfix/main_mailer_type select Local only' | debconf-set-selections
echo 'postfix postfix/mailname string pve-epyc-02' | debconf-set-selections
DEBIAN_FRONTEND=noninteractive apt-get install -y proxmox-ve postfix open-iscsi chrony
DEBIAN_FRONTEND=noninteractive apt-get -y remove os-prober
DEBIAN_FRONTEND=noninteractive apt-get -y purge linux-image-amd64 'linux-image-6.12*'
update-grub
# Disable enterprise repo (dará 401 sin subscription)
mv /etc/apt/sources.list.d/pve-enterprise.sources /etc/apt/sources.list.d/pve-enterprise.sources.disabled
pveversion # pve-manager/9.1.9 (running kernel: 7.0.0-3-pve)
Paso 4 — Tuning kernel (GRUB cmdline) + governor + sysctl
cp /etc/default/grub /etc/default/grub.bak-pre-deploy
sed -i 's|^GRUB_CMDLINE_LINUX_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT="quiet consoleblank=0 mitigations=off hugepagesz=1G hugepages=12 default_hugepagesz=1G transparent_hugepage=always"|' /etc/default/grub
update-grub
# CPU governor performance idempotente (Hetzner ya lo trae así, pero por si acaso)
DEBIAN_FRONTEND=noninteractive apt-get install -y linux-cpupower
cat > /etc/systemd/system/cpu-performance.service <<'EOF'
[Unit]
Description=Set CPU governor to performance
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/bin/bash -c 'for c in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do echo performance > "$c"; done'
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now cpu-performance.service
# Sysctl tuning
cat > /etc/sysctl.d/99-pve-epyc.conf <<'EOF'
vm.swappiness = 1
vm.overcommit_memory = 1
net.ipv4.ip_forward = 1
EOF
sysctl -p /etc/sysctl.d/99-pve-epyc.conf
reboot # aplicar GRUB cmdline
NO añadir
isolcpus/nohz_full/rcu_nocbs— rompen el load-balancing del scheduler para procesos OpenMP/llama.cpp y la performance cae 16× (footgun crítico documentado abajo).
Verificar tras reboot:
cat /proc/cmdline # debe incluir mitigations=off + hugepages 1G
grep -E 'HugePages_Total|Hugepagesize' /proc/meminfo # 12 × 1 GB
cat /sys/kernel/mm/transparent_hugepage/enabled # [always]
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor # performance
Paso 5 — Network (vmbr1 + iptables FORWARD)
cp /etc/network/interfaces /etc/network/interfaces.bak-pre-deploy
cat >> /etc/network/interfaces <<'EOF'
auto vmbr1
iface vmbr1 inet static
address 10.10.0.1/24
bridge-ports none
bridge-stp off
bridge-fd 0
post-up iptables -t nat -A POSTROUTING -s 10.10.0.0/24 -o enp195s0 -j MASQUERADE
post-up iptables -I FORWARD 1 -i vmbr1 -j ACCEPT
post-up iptables -I FORWARD 1 -o vmbr1 -j ACCEPT
post-down iptables -t nat -D POSTROUTING -s 10.10.0.0/24 -o enp195s0 -j MASQUERADE
post-down iptables -D FORWARD -i vmbr1 -j ACCEPT
post-down iptables -D FORWARD -o vmbr1 -j ACCEPT
EOF
ifup vmbr1
ip -4 addr show vmbr1
iptables -I FORWARD 1es obligatorio porque UFW reject-forward absorbe el tráfico aunqueDEFAULT_FORWARD_POLICY=ACCEPT(footgun s43).
Paso 6 — Pools ZFS sobre los 2 NVMe libres
DEBIAN_FRONTEND=noninteractive apt-get install -y zfsutils-linux
# Identificar by-id de los 2 NVMe SIN mdadm
for d in /dev/nvme0n1 /dev/nvme1n1 /dev/nvme2n1 /dev/nvme3n1; do
raid=$(lsblk -o FSTYPE $d -n 2>/dev/null | grep -c linux_raid_member || true)
serial=$(udevadm info --query=property --name=$d 2>/dev/null | grep ID_SERIAL_SHORT= | cut -d= -f2)
echo "$d: serial=$serial raid_partitions=$raid"
done
# Crear pools usando by-id (NUNCA /dev/sdX o /dev/nvmeXnY directos — footgun reorder)
zpool create -o ashift=12 -O compression=lz4 -O atime=off llamacpp \
/dev/disk/by-id/nvme-KXD51RUE960G_TOSHIBA_<SERIAL_LIBRE_1>
zfs create llamacpp/models
zpool create -o ashift=12 -O compression=lz4 -O atime=off backup \
/dev/disk/by-id/nvme-KXD51RUE960G_TOSHIBA_<SERIAL_LIBRE_2>
zfs create backup/zfs-snapshots
zfs create backup/lxc-dumps
zpool list
zfs list
# Registrar en Proxmox
pvesm add zfspool llamacpp-pool --pool llamacpp --content rootdir,images
pvesm add dir backup-storage --path /backup/lxc-dumps --content backup,iso,vztmpl
Paso 7 — Hardening (UFW + fail2ban + unattended-upgrades + SSH)
DEBIAN_FRONTEND=noninteractive apt-get install -y ufw fail2ban unattended-upgrades whois
# UFW
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp comment 'SSH temporal — se cierra tras Tailscale'
ufw deny 111 comment 'rpcbind — DDoS amplification vector'
yes | ufw enable
# fail2ban jail sshd
cat > /etc/fail2ban/jail.d/sshd.conf <<'EOF'
[sshd]
enabled = true
port = 22
filter = sshd
backend = systemd
maxretry = 3
findtime = 600
bantime = 3600
EOF
systemctl restart fail2ban
# unattended-upgrades solo Debian-Security
cat > /etc/apt/apt.conf.d/50unattended-upgrades <<'EOF'
Unattended-Upgrade::Origins-Pattern {
"origin=Debian,codename=${distro_codename},label=Debian-Security";
};
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::MinimalSteps "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false";
EOF
cat > /etc/apt/apt.conf.d/20auto-upgrades <<'EOF'
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
EOF
systemctl enable --now unattended-upgrades
# SSH harden
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak-pre-deploy
sed -i 's|^X11Forwarding yes|X11Forwarding no|; s|^#PasswordAuthentication yes|PasswordAuthentication no|; s|^#KbdInteractiveAuthentication yes|KbdInteractiveAuthentication no|' /etc/ssh/sshd_config
sshd -t && systemctl restart ssh
# Password root yescrypt 32 chars (vKVM emergencia)
ROOT_PASS=$(openssl rand -base64 32 | tr -d '/+=' | head -c 32)
HASH=$(echo -n "$ROOT_PASS" | mkpasswd -m yescrypt -s)
usermod -p "$HASH" root
echo "PASSWORD ROOT vKVM (copiar a gestor): $ROOT_PASS"
Paso 8 — Tailscale en host + cierre :22 público
DEBIAN_FRONTEND=noninteractive apt-get install -y tailscale
tailscale up --auth-key=<TS_AUTH_KEY> --hostname=pve-epyc-02 --ssh=false
tailscale status
tailscale ip -4
--ssh=falsedesde el principio (footgun s44:--ssh=trueintercepta:22por la IP Tailscale con flujo URL navegador).
Antes de cerrar :22 público — verificación humana real obligatoria (lección s44):
- Edu desde su laptop (que debe estar en el tailnet con cuenta
CreaRackSL@):ssh root@pve-epyc-02. Si entra → confirmar. - Solo entonces:
ufw allow in on tailscale0 to any port 22 proto tcp comment 'SSH via Tailscale'
yes | ufw delete allow 22/tcp
ufw status numbered
- Verificar desde fuera que
:22público da timeout y Tailscale sigue.
Paso 9 — LXC 100 unprivileged
pveam update
pveam download local ubuntu-24.04-standard_24.04-2_amd64.tar.zst
pct create 100 \
local:vztmpl/ubuntu-24.04-standard_24.04-2_amd64.tar.zst \
--hostname crearack-llama-epyc-02 \
--cores 24 \
--memory 65536 \
--swap 0 \
--net0 name=eth0,bridge=vmbr1,firewall=0,ip=10.10.0.10/24,gw=10.10.0.1 \
--nameserver '1.1.1.1 8.8.8.8' \
--unprivileged 1 \
--features nesting=1 \
--onboot 1 \
--storage local \
--rootfs local:32 \
--ostype ubuntu \
--ssh-public-keys /root/.ssh/authorized_keys
# Bind-mount: subdataset directo (NO el padre — bind-mount no atraviesa subdatasets)
mkdir -p /llamacpp/models
chown 100000:100000 /llamacpp /llamacpp/models
pct set 100 -mp0 /llamacpp/models,mp=/var/lib/llamacpp/models
# CPU pinning físico (footgun NUMA)
echo 'lxc.cgroup2.cpuset.cpus: 8-31' >> /etc/pve/lxc/100.conf
# TUN device para Tailscale dentro del LXC unprivileged
cat >> /etc/pve/lxc/100.conf <<'EOF'
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
EOF
pct start 100
pct exec 100 -- ip -4 addr show eth0
pct exec 100 -- ping -c 2 8.8.8.8
Paso 10 — llama.cpp build from source (NATIVE Zen 2)
El release oficial b8984 NO trae binario CPU x86_64 plano (solo openvino, rocm, sycl, arm64). Build from source con
-DGGML_NATIVE=ONda binarios optimizados para Zen 2 (AVX2 + FMA + AES + SHA + REPACK) — no hace faltaLD_PRELOAD libggml-cpu-haswell.soque requería el patrón anterior.
pct exec 100 -- bash -c '
DEBIAN_FRONTEND=noninteractive apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential cmake git libcurl4-openssl-dev libgomp1 libcurl4 ca-certificates wget unzip curl
cd /tmp
git clone https://github.com/ggml-org/llama.cpp.git
cd llama.cpp
git checkout b8984
cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_NATIVE=ON -DLLAMA_CURL=ON -DBUILD_SHARED_LIBS=ON
cmake --build build --config Release -j 24
mkdir -p /opt/llama.cpp/llama-b8984
cp -a /tmp/llama.cpp/build/bin/* /opt/llama.cpp/llama-b8984/
LD_LIBRARY_PATH=/opt/llama.cpp/llama-b8984 /opt/llama.cpp/llama-b8984/llama-server --version
'
Paso 11 — systemd unit llama-server (versión sesión 46 · calidad 99%)
Versión validada en sesión 46 tras afinado de visión. La inicial s45 (ctx 16384, threads 24, defaults) daba 70 % calidad en plano 70 racks. Esta versión sube a 99 % y procesa ~6 minutos.
pct exec 100 -- bash -c "cat > /etc/systemd/system/llama-server.service <<'EOF'
[Unit]
Description=llama-server (CreaRack Auto-Plan visión, b8984 native Zen 2, Iteracion A+B)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Restart=on-failure
RestartSec=5
Environment=\"LD_LIBRARY_PATH=/opt/llama.cpp/llama-b8984\"
ExecStart=/opt/llama.cpp/llama-b8984/llama-server \\
--model /var/lib/llamacpp/models/gemma-4-26B-A4B-it-UD-Q4_K_M.gguf \\
--mmproj /var/lib/llamacpp/models/mmproj-F32.gguf \\
--host 0.0.0.0 --port 8080 \\
--ctx-size 32768 \\
--threads 32 --threads-batch 32 \\
--batch-size 4096 --ubatch-size 4096 \\
--image-min-tokens 560 --image-max-tokens 560 \\
--temp 1.0 --top-p 0.95 --top-k 64 \\
--flash-attn on \\
--no-mmap --jinja
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable llama-server.service"
Por qué cada flag (sesión 46)
| Flag | Razón |
|---|---|
--mmproj mmproj-F32.gguf | Doble precisión proyección visión→LM (vs F16). +1 GB RAM, mejor lectura detalle visual. |
--ctx-size 32768 | Holgura para respuestas largas (planos densos generan ~5000-7000 tokens decode). |
--threads 32 --threads-batch 32 | Aprovechar los 32 cores del LXC tras subir cpuset a 0-31 (antes 24). Beneficia prompt processing (image encoding). |
--batch-size 4096 --ubatch-size 4096 | OBLIGATORIO con --image-max-tokens > 512: Gemma 4 vision encoder usa non-causal attention y todos los image tokens deben caber en un único ubatch. Si no, assertion non-causal attention requires n_ubatch >= n_tokens o degrada en silencio. |
--image-min-tokens 560 --image-max-tokens 560 | s47g: bajado de 2240 → 560. Presupuestos soportados Gemma 4: 70/140 (clasificación), 280/560 (chat), 1120 (OCR), 2240 (planos densos). Con prompt s47g (peripheral vision + STAR + anti-hallucination NODAL) + sin pseudo-thinking, 560 da 100% calidad en plano test 70 racks y prompt eval cae a la mitad (35.7s → 16.1s). Receta histórica s46 era 560-2240; s47g fija a 560/560. |
--temp 1.0 --top-p 0.95 --top-k 64 | Defaults oficiales Google para Gemma 4. Defaults llama.cpp (0.8/0.95/40) son subóptimos. |
--flash-attn on | Recomendación Reddit, aunque en CPU auto puede ser equivalente. No probado off/auto en s47 (las palancas A+B fueron suficientes para llegar a 4:45 estable). |
LXC: subir cores a 32
/etc/pve/lxc/100.conf:
-cores: 24
+cores: 32
-lxc.cgroup2.cpuset.cpus: 8-31
+lxc.cgroup2.cpuset.cpus: 0-31
Tras editar: pct reboot 100. Los 8 cores que se reservaban al host pasan al LXC. El host está casi idle siempre (load < 0.1).
Paso 12 — Tailscale en LXC
pct exec 100 -- bash -c '
curl -fsSL https://tailscale.com/install.sh | sh
systemctl start tailscaled
tailscale up --auth-key=<TS_AUTH_KEY> --hostname=crearack-llama-epyc-02 --ssh=false
tailscale status
tailscale ip -4'
Paso 13 — Modelos GGUF
Descargar de HuggingFace (unsloth/gemma-4-26B-A4B-it-GGUF):
pct exec 100 -- bash -c '
cd /var/lib/llamacpp/models
wget https://huggingface.co/unsloth/gemma-4-26B-A4B-it-GGUF/resolve/main/gemma-4-26B-A4B-it-UD-Q4_K_M.gguf
wget https://huggingface.co/unsloth/gemma-4-26B-A4B-it-GGUF/resolve/main/mmproj-F16.gguf
'
O si vienes migrando de otro server con los GGUFs ya descargados, rsync via Tailscale (~26 GB en ~4 min a 105 MB/s):
# En el server destino, generar key SSH efímera, autorizar en origen, rsync, limpiar
ssh root@<NUEVO> "ssh-keygen -t ed25519 -N '' -f /root/.ssh/transfer_key -q"
PUBKEY=$(ssh root@<NUEVO> "cat /root/.ssh/transfer_key.pub")
ssh root@<ORIGEN> "echo '$PUBKEY' >> /root/.ssh/authorized_keys"
ssh root@<NUEVO> "rsync -av --progress -e 'ssh -i /root/.ssh/transfer_key -o StrictHostKeyChecking=accept-new' \
root@<TS_ORIGEN>:/path/to/models/ /llamacpp/models/"
# Cleanup
ssh root@<ORIGEN> "sed -i '/transfer-rsync/d' /root/.ssh/authorized_keys"
ssh root@<NUEVO> "rm /root/.ssh/transfer_key /root/.ssh/transfer_key.pub"
Paso 14 — Smoke tests
# 1) /health desde dentro del LXC
pct exec 100 -- curl -s http://localhost:8080/health
# 2) /health desde host vía Tailscale del LXC
curl -s http://<TS_LXC>:8080/health
# 3) /health desde Crearack PROD
ssh root@crearack.com "docker exec crearack-pro-zcmvsl-web-1 curl -s http://<TS_LXC>:8080/health"
# 4) Inferencia mínima (sin thinking)
curl -s -X POST http://<TS_LXC>:8080/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"gemma","messages":[{"role":"user","content":"Di hola"}],"max_tokens":20,"chat_template_kwargs":{"enable_thinking":false}}'
Paso 15 — Switch Crearack PROD a este host
Edu en panel Dokploy → crearack-pro → Environment:
OLLAMA_BASE_URL=http://<TS_LXC>:8080
OLLAMA_MODEL=gemma-4-26B-A4B-it-UD-Q4_K_M.gguf
AUTOPLAN_PROVIDER=ollama
EDGE_AI_PROVIDER=openrouter
AUTOPLAN_PROVIDER=ollamaaunque NO usemos Ollama: el driver Python se llama así pero apunta a:8080llama-server (OpenAI-compatible API).
Save + Deploy. Verificar Auto-Plan UI con plano 70 racks.
5. Operación día a día
UI Proxmox
- URL:
https://23.88.74.167:8006(o vía Tailscale:https://pve-epyc-02:8006). - Login:
root@pamcon la password yescrypt seteada en Paso 7. (NO la rescue.) - 2FA: configurar TOTP en PVE 9.1 (UI → Datacenter → Permissions → Two Factor).
Backups (vzdump nightly)
# Datacenter → Backup → Add
# - Storage: backup-storage (= /backup/lxc-dumps)
# - Schedule: 03:00 daily
# - Selection: VMs/CTs all
# - Mode: snapshot
# - Compression: zstd
# - Retention: keep-daily=7, keep-weekly=4, keep-monthly=3
Restore: pct restore (LXC) o qmrestore (VM) desde fichero .tar.zst o .vma.zst en /backup/lxc-dumps.
Snapshots manuales antes de upgrade
pct snapshot 100 pre-llama-upgrade
# rollback:
pct rollback 100 pre-llama-upgrade
Monitorización
- UI Proxmox muestra CPU/RAM/disk/net por host y guest.
journalctl -fu llama-server.serviceen LXC para ver requests + timing.- Para alertas externas:
prometheus-pve-exporter(replicar patrón de Crearack PROD con node-exporter).
Mantenimiento del host — actualizaciones (apt upgrade)
unattended-upgrades corre con perfil solo Debian-Security (Paso 7), así que parches críticos entran automáticamente. Para el resto (Proxmox +pmx* rebadges, microcódigo CPU, kernel) hay que correr apt upgrade manual.
Comprobar estado desde PowerShell:
ssh root@pve-epyc-02 "apt update && apt list --upgradable 2>/dev/null"
ssh root@pve-epyc-02 "ls /var/run/reboot-required 2>/dev/null && cat /var/run/reboot-required.pkgs 2>/dev/null || echo 'No reboot required'"
Lanzar el upgrade desde PowerShell (one-liner, ~30-60 s):
ssh root@pve-epyc-02 "apt update && DEBIAN_FRONTEND=noninteractive apt upgrade -y"
DEBIAN_FRONTEND=noninteractiveevita prompts sobre/etc/default/grub,/etc/lvm/lvm.conf, etc. Mantiene la versión local por defecto. Útil porque ya hicimos backup de los configs críticos en.bak-pre-deploydurante el setup.
Sesión interactiva (mejor para ver cada cambio):
ssh root@pve-epyc-02
# ya dentro:
apt update && apt upgrade
Qué requiere reboot y qué no
| Tipo de paquete | Ejemplo típico s46 | Reboot |
|---|---|---|
Kernel proxmox-kernel-* | 7.0.0-4-pve reemplaza 7.0.0-3-pve | Sí, para entrar al nuevo |
amd64-microcode | 3.20251202 reemplaza 3.20250311 | Sí, para que el CPU cargue el microcódigo |
grub-* (+deb13u1 → +pmx2) | Rebadge Proxmox del bootloader | No (aplica al próximo arranque solo) |
lvm2, dmsetup, dmeventd | Rebadges +pmx1 | No |
Librerías sistema (openssl, glibc) | Si toca paquetes activos | Solo si los servicios se reinician — needrestart lo gestiona |
Heurística: si /var/run/reboot-required existe tras el upgrade → reboot recomendado.
Reboot del host — qué pasa con los guests
ssh root@pve-epyc-02 "reboot"
El host vuelve en ~2 min. El LXC crearack-llama-epyc-02 arranca solo porque tiene onboot=1. llama-server.service arranca al boot del LXC, así que Auto-Plan vuelve a estar disponible automáticamente. Verificar tras reboot:
ssh root@pve-epyc-02 "pct status 100 && pct exec 100 -- systemctl is-active llama-server.service"
ssh root@pve-epyc-02 "pct exec 100 -- curl -s http://localhost:8080/health"
LXC: actualizar por separado
El apt upgrade del host no toca los paquetes dentro del LXC. Para mantener el LXC al día:
ssh root@pve-epyc-02 "pct exec 100 -- bash -c 'apt update && DEBIAN_FRONTEND=noninteractive apt upgrade -y'"
Si el upgrade dentro del LXC requiere “reboot” (raro en LXC: solo si toca systemd del propio container):
ssh root@pve-epyc-02 "pct restart 100"
El LXC unprivileged comparte kernel con el host, así que un
linux-image-*dentro del container no se aplica realmente — solo cuenta el kernel del host.
Snapshot antes de upgrades arriesgados
Para upgrade de proxmox-ve meta-paquete o cambios mayores en kernel:
# Snapshot del LXC (rápido, ZFS/local-storage)
ssh root@pve-epyc-02 "pct snapshot 100 pre-apt-upgrade-$(date +%Y%m%d)"
# Tras verificar que todo va bien, cleanup:
ssh root@pve-epyc-02 "pct delsnapshot 100 pre-apt-upgrade-<fecha>"
# Si algo se rompió:
ssh root@pve-epyc-02 "pct rollback 100 pre-apt-upgrade-<fecha>"
Para el host Proxmox no hay snapshot nativo (root es ext4 sobre mdadm, no ZFS). Si el upgrade del host es muy crítico, hacer vzdump de los guests primero y, en caso de desastre, reinstalar host + restaurar guests (ver sección 6).
6. Rollback / Disaster Recovery
Caída del LXC llama-server
pct stop 100 && pct start 100.- Si no arranca:
pct rollback 100 <último-snapshot-bueno>. - Si pool ZFS corrupto:
zpool scrub llamacpp, esperar resultado. - Si HW: restaurar desde último vzdump en
/backup/lxc-dumpsa un nuevo host Proxmox.
Caída del host Proxmox completo
- Hetzner Robot → reinstalar Debian 13 Trixie vía installimage (mismo autosetup) + Proxmox VE encima.
- Reimportar pools (
backupyllamacppsobreviven si solo fallan los del RAID):zpool import backup; zpool import llamacpp. - Restaurar guests:
pct restore 100 /backup/lxc-dumps/vzdump-lxc-100-*.tar.zst --storage local. - Re-aplicar Tailscale auth keys en host y guests.
Pérdida total de discos
- BD Crearack PROD futura → restaurar desde último dump SQL externo.
- Modelos LLM → re-descargar de HuggingFace (~5 min Tailscale).
- Configuración Proxmox → reaplicar este runbook.
Capas de acceso de emergencia
- Tailscale en cualquier dispositivo de Edu logueado con
CreaRackSL@. Recomendable: laptop + móvil instalados. - Salto desde Crearack PROD como bastion (vía SSH key).
- Hetzner vKVM con password root (yescrypt 32 chars en gestor de Edu).
- Hetzner rescue boot con SSH key registrada en cuenta Robot.
7. Roadmap del host
| Fase | Estado | Sesión |
|---|---|---|
| Setup completo Proxmox + LXC + llama.cpp | ✅ Done | 45 |
| Smoke test plano 70 racks (calidad ~70 % / recall 91 %) | ✅ Done | 45 |
| Afinado calidad Auto-Plan 70 → 99 % (flags vision Reddit + 32 cores + mmproj F32) | ✅ Done | 46 |
| Apt upgrade host + reboot + microcode CPU activado | ✅ Done | 46 |
Velocidad: bajar 6 min → 3 min (objetivo Edu) — probar --flash-attn off/auto, --image-max-tokens 1120 | 🔥 Prioridad | 47 |
| Último 1 % calidad (perímetro exterior) — pipeline híbrido GLM-OCR + Gemma 4, o subir quant Q6_K_XL | 🔜 Sesión dedicada | 47+ |
| Quants superiores Q5_K_M / Q6_K_XL / Q8_K_XL (UD) | 🔜 Sesión dedicada | 47+ |
| Backups off-site nightly (Storage Box o cross-server) | 🔜 Tras 1 semana estable | 47+ |
| VM 200 crearack-prod en paralelo a CCX actual | Planeado | sesión futura |
| DNS cutover crearack.com → este host | Planeado | sesión futura |
| Cancelar CCX viejo crearack.com | Planeado | tras estabilización |
| Cluster Proxmox 2-node (opcional) | Largo plazo | TBD |
8. Decisiones documentadas
| Decisión | Razón | Fuente |
|---|---|---|
| Proxmox VE 9.1.9 (no 8.x) | Última estable, soporte hasta ~2030 | docs PVE |
| Debian 13 Trixie + Proxmox encima (no installimage Proxmox) | Hetzner installimage no tiene template Proxmox. Patrón canónico: Debian + apt install proxmox-ve | s45 |
mdadm RAID 1 + ext4 para / (no ZFS root) | Hetzner installimage no permite ZFS root. Compromiso aceptado: sistema host es estático y rebuilable; los snapshots los queremos en datos (LXC + GGUFs + backups) que sí van en ZFS | s45 |
2 pools ZFS separados llamacpp + backup (no único tank) | Aislar I/O: experimentos con quants en llamacpp sin afectar destino vzdump | s45 |
| ARC limit configurable (8 GiB recomendado) | Modelos LLM no se cachean filesystem; toda la RAM para llama.cpp y guests | docs PVE |
| LXC para llama-server | CPU-puro, 0 % overhead, performance bare-metal | comunidad PVE |
| KVM para Crearack futuro | Aislamiento total, Docker nativo sin quirks, migración limpia | docs PVE |
Build llama.cpp from source con -DGGML_NATIVE=ON | Release oficial b8984 no incluye binario CPU x86_64 plano. Build nativo en Zen 2 evita el LD_PRELOAD libggml-cpu-haswell.so que requería el patrón antiguo, y aprovecha AVX2+FMA+AES+SHA+REPACK específicos | s45 |
24 cores LXC (cpuset.cpus = 8-31) y --threads 24 | Aprovechar más cores con bandwidth doblado (8 canales DDR4); rendimientos decrecientes por encima de 24 hacen innecesario más | s45 |
Pool ZFS por /dev/disk/by-id/ (no /dev/sdX / /dev/nvmeXnY) | El kernel reordena nombres de device entre boots. by-id es estable | footgun confirmado |
| Bind-mount LXC a subdataset directo (no al padre) | Bind-mount no atraviesa subdatasets ZFS. Si el dataset padre tiene hijos montados, se ven vacíos en el LXC | footgun s45 |
| Sin Ollama en este host | Patrón antiguo daba problemas con enable_thinking y formatos propietarios. llama.cpp directo respeta el campo correctamente | s45 |
Tailscale --ssh=false desde el principio | El flag --ssh=true intercepta :22 por la IP Tailscale con flujo URL navegador, molesto para flujo tradicional con keys | footgun s44 |
Cierre :22 público SOLO con verificación humana real previa | Lección s44: cerrar sin verificar deja al humano fuera si Tailscale no estaba en su laptop. Prueba ssh root@<host> desde laptop antes de cerrar | footgun s44 |
9. Footguns conocidos
| Footgun | Mitigación |
|---|---|
Reordenamiento /dev/nvmeXnY tras reboot | Kernel asigna nombres según orden de detección. En s45 vimos cómo nvme0+nvme1 (rescue) pasaron a nvme0+nvme2 (boot final). SIEMPRE usar /dev/disk/by-id/ para discos no-RAID. Los del RAID los reidentifica mdadm por superblock. |
| Hetzner installimage NO soporta ZFS root | Solo mdadm + ext4/btrfs. Para ZFS root completo: vKVM + ISO oficial Proxmox. |
Repo pve-enterprise.sources da 401 sin subscription | Apt update queda roto. Renombrar a .disabled. Apt install proxmox-ve REAÑADE el repo, hay que renombrar tras cada install/upgrade del meta-paquete. |
cpufrequtils deprecated en Trixie | Usar linux-cpupower + systemd unit propio para set governor (sample en Paso 4). |
| isolcpus / nohz_full / rcu_nocbs en EPYC con LLM | Rompen scheduler load-balancing para procesos OpenMP/llama.cpp → todos los threads pelean por 1 core, performance cae 16×. NO usar para esta carga. |
| vmbr1 + iptables solo runtime no persisten | Stanza canónica en /etc/network/interfaces con post-up iptables -I FORWARD 1 (porque UFW reject-forward absorbe el tráfico aunque DEFAULT_FORWARD_POLICY=ACCEPT). |
| LXC unprivileged + Tailscale sin TUN | Falla con failed to connect to local tailscaled. Añadir al config LXC lxc.cgroup2.devices.allow: c 10:200 rwm + lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file. |
| LXC sin DNS tras reboot | pct set 100 -nameserver "1.1.1.1 8.8.8.8" para persistir. |
| Bind-mount LXC no atraviesa subdatasets ZFS | Si tienes dataset pool/foo con subdataset pool/foo/bar y bind-mount al padre pool/foo, dentro del LXC bar/ se ve vacío. Solución: bind-mount al subdataset directo (pool/foo/bar -> /var/lib/foo/bar), o que NO haya subdataset (todo plano en el dataset padre). |
| Release llama.cpp b8984 no trae binario CPU x86_64 plano | Solo arm64, openvino, rocm, sycl, s390x. Build from source con -DGGML_NATIVE=ON da mejor performance que cualquier binario genérico. |
Cerrar :22 público sin verificación humana real | Lección s44: el agente cerró asumiendo que el laptop de Edu estaba en el tailnet — no lo estaba → lockout breve. Antes de ufw delete allow 22/tcp, Edu prueba ssh root@<host> desde su laptop y confirma. Solo entonces cerrar. |
Tailscale --ssh=true molesta con flujo URL navegador | --ssh=false desde el principio + usar OpenSSH normal con key. |
Tailscale auth-key one-shot tras NoState | Generar key fresca en panel + reaplicar tailscale up con la nueva. |
| Gemma 4 thinking gasta tokens en reasoning si max_tokens bajo | Subir max_tokens a 1000+ o desactivar thinking con chat_template_kwargs.enable_thinking=false en cada llamada. |
nvme format -s 1 cripto-erase puede no estar soportado en algunos firmwares | Backup: wipefs -a + blkdiscard antes de cancelar. Hetzner reformatea discos antes de reasignar de todas formas. |
10. Resultados sesión 45 (01-05-2026) — migración a hardware con 8 canales
Stack final adoptado
- Vehículo: llama.cpp build b8984 from source con
-DGGML_NATIVE=ON(Zen 2 native). - Modelo:
unsloth/gemma-4-26B-A4B-it-GGUF—gemma-4-26B-A4B-it-UD-Q4_K_M.gguf(15.7 GB) +mmproj-F16.gguf(1.1 GB). - Listening:
:8080en LXC, accesible vía Tailscale100.80.142.60:8080desde Crearack PROD. - Crearack PROD env vars (verificar tras switch en Dokploy):
AUTOPLAN_PROVIDER=ollama OLLAMA_BASE_URL=http://100.80.142.60:8080 OLLAMA_MODEL=gemma-4-26B-A4B-it-UD-Q4_K_M.gguf EDGE_AI_PROVIDER=openrouter
Smoke tests (decode tokens/seg)
| Setup | tps decode | tps prompt processing |
|---|---|---|
| Inferencia mínima (10 tokens) | 24.68 tps | 99.85 tps |
| Inferencia “HOLA” desde PROD | 32.52 tps | 109.83 tps |
| Plano 70 racks Auto-Plan UI | ~26-27 tps (estimado) | 91.85 tps |
Smoke test plano 70 racks (Auto-Plan UI, vía Crearack PROD)
| Métrica | Resultado |
|---|---|
| Tiempo total | ~3:30 estimado (vs 7:33 patrón antiguo con 4 canales) |
| Recall numérico (racks detectados) | 62/68 (~91 %) |
| Calidad visual (a ojo, Edu) | ~70 % |
| HTTP status | 200 OK desde 100.126.106.56 |
| Tokens generados | 5307 (decode) tras 1402 (prompt) |
| Errores / crashes / OOM | 0 |
Veredicto: migración exitosa. Velocidad ~×1.85 vs patrón antiguo (acorde con expectativa de bandwidth doblado). Calidad estable o ligeramente mejor (variación natural sin seed fija). Cableado y detalle siguen siendo techo del modelo 26B-A4B Q4_K_M; subir Q6_K/Q8_0 sigue siendo la deuda viva para empujar 70→80 %.
Cambios persistidos en infraestructura
- GRUB
/etc/default/grubconmitigations=off hugepagesz=1G hugepages=12 default_hugepagesz=1G transparent_hugepage=always. Backup en.bak-pre-deploy. - /etc/network/interfaces con
vmbr1estática + iptables FORWARD ACCEPT. Backup en.bak-pre-deploy. - LXC 100
pct set 100 -nameserver "1.1.1.1 8.8.8.8"+onboot=1+ cpuset 8-31 + TUN config. - llama.cpp
/opt/llama.cpp/llama-b8984/build nativo Zen 2 (sin LD_PRELOAD). - Modelos GGUF
/var/lib/llamacpp/models/(bind-mount al subdatasetllamacpp/models). - systemd unit
/etc/systemd/system/llama-server.servicecon--ctx-size 16384 --threads 24 --threads-batch 24 --no-mmap --jinja. - Tailscale instalado en host (
pve-epyc-02) y LXC (crearack-llama-epyc-02), con--ssh=false. - Hardening seguridad UFW (default deny in, only
:22 on tailscale0y:111 deny), fail2ban jail sshd, unattended-upgrades solo Debian-Security, SSH key-only + X11=no. - Password root yescrypt 32 chars en
/etc/shadow(en gestor de Edu para vKVM).
Reversión rápida
- A llama-server otro server: cambiar Dokploy
OLLAMA_BASE_URL=http://<otra-IP-Tailscale>:8080, redeploy. - A OpenRouter cloud:
AUTOPLAN_PROVIDER=openrouter+ asegurarOPENROUTER_API_KEY=.... Calidad limitada en visión (verificado s40) pero funcional. - LXC roto: restaurar
vzdumpdesde/backup/lxc-dumps/.
Deuda técnica abierta
- 🔥 Velocidad — bajar plano 70 racks de ~6 min a 3 min (objetivo declarado Edu para competir con Gemini cloud). Próximos experimentos s47:
--flash-attn off/auto(sospecha queonpenaliza decode tps en CPU),--image-max-tokens 1120(oficial Google, mitad de prompt processing — riesgo de perder racks pequeños). - 🔜 Último 1 % calidad (perímetro exterior del plano — top OK, lateral derecho/izquierdo y fondo perdidos). Probado en s46 con paso “OUTER PERIMETER FIRST” en METHODOLOGY → recuperaba el perímetro pero rompía el interior (NODAL derecho mal clasificado). Revertido. Caminos: pipeline híbrido GLM-OCR (
ggml-org/GLM-OCR-GGUF, 0.9B) para extraer labels + Gemma 4 para topología; o subir quant a Q6_K_XL (más conocimiento topológico). - 🔜 Quants superiores Q5_K_M (21 GB) / Q6_K_XL (23 GB) / Q8_K_XL (28 GB) UD del 26B-A4B. Cabe sin problema en RAM (LXC 64 GB).
- 🔜 Backups off-site nightly (Storage Box 3.20€/mes vs Crearack PROD como destino). Diferido por Edu hasta tener el server estabilizado 1 semana.
- 🔜 VM 200 crearack-prod + DNS cutover crearack.com a este host. Sesión futura.
12. Resultados sesión 46 (01-05-2026 tarde) — afinado calidad Auto-Plan
Cambios aplicados
- LXC cpuset 8-31 → 0-31 (24 → 32 cores). Recuperar los 8 cores reservados al host (que está casi idle).
- mmproj F16 → F32 (descarga 2.3 GB).
- systemd unit llama-server expandido con flags vision (ver Paso 11 actualizado): image-tokens 560-2240, batch/ubatch 4096, ctx 32k, temp 1.0 / top-p 0.95 / top-k 64, flash-attn on.
- Prompt Auto-Plan (
prompts/blueprint_analyst.mden CreaRack-Pro) iterado:- Criterio NODAL funcional/topológico (3+ cables = NODAL, no es cuestión de tamaño).
- STAR TOPOLOGY RULE explícita (cuando varios racks convergen en hub, conexiones rack→NODAL, no entre racks adyacentes).
- Eliminada línea contradictoria que inducía cableado en cadena.
- Refuerzo WALLS / FLOOR DIVIDERS.
- Eliminada instrucción positiva ambigua del bloque NEGATIVE CONSTRAINTS.
Iteraciones de calidad (plano test 70 racks)
| Iteración | Calidad | Tiempo | Notas |
|---|---|---|---|
| s45 baseline | 70 % | 3:30 | Flags por defecto, 24 cores, mmproj F16 |
| s46 ajuste 1 (flags Reddit + 32c + mmproj F32) | 95 % | 6:19 | Sin tocar prompt |
| s46 ajuste 3 (criterio NODAL + STAR + anti-sesgo) | 96-97 % | 6:05 | Resuelve A08 + cableado estrella |
| s46 ajuste 4 (refuerzo WALLS / floor dividers) | 99 % | 6:21 | Recupera divisiones de pisos. Hotfix _clean_llm_json para JSON malformado |
| s46 ajuste 5 (sin línea contradictoria NEGATIVE CONSTRAINTS) | 99 % estable | 6:03 | Estado final s46 |
| s46 ajuste 6 (OUTER PERIMETER FIRST) | bajó a ~93 % | 6:03 | Revertido — recuperaba perímetro pero rompía interior |
| s47d (image 1120, thinking ON, prompt nuevo SYSTEM ROLE expert) | 100 %+ | 10:20 | Thinking añadía ~10 800 tokens de output sin mejora de calidad |
| s47e (image 1120, thinking OFF, prompt nuevo) | 100 % | 5:53 | Apagar thinking recupera velocidad. JSON 11 893 chars |
| **s47g (image 560, prompt nuevo SIN bloque `< | think | >`)** | 100 % |
Insight crítico — _clean_llm_json patch
Gemma 4 26B-A4B Q4_K_M genera JSON con clave malformada en outputs largos: "x2: 380 en vez de "x2": 380 (falta comilla cierre). Detectado al fallar plano s46 ajuste 4 con HTTP 500. Patch en blueprints/services/autoplan.py::_clean_llm_json con regex ([{,]\s*)"([A-Za-z_][\w-]*): → \1"\2": aplicado solo en contexto de clave (precedido por { o ,) para no romper strings de valor con : legítimo. Probabilidad de glitch crece con número de tokens decode.
Mantenimiento del host aplicado
apt upgradehost: 13 paquetes (amd64-microcode+grub-*rebadge Proxmox +lvm2/dmsetup).- Reboot host: ~50 s. LXC arrancó solo (
onboot=1+enablellama-server). - Microcode CPU activado:
0x08301072 → 0x0830107c(early-loading desde initramfs durante boot). Confirmado enjournalctl -k.
Smoke test final s46
| Métrica | Valor |
|---|---|
| Calidad subjetiva (Edu) | 99 % |
| Tiempo total | ~6:03 (363s) |
| Prompt input | 2586 tokens |
| Decode output | 6185 tokens |
| Prompt tps | 53.74 |
| Decode tps | 18.54 |
| Errores / OOM / crashes | 0 |
Cierre s47 — palancas exploradas (02-05-2026)
Aplicadas (efectivas):
- A.
--image-max-tokens 1120 → 560: prompt eval −55% (35.7s → 16.1s). - B. Quitar bloque
<|think|>literal del prompt: generation −18% (8095 → 6611 tokens output). - Resultado combinado: 5:53 → 4:45 (−20%) con calidad 100%.
Descartadas (footguns multimodal):
--cache-reuse 256(prefix caching): el server lo desactiva silenciosamente concache_reuse is not supported by multimodal, it will be disabled. Documentado en memoria localfootguns_llamacpp_cache_reuse_multimodal.md.--model-draft <gemma-4-2b.gguf>(speculative decoding): el server crashea conspeculative decoding is not supported with multimodal. Issue upstream llama.cpp #19712 abierto en feb 2026, sin merge a 02-05-2026. Documentado enfootguns_llamacpp_speculative_multimodal.md.
Pendiente para s48+
- Velocidad: 4:45 → 3:00 (objetivo declarado Edu, parcialmente cumplido). Bloqueado upstream — esperar merge de issue #19712 (speculative decoding multimodal). Cuando merge → ganancia esperada 1.5-2× en generation = 3:00-3:30 estimado.
- Alternativas a evaluar si urge: cambio de motor (vLLM / exllama2 con caching multimodal), cambio de modelo VL más pequeño (riesgo calidad alto, ya descartado en s40-s41).
- Cerrar último 1 % perímetro: pipeline híbrido GLM-OCR + Gemma 4, o subir quant a Q6_K_XL (23 GB UD) / Q8_0 (25 GB).
11. Referencias externas
- Proxmox VE Admin Guide: https://pve.proxmox.com/pve-docs/pve-admin-guide.html
- Hetzner installimage docs: https://docs.hetzner.com/robot/dedicated-server/operating-systems/installimage/
- ZFS on Linux: https://openzfs.github.io/openzfs-docs/
- llama.cpp releases: https://github.com/ggml-org/llama.cpp/releases
- Memoria local Edu
project_hetzner_llama_server_gemma4.md— historial de sesiones previas (s41-s44 sobre hardware antiguo, s45 sobre este host).
Véase también
- [[crearack-tech—admin—server-management]] — gestión Hetzner CCX/CX23 actuales (ver evolución hacia este host)
- [[crearack-tech—admin—hetzner-ssh-setup]] — flujo SSH-key Hetzner (mismo patrón aquí)
- [[crearack-tech—admin—dokploy-guide]] — Dokploy guía (futuro: contenedor en VM 200)
- [[crearack-tech—admin—backup-restore]] — backup/restore CreaRack PROD (interactúa con vzdump local)
- [[crearack-tech—guides—disaster-recovery]] — DR maestro CreaRack
- [[crearack-tech—guides—production-deployment]] — guía deploy producción (relevante para VM 200)