Ivan Micai
← Voltar para o blog

24 horas resgatando meu TrueNAS: o caso do pool que panicava no boot

Publicado em

Se você tem um homelab, já sabe: algumas terças-feiras você acorda achando que vai só ajustar um detalhe e termina o dia redescobrindo ZFS internals, logs de kernel e a paciência que você pensava ter perdido. Esse post é o relato de 24h caçando dois bugs simultâneos que derrubavam meu TrueNAS toda hora — um do driver NVIDIA e outro de corrupção de metadata num pool ZFS.

Spoiler: deu tudo certo no fim. Perdi 5 arquivos de mídia no caminho.


O setup

O servidor rodava:

  • TrueNAS SCALE 26.04-MASTER (nightly — primeiro erro, já adianto)
  • Kernel 6.18.1 (nightly também)
  • 2× GPUs NVIDIA Blackwell (série 50, hardware recente)
  • Driver NVIDIA open module 590.44.01 (nightly)
  • PSU com folga, 62 GiB RAM

Os pools (organização típica de homelab com vários discos):

FunçãoTopologiaTamanho
BootNVMe single~500 GB
Mídiaraidz2 (4 HDDs)~29 TB
Apps + projetosraidz2 (4 SSDs)~3.6 TB — o problemático
Pools avulsossingle-diskvários

Em cima disso, 30+ containers: stack *arr, Plex, Pi-hole, Netdata, Portainer, Tailscale, plataforma de deploy (tipo Coolify), ferramentas de LLM local, database managers, bots, game servers, observabilidade. O servidor fazia de tudo — e fazia bem, até parar de fazer.


Os sintomas

Comecei a notar reinícios espontâneos. Não com panic visível, só… sumia da rede e voltava minutos depois.

Depois de uma rodada de reinícios mais pesada, o servidor entrou em boot loop — subia, ficava uns 13-14 minutos no ar, caía, reiniciava. Repetidas vezes.

Ironia: comecei a investigar achando que era um app novo que tinha instalado recentemente. Foi o maior red herring da semana.


Descoberta 1: o driver NVIDIA está morrendo

Primeira coisa útil que achei no journalctl:

kernel: NVRM: iovaspaceDestruct_IMPL: 1 left-over mappings in IOVAS 0x200
kernel: NVRM: GPU1 nvAssertFailedNoLog: Assertion failed: pIOVAS != NULL @ io_vaspace.c:592
kernel: BUG: unable to handle page fault for address: 0000000200000100

Assertion do driver NVIDIA na destruição de um IOVA space, seguido imediatamente por page fault e reset. Clássico bug do driver 590.x open com kernel 6.18 nightly em arquitetura Blackwell (GPU Série 50, lançada em 2025). Combinação nightly + hardware novo = instável.

Desabilitei os workloads que usavam GPU e o sistema estabilizou por umas horas. “Resolvi”. Fui dormir achando que era só questão de não usar GPU até sair release stable.

Não era só isso.


Descoberta 2: o panic no boot

No dia seguinte, outro reboot. E outro. E outro. Boot após boot, o sistema morria exatamente quando o ix-zfs.service começava a importar os pools. Nem o panic trace sobrevivia — o kernel travava tão rápido que o journalctl não conseguia fazer flush.

Aí veio o primeiro workaround: editar o GRUB manualmente a cada boot e adicionar:

systemd.mask=ix-zfs.service

Sem o ix-zfs, o middleware do TrueNAS não tenta importar pools. O sistema boota, fica estável, e eu tenho SSH.

Tentei persistir esse parâmetro:

# Primeira tentativa: /etc/default/grub (não existe no TrueNAS)
# Segunda tentativa: /etc/default/grub.d/truenas.cfg
sudo sed -i 's|nvme_core.multipath=N|nvme_core.multipath=N systemd.mask=ix-zfs.service|' \
  /etc/default/grub.d/truenas.cfg
sudo update-grub

Funcionou — 10 menuentries no grub.cfg com o parâmetro. Até o middleware do TrueNAS regerar o arquivo e apagar tudo. 😤

Solução final: systemctl mask via symlink, que é um dataset per-BE (/etc é um dataset separado em cada Boot Environment) e portanto persiste:

sudo systemctl mask ix-zfs.service
# Cria /etc/systemd/system/ix-zfs.service → /dev/null

Esse o middleware não reverte. Pool não importa no boot, sistema sobe limpo.


Descoberta 3: qual pool quebra?

Com o SSH estável, parti pra caçar qual pool panicava o kernel. Estratégia: importar um por vez, em modo readonly, sem cache persistente:

sudo zpool import -o cachefile=none -o readonly=on -N POOL_A   # OK
sudo zpool import -o cachefile=none -o readonly=on -N POOL_B   # OK
sudo zpool import -o cachefile=none -o readonly=on -N POOL_C   # OK
# ... todos os pools importam em readonly sem problema

Teste crítico: importar o pool de apps em RW:

sudo zpool import -o cachefile=none -N POOL_APPS

Kernel panic imediato. SSH caiu. Servidor reboot.

Achei. O pool tinha metadata corrompido que só era processado em modo read-write. Especificamente, o feature log_spacemap estava active:

POOL_APPS  feature@log_spacemap  active  local

Esse feature loga mudanças de space-map em TXGs pendentes. Se o pool crashou no meio de um commit (e crashou — o servidor morreu várias vezes), o log pode ficar inconsistente. Em readonly, ZFS pula o replay. Em RW, ele tenta aplicar o log → metadata quebrado → panic.

Tentei todas as flags de recuperação:

echo 1 | sudo tee /sys/module/zfs/parameters/zfs_recover
echo 1 | sudo tee /sys/module/zfs/parameters/zil_replay_disable
echo 0 | sudo tee /sys/module/zfs/parameters/spa_load_verify_metadata
echo 0 | sudo tee /sys/module/zfs/parameters/spa_load_verify_data
sudo zpool import -o cachefile=none -N -FX POOL_APPS   # rewind extremo

Mesmo assim: panic. A corrupção é fatal em qualquer write path.

Decisão: destruir o pool e recriar do zero. Mas antes, salvar os ~630 GB de dados que estavam lá (configs de apps, docker layers, projetos).


Descoberta 4: o driver NVIDIA sabota o backup

Montei o pool de apps em readonly e disparei rsync via systemd-run pra não depender do SSH:

sudo systemd-run --unit=backup-job /root/backup.sh

Primeira tentativa: servidor caiu perto do fim do backup. Sem trace. Silent kill.

Segunda tentativa: caiu em ponto parecido. Sem trace.

Terceira tentativa: mesma coisa.

Nada disso tinha a ver com import RW. O pool de origem estava readonly, as leituras não disparavam o bug do log_spacemap. O que estava matando o kernel?

Suspeito de sempre: o driver NVIDIA. Sob pressão de IO (rsync de 1 milhão de arquivos), algo no gerenciamento de memória do driver 590 quebrava.

Teste: rmmod do módulo antes do backup:

sudo rmmod nvidia_uvm nvidia_drm nvidia_modeset nvidia
sudo systemctl reset-failed backup-job
sudo systemd-run --unit=backup-job /root/backup.sh

Backup completou sem nenhum crash. 🎉

Confirmação final: eram DOIS bugs independentes:

  1. Driver NVIDIA instável (provoca crashes aleatórios sob carga)
  2. Pool com metadata corrompido (panic específico em RW import)

O backup (em números)

Arquivos copiados:  ~2.9 milhões
Tamanho total:      ~730 GB
Throughput médio:   ~400 MB/s no destino raidz2
Arquivos pulados:   5 (I/O errors reais em setores ruins)

Os 5 arquivos corrompidos eram mídia em setores com bit rot pontual — nada crítico, totalmente re-baixáveis. Aceitei a perda.


A migração pra versão estável

Antes de destruir o pool, resolvi mudar pra uma versão estável do TrueNAS pra não ficar refém do kernel nightly:

  • Versão nightly atual: kernel 6.18.1 + NVIDIA 590.44 open (bugados)
  • Versão Release Candidate: kernel 6.12.33 LTS + NVIDIA 570.172.08 open (release, suporta Blackwell)

Já tinha a RC instalada como boot environment alternativo. Primeira tentativa: setar bootfs e reiniciar.

Boot loop.

Óbvio em retrospecto: o BE tinha o próprio /etc, sem o ix-zfs masked. Então ele tentava importar os pools e panicava igualzinho (a corrupção do pool é no disco, não no kernel).

Fix: montar o /etc do BE alvo e aplicar as proteções lá antes de bootar:

sudo zfs set mountpoint=legacy boot-pool/ROOT/<BE>/etc
sudo mount -t zfs boot-pool/ROOT/<BE>/etc /mnt/be-tmp/etc

sudo ln -sf /dev/null /mnt/be-tmp/etc/systemd/system/ix-zfs.service
sudo tee /mnt/be-tmp/etc/modprobe.d/blacklist-nvidia.conf <<EOF
blacklist nvidia
blacklist nvidia_drm
blacklist nvidia_modeset
blacklist nvidia_uvm
EOF

sudo umount /mnt/be-tmp/etc
sudo zfs set mountpoint=/etc boot-pool/ROOT/<BE>/etc

sudo zpool set bootfs=boot-pool/ROOT/<BE> boot-pool
sudo update-grub
sudo systemctl reboot

Boot limpo. Kernel 6.12 LTS rodando.


Destroy e recreate

Com o novo kernel, repeti o teste em RW. Panic de novo. Confirmado que o problema era o pool, não o kernel.

Hora da cirurgia:

# Limpar labels ZFS dos discos
for uuid in <uuid_1> <uuid_2> <uuid_3> <uuid_4>; do
  sudo zpool labelclear -f /dev/disk/by-partuuid/$uuid
done

# Recriar pool do zero, mesmo layout (raidz2 com 4 discos)
sudo zpool create -f \
  -o ashift=12 -o cachefile=none \
  -O compression=lz4 -O atime=off \
  -R /mnt/new-pool POOL_APPS raidz2 \
  <uuid_1> <uuid_2> <uuid_3> <uuid_4>

# Recriar datasets do TrueNAS Apps com as props que o middleware espera
for ds in ix-apps ix-apps/app_configs ix-apps/app_mounts ix-apps/docker ix-apps/truenas_catalog; do
  sudo zfs create -o canmount=noauto -o mountpoint=/.ix-apps/$(basename $ds) POOL_APPS/$ds
done

Pool novo, zero labels corrompidos, zero log_spacemap suspeito.


Restore

sudo systemd-run --unit=restore-job /root/restore.sh

Onde o script faz rsync do backup de volta pro novo pool. ~20 horas. Sério.

Por que tão lento? O backup foi rápido (~30 min) porque estava escrevendo sequencial num raidz2 de HDDs. O restore é ler milhões de arquivos pequenos desses HDDs (discos mecânicos sofrem em seek pra random read). Mesmo com o destino sendo SSD rápido, o bottleneck virou a fonte:

Regra: o lado mais lento manda. E HDD lendo milhão de arquivinho domina.

Durante o restore, sistema estável como nunca (LTS kernel + NVIDIA descarregado).


O retorno

Depois do restore, importei os pools pela UI do TrueNAS (Storage → Import Pool). O middleware registra os pools no DB dele e eles voltam a aparecer normal.

Surpresa: “Failed to start docker — Missing ix-apps/ dataset(s)”*.

Os datasets existiam e tinham dados. Mas estavam com canmount=noauto e não estavam montados. O middleware não sabia deles.

for ds in ix-apps ix-apps/app_configs ix-apps/app_mounts ix-apps/docker ix-apps/truenas_catalog; do
  sudo zfs mount POOL_APPS/$ds
done

sudo midclt call docker.state.start_service

Docker RUNNING. Apps começaram a deployar.

Last step: remover o blacklist da NVIDIA pra testar se o driver 570 no kernel 6.12 LTS é estável:

sudo rm /etc/modprobe.d/blacklist-nvidia.conf
sudo systemctl reboot

Boot limpo. NVIDIA carrega. nvidia-smi detecta as duas GPUs, idle, temperaturas normais. Todos os pools auto-importados via ix-zfs.service (desmascarado). Apps do TrueNAS + stacks compose subindo.

Load 20+ nos primeiros minutos (30 containers iniciando simultaneamente mordem o pool), mas estabilizou em load 2 depois. Dashboard da TrueNAS lenta durante o startup por causa de timeouts do middleware na API do Docker saturada — normal, passa.


Estado final

ItemAntesDepois
TrueNASMASTER nightly25.10-RC (release candidate)
Kernel6.18.1 (nightly)6.12.33 (LTS)
NVIDIA590.44 open (nightly)570.172.08 open (release)
Pool de appscorrompido, panic em RWfresh, raidz2 limpo
Appscrash looptodos RUNNING
Uptime estável~14 minem dias 🎉

Lições aprendidas

1. Nightly em produção é roleta russa

O kernel 6.18.1 + driver 590 open era bleeding edge demais pra rodar como produção. A combinação de hardware novo (GPU lançada em 2025) + driver nightly + kernel nightly acumulou bugs o bastante pra crashar sob carga. LTS + driver release resolveu sem precisar tocar em nada.

2. log_spacemap pode ficar irrecuperável

Se o pool crashou no meio de um TXG com feature@log_spacemap=active ativo e a corrupção for em estrutura persistida, RW mount panica o kernel. zfs_recover, zil_replay_disable, spa_load_verify_*=0, -FX — nada resolve. Readonly salva os dados, mas o pool precisa ser destruído e recriado.

3. RAID não protege contra bug de software

Meu pool era raidz2 (aguenta perder 2 discos). Isso não ajudou em nada contra um bug de metadata. Backup em outro pool foi o que salvou. Ter espaço livre em outro pool pra guardar o pool inteiro foi puro acaso que virou o ponto crítico.

4. TrueNAS middleware reescreve /etc/default/grub.d/*

Qualquer edição manual nesses arquivos é sobrescrita eventualmente. Pra parâmetro de kernel persistente confiável:

  • systemctl mask via symlink em /etc/systemd/system/* → persiste (é dataset per-BE)
  • Via UI do TrueNAS → persiste
  • /etc/default/grub.d/*não persiste

5. Docker + TrueNAS Apps no boot = tempestade de IO

30+ containers subindo simultaneamente geram load 20+, I/O wait absurdo, middleware API do docker com timeout. Dashboard lenta por 5-10 min. Isso não é crash, é só a realidade. Paciência.

6. Paciência é um recurso finito — conserve

Cada tentativa de import RW “pra testar” custou um reboot manual. No começo eu tentava várias combinações. No final, confirmei o diagnóstico uma vez e fui executar o plano.

7. midclt é seu amigo

O CLI do middleware do TrueNAS (midclt call ...) resolve muita coisa que a UI não deixa. midclt call docker.config, app.query, app.start, docker.state.start_service foram salva-vidas durante a recuperação.


Fechamento

No fim, 24 horas trabalhadas pra aprender: (a) como o ZFS lida com log_spacemap corrompido, (b) como o TrueNAS gerencia Boot Environments e middleware state, (c) que nightly builds não são pra rodar dezenas de containers de produção, (d) que algumas perdas fazem parte do custo da lição.

Se você tá começando homelab, fica a recomendação: release > RC > nightly, nessa ordem. Pra TrueNAS SCALE, use a versão release da linha atual. Nightly é pra quem quer testar, reportar bugs, e topa perder o fim de semana.

E tenha espaço livre em outro pool. Sempre.

Se quiser trocar figurinha sobre TrueNAS, ZFS ou homelab em geral, chama. 🦥