Author: fabio

  • Qwen3 235B su Cerebras per Q&A su documentazione tecnica e generazione playbook Ansible

    Qwen3 235B su Cerebras per Q&A su documentazione tecnica e generazione playbook Ansible

    Qwen3 235B servito sugli acceleratori Cerebras è il pezzo che ho aggiunto al toolkit per leggere documentazione lunga senza perdere il filo. Il free tier dà un milione di token al giorno, sufficiente per caricare due o tre RFC complete dentro lo stesso contesto e farci sopra domande senza ricaricare. La cosa che mi ha convinto a tenerlo fisso è la velocità: una risposta su un contesto da 80k token la ottengo in pochi secondi, non in minuti.

    Il modello è open weights, Apache 2.0, sviluppato da Alibaba. Cerebras serve esclusivamente modelli open-source sulla sua piattaforma, quindi non ho la zona grigia dei modelli proprietari che ogni tanto compaiono sui gateway. Lo uso per ragionare su standard di rete, produrre prime bozze di playbook Ansible e estrarre tabelle pulite da PDF già convertiti in markdown.

    Configurazione di opencode

    La chiave la creo su cloud.cerebras.ai, la salvo in ~/.config/claude-credentials/credentials.env con chmod 600 e la carico in shell come CEREBRAS_API_KEY. Poi nel file ~/.config/opencode/opencode.json aggiungo il provider:

    
    {
      "provider": {
        "cerebras": {
          "npm": "@ai-sdk/openai-compatible",
          "options": {
            "apiKey": "{env:CEREBRAS_API_KEY}",
            "baseURL": "https://api.cerebras.ai/v1"
          },
          "models": {
            "qwen-3-235b-a22b-instruct-2507": { "name": "Qwen3 235B Instruct" }
          }
        }
      }
    }
    

    Da terminale lancio la TUI puntandola alla cartella che mi serve:

    
    opencode . --model cerebras/qwen-3-235b-a22b-instruct-2507
    

    La working directory è di solito ~/Documenti/progetti//, dove tengo già le RFC in markdown e i ruoli Ansible esistenti da cui voglio derivare il nuovo playbook.

    Un esempio di sessione reale

    Martedì alle 16:40 dovevo riscrivere un ruolo Ansible per installare e configurare uno stack LAMP su un host Debian 13 di laboratorio. Avevo un ruolo webserver vecchio di due anni, mezzo rotto su systemd recente per via di apache2-utils che era stato spostato di pacchetto. Ho aperto opencode dentro progetti/lamp-debian13/, dove avevo copiato il ruolo vecchio, il site.yml e un README con i requisiti. Il prompt:

    rivedi il ruolo webserver per Debian 13. Mantieni la struttura tasks/handlers/templates ma sostituisci i moduli deprecati, allinea agli idempotency check correnti di Ansible 2.18 e produci un changelog dei diff in coda.

    La risposta è arrivata in undici secondi. Ha riscritto tasks/main.yml sostituendo apt: con ansible.builtin.apt esplicito, ha aggiunto un meta/argument_specs.yml che prima non c’era, e ha messo il changelog ordinato per file alla fine. Su due handler ha proposto di passare da notify esplicito a listen, scelta che ho accettato dopo averla verificata sulla doc. Il playbook risultante ha passato ansible-lint al primo colpo.

    Cosa fa bene

    Ragionamento lungo su molti file aperti nello stesso contesto. Estrazione di tabelle e elenchi puliti da documentazione tecnica densa. Generazione di Ansible idiomatico quando gli si fornisce un esempio recente come ancora. La velocità di inferenza Cerebras rende l’esperienza interattiva anche su prompt grandi: non aspetto mai più di una manciata di secondi.

    Cosa fa meno bene

    Su domande aperte di tipo architetturale tende a essere prolisso e a riepilogare invece di prendere posizione. Sui task piccoli e veloci è sovradimensionato: ci sono modelli più snelli che rispondono in mezzo secondo. Il limite di 1M token al giorno è abbondante per uso individuale ma si sente se carico più volte di seguito un repository intero.

    Privacy e termini del provider

    Cerebras dichiara, nei Terms del 27 agosto 2024, che non usa input né output per addestramento. I log vengono cancellati “when no longer necessary”, senza finestra esplicita ma di fatto trattati come telemetria operativa. La residency è USA, non c’è opzione EU. Il limite del free tier è 1M token al giorno e 30 richieste al minuto, senza richiesta di carta di credito.

    Il modello servito è Qwen 3 235B, licenza Apache 2.0, rilasciato da Alibaba: pesi pubblici, ridistribuibili, ispezionabili. Cerebras si limita all’hosting su acceleratori proprietari, non al training. La combinazione policy Cerebras + modello open è quella che mi dà la frizione minore sul piano legale per task tecnici, anche se la giurisdizione resta extra UE.

    Cosa non gli mando

    Niente nomi di clienti, niente IP interni, niente file di configurazione con secret, niente nomi reali di host del mio homelab. Prima di mandare un blocco di log faccio passare un sed che redatta indirizzi privati e hostname in placeholder generici. Se il contenuto è davvero sensibile, esco da Cerebras e uso qwen2.5-coder:14b in locale su Ollama: stessa famiglia di modelli, niente uscita di rete.

    In pratica

    Nel mio toolkit Qwen3 235B su Cerebras è il “lettore lungo” per documentazione e ruoli Ansible. Per task brevi e veloci scelgo Llama 3.1 8B Instant su Groq, che risponde in mezzo secondo. Per ragionamento architetturale puro con contesto enorme passo a Gemini 2.5 Pro, che gestisce finestre ancora più grandi. Per coding mirato su singoli file Qwen3 32B su Groq è più rapido e altrettanto preciso. Tenere Cerebras pronto significa avere un modello frontier open ad altissima velocità senza pagare nulla.


    Immagine generata con Cloudflare Workers AI / FLUX.

  • Qwen3 32B su Groq per ragionamento step-by-step e analisi cause

    Qwen3 32B su Groq per ragionamento step-by-step e analisi cause

    Qwen3 32B servito su Groq è il modello che apro quando Llama 3.1 8B è troppo poco e Llama 3.3 70B è sovradimensionato. Sta nel mezzo, ha un ragionamento step-by-step migliore del 70B sui task di coding piccoli, e sulle LPU di Groq la latenza resta sotto i due secondi anche su risposte da 1500 token.

    Lo tengo come “secondo cervello” del terminale: quando devo trasformare uno schema, riscrivere una funzione, leggere un Dockerfile e suggerire un’ottimizzazione, è il modello che dà la qualità più alta a parità di tempo di attesa. Qwen 3 è open weights, Apache 2.0, sviluppato da Alibaba. Pesi pubblici, ridistribuibili, ispezionabili.

    Configurazione di opencode

    La chiave Groq è la stessa che uso per gli altri modelli del provider, presa da console.groq.com/keys e salvata in ~/.config/claude-credentials/credentials.env come GROQ_API_KEY. Nel file ~/.config/opencode/opencode.json aggiungo il modello sotto il provider Groq:

    
    {
      "provider": {
        "groq": {
          "npm": "@ai-sdk/openai-compatible",
          "options": {
            "apiKey": "{env:GROQ_API_KEY}",
            "baseURL": "https://api.groq.com/openai/v1"
          },
          "models": {
            "qwen/qwen3-32b": { "name": "Qwen3 32B" }
          }
        }
      }
    }
    

    Lo lancio dalla shell dentro la cartella del progetto:

    
    opencode . --model groq/qwen/qwen3-32b
    

    La working directory che passo è di solito ~/Documenti/progetti//, con dentro il sorgente da rivedere.

    Un esempio di sessione reale

    Giovedì alle 10:50 stavo riscrivendo un piccolo script Python che parsava un export CSV di Strava in record JSON per caricarli su un database. Lo script originale funzionava ma era 180 righe di codice non idiomatico, con tre cicli annidati e una logica di deduplicazione fragile. Ho aperto opencode dentro la cartella del progetto e ho dato il prompt:

    leggi parser.py. Riscrivilo idiomatico Python 3.13, usa pathlib, dataclasses e itertools dove sensato. Mantieni esattamente la stessa interfaccia CLI e produci la lista dei diff principali in un commento in cima.

    Risposta in circa due secondi e mezzo. Ha consolidato i tre cicli in uno solo usando groupby, ha sostituito la deduplicazione manuale con un set su tuple ordinate, e ha aggiunto un dataclass Activity che ha reso il resto leggibile. Le 180 righe sono diventate 95, con cinque commenti utili e zero modifiche all’interfaccia CLI. La regressione l’ho verificata con un diff sui JSON di output: identici riga per riga.

    Cosa fa bene

    Riscritture di funzioni medie con stile idiomatico nel linguaggio target. Analisi di una catena di errori con causa probabile motivata. Trasformazioni di schema (CSV verso JSON, YAML verso TOML, eccetera) con regole esplicite. Spiegazioni passo passo di pezzi di codice altrui. Generazione di test parametrizzati a partire da una funzione esistente. Mantiene il filo su prompt da 5-10k token senza divagare.

    Cosa fa meno bene

    Output narrativo molto lungo: tende a essere meno scorrevole del 70B. Output puramente creativo: non è la sua area. Su prompt molto vaghi propone soluzioni generiche, vuole specifiche e vincoli per dare il meglio.

    Privacy e termini del provider

    Groq, nella Services Agreement Sezione 4.2 aggiornata al 15 ottobre 2025, dichiara esplicitamente che input e output non vengono usati per training né fine-tuning. La retention dei log è al massimo 30 giorni per troubleshooting e abuse monitoring. L’infrastruttura è GCP US, quindi residency statunitense. Il toggle ZDR esiste sulla console sotto Data Controls, eligibility per il free tier non chiaramente documentata.

    Il modello Qwen 3 32B è open weights, Apache 2.0, rilasciato da Alibaba. Posso scaricarlo e riprodurlo in locale (con hardware adeguato) o sostituirlo con la variante più piccola della stessa famiglia se serve. La combinazione policy Groq più modello Apache 2.0 è una delle più pulite per task tecnici di routine.

    Cosa non gli mando

    Niente nomi reali di clienti, niente IP interni, niente hostname del mio homelab, niente stralci con secret. Su uno script che contiene path personali o nomi di file di configurazione, sostituisco con placeholder prima di mandarlo. Per codice davvero sensibile fallback su qwen2.5-coder:14b su Ollama in locale: la latenza sale a qualche secondo sul mio hardware ma il codice non lascia la macchina.

    In pratica

    Nel mio mix Qwen3 32B su Groq è il default per coding di precisione su singoli file. Per task triviali e veloci scendo a Llama 3.1 8B Instant. Per output narrativi lunghi salgo a Llama 3.3 70B Versatile. Per ragionamento su codebase intere con tanti file dentro lo stesso contesto passo a Qwen3 235B su Cerebras o a Gemini 2.5 Pro. Il 32B è il punto dolce della curva qualità/latenza nel mio uso quotidiano.


    Immagine generata con Cloudflare Workers AI / FLUX.

  • Llama 3.1 8B Instant su Groq per shell one-liner e parsing rapido

    Llama 3.1 8B Instant su Groq per shell one-liner e parsing rapido

    Llama 3.1 8B Instant su Groq è il modello che chiamo decine di volte al giorno per i task piccoli. Non è il più intelligente del toolkit, ma è quello che mi risponde in mezzo secondo. Per le classificazioni rapide, le regex abbozzate, i riassunti di un blocco di journalctl prima di mandarlo in un alert, mi serve esattamente quello.

    Quando devo decidere se una riga di log è un warning innocuo o un errore vero, non voglio aspettare due secondi. La latenza delle LPU Groq su un modello da 8B mantiene fluido il workflow. Llama 3.1 8B è open weights, distribuito da Meta sotto Llama 3 Community License: pesi pubblici, ridistribuibili, usabili senza vincoli pratici per l’homelab.

    Configurazione di opencode

    La chiave la creo gratis su console.groq.com/keys e la salvo in ~/.config/claude-credentials/credentials.env come GROQ_API_KEY. La shell la carica all’avvio. Nel file ~/.config/opencode/opencode.json aggiungo il modello dentro il provider OpenAI-compatible Groq, lo stesso che uso per il 70B; basta registrare il nome modello aggiuntivo:

    
    {
      "provider": {
        "groq": {
          "npm": "@ai-sdk/openai-compatible",
          "options": {
            "apiKey": "{env:GROQ_API_KEY}",
            "baseURL": "https://api.groq.com/openai/v1"
          },
          "models": {
            "llama-3.1-8b-instant": { "name": "Llama 3.1 8B Instant" }
          }
        }
      }
    }
    

    Per usarlo via opencode, comando diretto in shell:

    
    opencode . --model groq/llama-3.1-8b-instant
    

    Per i task batch da terminale, tipo parsing di un file di log, preferisco un curl secco contro l’endpoint senza aprire la TUI. La risposta arriva in tempi che mi permettono di concatenare le chiamate con xargs quando serve.

    Un esempio di sessione reale

    Martedì pomeriggio alle 15:20 stavo leggendo un dump di 4000 righe di Nginx access log da un container di staging. Cercavo richieste con codice 5xx ma con pattern URL anomalo rispetto al traffico normale. La regex per i 5xx la sapevo, ma volevo un riassunto in linguaggio naturale dei pattern emergenti per capire se era un attacco o solo un bug nostro. Ho fatto:

    
    grep ' 5[0-9][0-9] ' /tmp/access.log | head -200 > /tmp/5xx.txt
    

    Poi dentro la TUI di opencode puntata al modello, prompt:

    leggi /tmp/5xx.txt. Raggruppa le richieste per pattern di path (ignora query string), elenca top 5 pattern con conteggio. Per ogni pattern indica se sembra ricerca legittima, scan automatizzato, o errore applicativo, con motivazione di una riga.

    Risposta in circa 800 millisecondi. Cinque cluster identificati correttamente: tre erano endpoint di un’applicazione interna con un bug noto, due erano scansioni di wp-login.php su un host che non aveva nemmeno WordPress. Ho chiuso il ticket in pochi minuti.

    Cosa fa bene

    Risposte in meno di un secondo su prompt corti. Classificazioni binarie o a poche categorie. Riassunto di righe di log in linguaggio naturale. Generazione di regex semplici, parsing di output journalctl. Conversione di output testuale strutturato in JSON quando lo schema è chiaro. Tutto quello che farei con uno script awk se sapessi awk meglio.

    Cosa fa meno bene

    Ragionamento step-by-step lungo: lo perde dopo poche battute. Codice di una certa complessità: tende a improvvisare API che non esistono. Documentazione tecnica articolata: serve un modello più grande. Per qualunque task in cui la qualità conta più della latenza, salgo al 70B o al Qwen3 32B sullo stesso provider.

    Privacy e termini del provider

    Groq dichiara, nella Services Agreement Sezione 4.2 aggiornata al 15 ottobre 2025, che gli input e gli output non vengono usati per training né fine-tuning. La retention dei log è al massimo 30 giorni per troubleshooting e abuse monitoring. L’infrastruttura è GCP US, residency statunitense. Il toggle ZDR è in console sotto Data Controls, ma l’eligibility per il free tier non è documentata chiaramente.

    I modelli sul catalogo Groq sono misti tra proprietari (Compound) e open. Llama 3.1 8B Instant è open weights, Llama 3 Community License, pesi su Hugging Face. Posso ispezionare e riprodurre il modello fuori dal provider se ne ho bisogno.

    Cosa non gli mando

    Niente IP interni, niente hostname reali del mio homelab, niente nomi di clienti, niente stralci di file di config con chiavi. Per i log applicativi passo un filtro di redazione prima dell’invio. Per contenuti sensibili evito Groq e uso qwen2.5-coder:14b su Ollama in locale: il tempo di risposta sale a 4-5 secondi sul mio hardware, ma il dato non esce di casa.

    In pratica

    Nel mio toolkit Llama 3.1 8B Instant è il “primo livello” del filtro: triage di log, classificazione veloce, parsing leggero. Quando serve più qualità salgo a Qwen3 32B su Groq, che mantiene una latenza accettabile. Per output lunghi e ben strutturati vado su Llama 3.3 70B Versatile. Per ragionamento su documentazione enorme passo su Cerebras con Qwen3 235B. Il 8B Instant è la mano destra che gira sempre in background mentre lavoro.


    Immagine generata con Cloudflare Workers AI / FLUX.

  • Llama 3.3 70B su Groq per generare script bash e documentare changelog di release

    Llama 3.3 70B su Groq per generare script bash e documentare changelog di release

    Llama 3.3 70B servito sulle LPU di Groq è il modello che apro quando ho bisogno di una risposta lunga in pochi secondi. Le LPU spingono il 70B oltre i 250-300 token al secondo nelle finestre buone, e per me significa poter pensare a voce alta nel terminale senza aspettare. Lo tengo come “scrittore lungo” del toolkit: stesura di script bash con commenti seri, changelog di release a partire dal git log, sezioni di README ricavate dai sorgenti.

    Il modello è Llama 3.3 70B Instruct, distribuito da Meta sotto Llama 3 Community License. Non è strettamente Apache 2.0, ma i pesi sono pubblici e la licenza permette uso commerciale fino a soglie molto alte, irrilevanti per uso individuale di homelab. Su contesti da 30-40k token è la combinazione più equilibrata che ho tra qualità del ragionamento e latenza.

    Configurazione di opencode

    La chiave la creo su console.groq.com/keys, gratuita e senza carta, la salvo in ~/.config/claude-credentials/credentials.env come GROQ_API_KEY e la lascio caricare dalla shell. Nel file ~/.config/opencode/opencode.json registro il provider come OpenAI-compatible:

    
    {
      "provider": {
        "groq": {
          "npm": "@ai-sdk/openai-compatible",
          "options": {
            "apiKey": "{env:GROQ_API_KEY}",
            "baseURL": "https://api.groq.com/openai/v1"
          },
          "models": {
            "llama-3.3-70b-versatile": { "name": "Llama 3.3 70B Versatile" }
          }
        }
      }
    }
    

    Per aprirlo dentro la TUI con il working directory già montato:

    
    opencode . --model groq/llama-3.3-70b-versatile
    

    La cartella che passo come argomento è di solito ~/Documenti/progetti//, con dentro il sorgente o il CHANGELOG.md esistente.

    Un esempio di sessione reale

    Lunedì sera alle 21:15 dovevo chiudere la release di un piccolo tool di archiviazione foto. Avevo 38 commit dalla tag precedente, messaggi misti italiano-inglese, alcuni “fix vari” poco descrittivi. Ho aperto opencode nella repo e ho preparato il contesto con:

    
    git log --oneline v0.4.0..HEAD > /tmp/log.txt
    git diff v0.4.0..HEAD --stat > /tmp/stat.txt
    

    Poi dentro la TUI il prompt:

    leggi /tmp/log.txt e /tmp/stat.txt. Produci CHANGELOG.md in formato Keep a Changelog (Added, Changed, Fixed). Inglese, niente parole filler, raggruppa i commit che toccano lo stesso file. Aggiungi una nota di migrazione se vedi rinomine di chiave nel file di configurazione.

    La risposta è arrivata in circa quattro secondi. Aveva raggruppato i 38 commit in undici voci pulite, distinte per sezione, con una nota di migrazione corretta su una rinomina di chiave YAML che io stesso avevo dimenticato. Ho dovuto correggere una sola voce in cui aveva interpretato un commit di test come feature.

    Cosa fa bene

    Output lunghi e strutturati prodotti in pochi secondi: script bash di 200 righe con commenti decenti, changelog ben raggruppati, README narrativi a partire da --help e file main. Tiene bene il filo su contesti da 30-40k token. La latenza Groq trasforma una stesura da “vado a prendere un caffè” a “mentre prendo un sorso d’acqua è già qui”.

    Cosa fa meno bene

    Su ragionamento step-by-step di tipo logico-matematico Qwen3 32B sullo stesso provider lo batte. Sull’analisi di trade-off architetturali tende a essere riassuntivo invece che critico. Non è il modello giusto quando voglio una posizione netta su un dilemma di design.

    Privacy e termini del provider

    Groq, nella Services Agreement Sezione 4.2 aggiornata al 15 ottobre 2025, dichiara esplicitamente che “Groq is not permitted to use Inputs or Outputs for training or fine-tuning”. I log vengono conservati al massimo 30 giorni per troubleshooting e abuse monitoring. L’infrastruttura è GCP US, quindi residency statunitense. Sulla console esiste un toggle ZDR (Zero Data Retention) sotto Data Controls, ma l’eligibility per il free tier non è chiara dalla documentazione pubblica, va verificata caso per caso.

    I modelli serviti sono misti: Llama 3.x e Qwen 3 sono open weights, Whisper open source, mentre Groq Compound è proprietario. Llama 3.3 70B che uso qui è open weights sotto Llama 3 Community License, pesi disponibili su Hugging Face.

    Cosa non gli mando

    Mai prompt che contengano nomi reali di clienti, credenziali, IP interni o stralci di file di configurazione con secret. Prima di inviare un log lo passo in un filtro che redatta IP privati e hostname in placeholder. Per contenuti che restano sotto il mio controllo, fallback a qwen2.5-coder:14b su Ollama in locale: stessa famiglia, niente rete.

    In pratica

    Nel mio mix Llama 3.3 70B su Groq è il “produttore di prosa tecnica”. Per task brevi e veloci scelgo Llama 3.1 8B Instant, sempre su Groq, perché risponde in mezzo secondo. Per ragionamento step-by-step su codice piccolo passo a Qwen3 32B. Per documentazione molto lunga con tanti file dentro lo stesso contesto vado su Qwen3 235B su Cerebras. Il 70B sta nel mezzo come default solido quando voglio output lungo ma non aspettare.


    Immagine generata con Cloudflare Workers AI / FLUX.

  • Gemma3 4B sul Mac Mini M1 come modello rapido di backup

    Gemma3 4B sul Mac Mini M1 come modello rapido di backup

    Gemma3 4B è il modello che ho scelto per il Mac Mini M1 acceso vicino allo schermo come piccolo nodo di servizio. Avevo bisogno di un modello che rispondesse istantaneamente a domande brevi senza che dovessi accendere altre macchine, e che potesse anche accettare immagini quando capita. Sul chip Apple Silicon, con backend Metal, una 4B in quantizzazione Q4 risponde alla prima parola in una frazione di secondo: la latenza percepita è bassissima, e questo cambia il modo in cui lo interpello durante il lavoro.

    Il modello è rilasciato da Google con la Gemma License, semi-permissiva: l’uso è libero entro una use policy che vieta categorie specifiche (armi, malware, contenuti pedopornografici, ecc.), va letta una volta perché differisce da una Apache 2.0 piena. Sul Mac Mini ho macOS aggiornato e Ollama installato con il pacchetto ufficiale. La 4B di Gemma 3 sta in circa 3 GB di RAM unificata e resta caldo abbastanza da non dover ricaricare il modello a ogni invocazione. Uso il terminale in fish, che è la mia shell su macOS, mentre sul server Debian dell’homelab uso bash.

    I parametri base: 4B di parametri, contesto nativo 8k che spingo a 8192 effettivi via Modelfile quando serve, multimodalità per immagini singole, quantizzazione Q4 predefinita di Ollama. Knowledge cutoff intorno a metà 2024.

    Setup base con Ollama

    Su macOS conviene partire dal pacchetto ufficiale di Ollama (o da brew, come preferisco io), poi pull del modello e prima esecuzione interattiva.

    
    brew install ollama
    brew services start ollama
    ollama pull gemma3:4b
    ollama run gemma3:4b
    

    Per la modalità multimodale, dentro la chat passo direttamente un percorso di immagine usando il tag che Ollama riconosce per gli allegati, e il modello la legge come parte del prompt. Per task con prompt più lunghi creo un Modelfile derivato.

    
    ollama show gemma3:4b --modelfile > /tmp/gemma3.Modelfile
    

    Aggiungo PARAMETER num_ctx 8192 al file e registro la variante con ollama create gemma3-long -f /tmp/gemma3.Modelfile. La uso solo per i task che richiedono una finestra più ampia, in modo da non sprecare RAM unificata sul Mac Mini per tutti gli altri usi quotidiani.

    Setup avanzato con opencode

    Quando devo dare al modello accesso a una cartella di file e non solo a una domanda singola, passo a opencode. La TUI agentica monta la working directory, mi permette di passare file in contesto in modo esplicito, e gestisce sessioni multi-step. Il provider punta a Ollama locale via endpoint OpenAI-compatible.

    
    {
      "provider": {
        "ollama-local": {
          "npm": "@ai-sdk/openai-compatible",
          "options": {
            "apiKey": "ollama",
            "baseURL": "http://localhost:11434/v1"
          },
          "models": {
            "gemma3:4b": { "name": "Gemma 3 4B" }
          }
        }
      }
    }
    

    Mi sposto nella cartella di lavoro e apro la TUI.

    
    opencode . --model ollama-local/gemma3:4b
    

    Un esempio di sessione reale

    Lunedì sera intorno alle 22 stavo deserializzando un dump JSON di configurazione che mi era arrivato da un collega: un blob da 300 KB di flag e valori per un sistema di feature toggle, con chiavi annidate fino a quattro livelli, e mi serviva tirarne fuori un sottoinsieme specifico (le chiavi che iniziano con experiment_ e che hanno valore booleano) per metterle in una tabella leggibile da incollare in un ticket. Non avevo voglia di scrivere lo script jq a mano in quel momento. Ho aperto opencode sulla cartella del dump, ho dato in contesto il file JSON e ho chiesto a Gemma3 4B di proporre un comando jq che facesse il filtro.

    La prima versione che ha proposto era quasi corretta ma usava select(type == "boolean") senza prima fare paths(...) per gestire il nesting, e su un blob annidato come quello non funzionava. Gli ho detto di rifare il filtro tenendo conto della profondità arbitraria, ed è uscito con paths(scalars) as $p | select($p[-1] | tostring | startswith("experiment_")) combinato con un check sul tipo del valore. Ho lanciato il comando, ho ottenuto la lista pulita, l’ho formattata come tabella markdown e l’ho incollata nel ticket. Trenta minuti scarsi per una rogna che mi avrebbe rubato il doppio del tempo a farla in piedi a mano.

    Cosa fa bene

    La latenza percepita su Apple Silicon è il suo punto di forza assoluto: la prima parola arriva quasi sempre prima che il mio cervello finisca di pensare alla domanda. Per task brevi (spiegazioni di parametri di tool, mini-script, formattazione di dati, domande dirette su sintassi) è imbattibile come compromesso tra qualità e velocità. La multimodalità su immagini singole è utile per descrivere screenshot o leggere testo da una foto.

    Cosa fa meno bene

    Per task lunghi e complessi i suoi 4B di parametri si vedono: tende a perdere il filo, semplifica troppo, e su pattern di codice non banali sbaglia con grande aplomb. Sulla finestra di contesto, anche spinta a 8k, non regge file molto grandi: meglio passargli un estratto rilevante che il file intero. Per code review serie o reasoning su problemi sfumati va sostituito con modelli più grossi su altre macchine.

    Privacy: tutto resta sul mio host

    Gemma3 gira interamente sul Mac Mini: nessun provider terzo, nessun upload, nessuna telemetria di default. Ollama si lega su 127.0.0.1:11434 e non parla con l’esterno. I log restano in ~/.ollama/logs/, li cancello con un rm quando voglio, e non c’è alcuna policy esterna da rileggere. Rispetto ai servizi cloud, dove ogni prompt passa per server di terzi (con retention variabile e in qualche caso riuso per training salvo opt-out), qui le immagini, i dump JSON e i frammenti di codice che gli passo non escono mai dalla rete di casa. La Gemma License, pur semi-permissiva, mi lascia comunque libero per gli usi reali che faccio in homelab; vale la pena leggere la use policy una volta e archiviarla.

    In pratica

    Gemma3 4B sul Mac Mini è il modello istantaneo per task brevi che non vogliono attesa. Quando serve code review seria sul server passo a Qwen2.5-Coder 14B, per reasoning step-by-step apro DeepSeek-R1 7b, e per il motore quotidiano sul laptop tengo qwen3:8b. La differenza la fa avere il modello giusto sulla macchina giusta in base a quanto contesto serve e a quanto tempo voglio aspettare.


    Immagine generata con ComfyUI Mac M1 / RealVisXL V5 Lightning.

  • Modello generalista qwen3:8b come motore quotidiano su CachyOS

    Modello generalista qwen3:8b come motore quotidiano su CachyOS

    qwen3:8b sta caricato in memoria sul mio laptop personale per quasi tutta la giornata, e si vede. Lo interpello quando voglio una risposta rapida su qualcosa che non vale la pena mandare a un servizio esterno: una bozza di script bash da rifinire, la spiegazione di un parametro oscuro di un tool, un riassunto di un log lungo che ho copincollato dentro la chat. La 8B in quantizzazione Q4_K_M sta dentro i 6 GB di VRAM, gira in modo confortevole anche senza una scheda di fascia alta, e su CachyOS sfrutto un kernel pacchettizzato in modo aggressivo per latenza bassa che si fa sentire nelle risposte.

    Il modello è rilasciato da Alibaba con licenza Apache 2.0, quindi nessun vincolo d’uso anche per progetti professionali. Il laptop è una macchina Intel di qualche anno fa, distribuzione rolling Arch-based, swappiness ridotto a 10, ZRAM attivo. Ollama gira come servizio, parte da solo all’avvio e resta in ascolto su 127.0.0.1. Tengo il modello caricato in VRAM per le prime sessioni della giornata, e quando il laptop entra in idle Ollama lo scarica da solo, liberando memoria per il resto.

    I parametri di partenza: 8B di parametri, contesto nativo 32k che spingo a 12k effettivi via Modelfile per i task di sintesi, quantizzazione Q4_K_M predefinita. Knowledge cutoff metà 2024, abbastanza recente per quasi tutto quello che mi capita.

    Setup base con Ollama

    Su CachyOS Ollama è già nei repository, si installa con pacman in modo pulito senza dover ricorrere allo script ufficiale.

    
    sudo pacman -S ollama
    sudo systemctl enable --now ollama
    ollama pull qwen3:8b
    ollama run qwen3:8b
    

    Per come lo uso, il default funziona bene. Quando devo lavorare su file di testo lunghi alzo num_ctx con un Modelfile derivato partendo dal modello base.

    
    ollama show qwen3:8b --modelfile > /tmp/qwen3.Modelfile
    

    Aggiungo PARAMETER num_ctx 12288 al file e creo la variante con ollama create qwen3-long -f /tmp/qwen3.Modelfile. Tengo la variante per i task di sintesi sui log, la base per tutto il resto della giornata, in modo da non mangiare VRAM senza ragione.

    Setup avanzato con opencode

    Quando il task supera la singola domanda e voglio dare al modello accesso a una cartella di file, passo a opencode. La TUI agentica monta la working directory, mi permette di passare file in contesto, e gestisce sessioni multi-step senza che debba reincollare ogni volta lo stesso pezzo di codice. La configurazione punta a Ollama in locale via endpoint OpenAI-compatible.

    
    {
      "provider": {
        "ollama-local": {
          "npm": "@ai-sdk/openai-compatible",
          "options": {
            "apiKey": "ollama",
            "baseURL": "http://localhost:11434/v1"
          },
          "models": {
            "qwen3:8b": { "name": "Qwen3 8B" }
          }
        }
      }
    }
    

    Mi sposto nella cartella di lavoro e apro la TUI.

    
    opencode . --model ollama-local/qwen3:8b
    

    Un esempio di sessione reale

    Ieri sera alle 22:30 stavo finendo uno script di backup per una cartella di documenti che cambia continuamente, e mi serviva aggiungere un piccolo blocco di codice che generasse un report di differenza tra l’ultimo backup completato e quello in corso, da spedire come riga di log strutturata. Non avevo voglia di scriverlo da zero, e nemmeno di pensarci troppo. Ho aperto opencode nella cartella dello script, ho dato in contesto il file principale e ho chiesto a qwen3:8b di proporre una funzione bash che producesse l’elenco dei file aggiunti, modificati e rimossi rispetto a un manifest precedente, in formato leggibile da una pipeline rsyslog.

    La prima versione che ha proposto usava comm su due liste ordinate generate da find, con una piccola routine per estrarre il mtime e confrontarlo con quello del manifest. Era pulita, ma faceva quattro letture del filesystem invece di due. Gli ho chiesto di farla in un solo passaggio salvando le tuple path:mtime:size in due array associativi e confrontando le chiavi. La seconda versione era esattamente quello che mi serviva, con il dettaglio in più che ha aggiunto da solo: un controllo che il manifest precedente esistesse, e un fallback al backup completo come prima esecuzione. Ho integrato lo script, l’ho testato sulla cartella reale, e in mezz’ora ero a letto.

    Cosa fa bene

    Sui task di tutti i giorni si comporta sorprendentemente bene per un modello da 8B. Le risposte su bash, Python di livello base, configurazione di servizi comuni, lettura di log sono solide. Riassume bene testi lunghi se gli si dà il contesto giusto. È rapido a partire e rapido a rispondere, e questo lo rende perfetto come motore quotidiano che non interrompe il flusso di lavoro.

    Cosa fa meno bene

    Su codice complesso o pattern di concorrenza inventa cose plausibili ma sbagliate, e va sempre verificato. Sul reasoning esplicito su problemi sfumati è inferiore a un modello di reasoning dedicato come DeepSeek-R1. Su prompt molto lunghi (oltre 10k token) la qualità delle risposte cala, e conviene segmentare il task. La sua conoscenza di librerie e tool molto recenti è incompleta, come per quasi tutti i modelli con cutoff a metà 2024.

    Privacy: tutto resta sul mio host

    qwen3:8b gira interamente sul mio laptop: nessun provider terzo, nessun upload, nessuna telemetria di default. Ollama si lega su 127.0.0.1:11434 e non espone nulla all’esterno. I log restano in ~/.ollama/logs/ e li elimino quando voglio con un rm, e non ci sono policy esterne da rileggere ogni volta che cambia qualcosa. Rispetto ai servizi cloud, dove ogni domanda passa per server di terzi con retention variabile (e in qualche caso riuso per training se non si fa opt-out esplicito), qui i prompt e i frammenti di log che gli incollo non lasciano mai la macchina. La licenza Apache 2.0 di Qwen 3 mi lascia libero di usarlo come voglio, anche su lavoro a pagamento.

    In pratica

    qwen3:8b è il mio motore quotidiano sul laptop, quello che apro decine di volte al giorno per task brevi che non richiedono né reasoning esplicito né code review approfondita. Per ragionamento step-by-step su problemi tosti passo a DeepSeek-R1 7b sul server, per code review serie sul codice del lavoro mi sposto su Qwen2.5-Coder 14B, per risposte istantanee sul Mac Mini tengo Gemma3 4B. Avere modelli con vocazioni diverse a portata di SSH cambia parecchio il modo in cui distribuisco il carico cognitivo durante la giornata.


    Immagine generata con Cloudflare Workers AI / FLUX.

  • Reasoning step-by-step con DeepSeek-R1 7b in locale via opencode

    Reasoning step-by-step con DeepSeek-R1 7b in locale via opencode

    DeepSeek-R1 7b lo apro quando devo capire qualcosa che non si risolve con una risposta sbrigativa. Non lo carico per generare codice né per riassunti veloci: lo carico per ragionare. Mi serve un interlocutore che, davanti a un log strano o a una scelta architetturale, espliciti i passaggi del proprio ragionamento dentro blocchi prima di buttare giù una conclusione. Quei blocchi sono spesso più utili della risposta finale, perché mi mostrano dove il modello sta facendo un’assunzione che potrei voler contestare prima ancora di leggere la sintesi.

    Il modello è rilasciato da DeepSeek AI con licenza MIT, quindi è libero da vincoli d’uso. Lo tengo su un server Debian 12 nella mia rete locale, lo stesso che ospita altri modelli Ollama. La 7B in quantizzazione Q4_K_M occupa circa 5 GB di VRAM e gira comoda anche su una scheda di fascia media. La latenza non è il punto forte di questa famiglia di modelli, perché generano molti token di pensiero prima della risposta vera, ma per la qualità di ragionamento che restituisce è un compromesso che accetto volentieri.

    I parametri base sono 7B, contesto nativo 32k che spingo a 16k effettivi via Modelfile per non esagerare con la VRAM, temperature di default a 0.6 (la lascio così, non ho mai trovato motivo di toccarla per come la uso).

    Setup base con Ollama

    Su Debian si installa con lo script ufficiale e si scarica il modello in un solo passaggio.

    
    curl -fsSL https://ollama.com/install.sh | sh
    sudo systemctl enable --now ollama
    ollama pull deepseek-r1:7b
    ollama run deepseek-r1:7b
    

    Una volta entrato in chat, il modello inizia ogni risposta con un blocco ... che contiene il ragionamento esplicito. Conviene lasciarlo visibile durante l’analisi e nasconderlo solo quando si vuole una risposta sintetica da incollare altrove. Per task lunghi alzo num_ctx: parto dal Modelfile esistente e ne creo una variante.

    
    ollama show deepseek-r1:7b --modelfile > /tmp/r1.Modelfile
    

    Dopo aver aggiunto PARAMETER num_ctx 16384 al file, costruisco la variante con ollama create deepseek-r1-long -f /tmp/r1.Modelfile e la tengo a parte per le sessioni di analisi più impegnative, in modo da non sprecare VRAM per tutto il resto della giornata.

    Setup avanzato con opencode

    opencode lo uso come TUI agentica: la finestra di chat è più comoda della shell di ollama run, mi permette di dare al modello accesso ai file di un progetto, e mantiene la cronologia. Lo collego al server Ollama locale tramite endpoint OpenAI-compatible, che Ollama espone di default su /v1.

    
    {
      "provider": {
        "ollama-local": {
          "npm": "@ai-sdk/openai-compatible",
          "options": {
            "apiKey": "ollama",
            "baseURL": "http://localhost:11434/v1"
          },
          "models": {
            "deepseek-r1:7b": { "name": "DeepSeek R1 7B" }
          }
        }
      }
    }
    

    Salvato il file, mi posiziono nella cartella del progetto e apro la sessione.

    
    cd ~/Documenti/progetti/analisi-log
    opencode . --model ollama-local/deepseek-r1:7b
    

    Dentro la TUI passo i file rilevanti come contesto e il modello li tiene presenti per tutta la sessione, senza che debba riallegare niente a ogni messaggio.

    Un esempio di sessione reale

    Ieri sera alle 23 stavo cercando di capire perché una migration Django in un progetto interno andava in errore solo sul database di staging, mai in locale, sempre con un messaggio criptico su una foreign key che si rifiutava di essere creata. Avevo davanti il dump dello schema, il file della migration generata dal makemigrations, e il traceback intero. Ho aperto opencode nella cartella del progetto, ho dato in contesto i tre file e ho chiesto a DeepSeek-R1 di ragionare ad alta voce su cosa potesse causare quel comportamento solo in staging.

    Il blocco ha messo in fila le ipotesi una per una: differenza di engine InnoDB tra le due istanze, charset diverso, una migration precedente non applicata davvero, una constraint dichiarata su una colonna che nel dump aveva tipo BIGINT mentre nella nuova migration era INT. Ha scartato le prime tre con argomenti che potevo verificare e si è concentrato sulla quarta, indicandomi esattamente quale tabella e quale colonna controllare. Ho aperto il dump, ho confermato il mismatch di tipo, ho corretto la migration con una operazione esplicita di alter sulla colonna, e in dieci minuti il problema era chiuso. Il valore del ragionamento esplicito sta proprio qui: mi fa risparmiare i giri morti dietro a ipotesi sbagliate.

    Cosa fa bene

    Quando il problema richiede di mettere in fila ipotesi e scartarle con un argomento, DeepSeek-R1 7b dà il meglio. Lo trovo utile per debug di errori non ovvi, per decisioni di architettura su scala piccola (scegliere tra due pattern di retry, capire dove mettere un lock), per analisi di log dove serve ricostruire una sequenza causale. Il fatto che il ragionamento sia esplicito e contestabile vale tantissimo.

    Cosa fa meno bene

    Per task brevi è sovradimensionato: produce sempre il blocco di pensiero, anche per una domanda da due righe, e il tempo di risposta totale lievita. Su task strettamente di codice è meno preciso di un modello specializzato come Qwen2.5-Coder, perché il suo focus è il reasoning e non la generazione. Su prompt molto lunghi (oltre 10k token) la qualità del ragionamento cala vistosamente, e conviene spezzare il problema invece di buttargli tutto in pasto.

    Privacy: tutto resta sul mio host

    DeepSeek-R1 gira interamente sul mio server: nessun provider terzo coinvolto, nessun upload, nessuna telemetria. Ollama si lega su 127.0.0.1:11434 e non parla con l’esterno. I log stanno in ~/.ollama/logs/ e li cancello quando voglio con un rm, senza alcuna policy esterna da rileggere. Rispetto ai servizi cloud, dove ogni prompt e ogni risposta passano per server di terzi (e in qualche caso possono finire dentro pipeline di training o retention prolungata), qui l’analisi resta dentro l’host: i log che gli passo per ragionare non escono mai dalla mia rete. La licenza MIT di DeepSeek-R1 è la più permissiva possibile, posso integrarlo dove voglio senza altri vincoli.

    In pratica

    DeepSeek-R1 7b è il modello che apro per ragionare ad alta voce su un problema poco chiaro. Quando devo invece scrivere o rivedere codice passo a Qwen2.5-Coder, per domande generiche e veloci sul laptop mi appoggio a qwen3:8b, e per risposte brevi sul Mac Mini tengo Gemma3 4B. Tenere a portata modelli con vocazioni diverse cambia parecchio il modo in cui affronto i problemi durante una sessione di lavoro lunga.


    Immagine generata con Cloudflare Workers AI / FLUX.

  • Code Review Locale con Qwen2.5-Coder e opencode su Debian

    Code Review Locale con Qwen2.5-Coder e opencode su Debian

    Qwen2.5-Coder è diventato il mio revisore fisso per le code review che faccio in homelab. Avevo bisogno di un modello che capisse il codice senza che ogni snippet finisse fuori dalla mia rete, perché tanti progetti che mi passano davanti contengono pezzi di configurazione, token di servizio e hostname interni che non voglio veder uscire. La variante 14B su una scheda con 16 GB di VRAM regge una sessione di mezza giornata senza farmi pensare alla latenza, ed è ormai la mia scelta principale per qualsiasi review che tocchi file di una certa lunghezza.

    Il modello è rilasciato da Alibaba con licenza Apache 2.0, quindi posso usarlo liberamente anche per lavoro a pagamento. Lo tengo su un mini server Debian 12 headless, raggiungibile via SSH dal mio desktop. Ollama gira come servizio systemd, si lega di default su 127.0.0.1:11434, e su quella stessa macchina ho opencode installato. Quando devo fare review entro in SSH, apro tmux e lavoro lì dentro per tutto il tempo necessario.

    I parametri che mi interessano sono pochi: 14B di parametri, finestra di contesto base 32k token (la spingo a 16k effettivi via Modelfile per non sprecare VRAM), quantizzazione Q4_K_M predefinita di Ollama. Il knowledge cutoff è metà 2024 e si sente quando arrivano librerie nuove, ma per code review su codice mio non è un problema.

    Setup base con Ollama

    Su Debian 12 lo script ufficiale di Ollama tira giù tutto in un colpo, compreso il service unit systemd.

    
    curl -fsSL https://ollama.com/install.sh | sh
    sudo systemctl enable --now ollama
    ollama pull qwen2.5-coder:14b
    ollama run qwen2.5-coder:14b
    

    Con 8 GB di VRAM la 14B fatica davvero, e in quel caso scendo alla 7B che resta comunque dignitosa per la review riga per riga. Quando devo dargli in pasto file lunghi, parto dal Modelfile esistente e ne creo una variante con un contesto più ampio.

    
    ollama show qwen2.5-coder:14b --modelfile > /tmp/qwen-coder.Modelfile
    

    Apro il file con il mio editor, aggiungo PARAMETER num_ctx 16384 e ricostruisco la variante con ollama create qwen-coder-long -f /tmp/qwen-coder.Modelfile. La uso solo quando serve davvero, perché la finestra grande mangia VRAM e mi costringe a tenere meno modelli caricati in parallelo.

    Setup avanzato con opencode

    opencode è una TUI agentica che si appoggia a un provider OpenAI-compatible, e per le code review fa esattamente quello che mi serve: monta la working directory, mi permette di passare file in contesto con un comando, e mantiene la cronologia della sessione su disco. Ollama da tempo espone un endpoint compatibile su /v1, quindi basta dichiararlo come provider locale dentro ~/.config/opencode/opencode.json.

    
    {
      "provider": {
        "ollama-local": {
          "npm": "@ai-sdk/openai-compatible",
          "options": {
            "apiKey": "ollama",
            "baseURL": "http://localhost:11434/v1"
          },
          "models": {
            "qwen2.5-coder:14b": { "name": "Qwen 2.5 Coder 14B" }
          }
        }
      }
    }
    

    Salvato il file, mi sposto nella cartella del progetto e lancio la sessione agentica.

    
    cd ~/Documenti/progetti/router-config
    opencode . --model ollama-local/qwen2.5-coder:14b
    

    A quel punto sono nella TUI con la working directory già montata, senza wrapper intermedi e senza dovermi inventare integrazioni a parte.

    Un esempio di sessione reale

    Martedì pomeriggio, verso le 16, dovevo rivedere un retry decorator scritto in Python da una persona del team che gestisce una pipeline ETL. Il decorator avvolgeva chiamate HTTP verso un endpoint che ogni tanto restituiva 502 transitori, e la sensazione era che facesse troppi retry e troppo rapidi, saturando il rate limit a valle. Ho aperto la cartella del progetto in opencode, ho chiesto a Qwen2.5-Coder di leggere il file retry.py e di valutare se il backoff fosse implementato correttamente.

    La risposta è arrivata in una trentina di secondi e ha colto due cose che mi erano sfuggite: il jitter era applicato solo al primo tentativo (per un errore di indentazione del randint dentro il loop), e il decorator non distingueva tra eccezioni di rete e errori HTTP 4xx, finendo per ritentare anche su 401 e 403. Ho chiesto una proposta di refactoring tenendo le firme pubbliche identiche e ho ottenuto una versione con backoff esponenziale corretto, jitter su ogni tentativo, e una whitelist esplicita di status code retriabili. Il merge request è uscito in serata.

    Cosa fa bene

    Sul codice scritto in linguaggi mainstream (Python, TypeScript, Go, bash, SQL) la qualità della review è solida. Coglie pattern di concorrenza fragile, vede dove manca la gestione delle eccezioni, suggerisce refactor di funzioni che fanno troppe cose. Sui Dockerfile e sui file Ansible è meno brillante ma sempre utile, soprattutto per stanare uso di latest come tag e privilegi superflui nei container.

    Cosa fa meno bene

    Su librerie uscite dopo la metà del 2024 inventa firme che sembrano plausibili ma non esistono, quindi le sue proposte vanno sempre verificate contro la documentazione vera. La 14B in Q4 ogni tanto perde il filo su file molto lunghi anche con num_ctx alzato, e la latenza per risposta lunga non è paragonabile a quella di un servizio cloud su acceleratori dedicati: con prompt da 6k token la prima parola arriva in 4-5 secondi.

    Privacy: tutto resta sul mio host

    Qwen2.5-Coder gira interamente dentro il mio server: nessun provider terzo coinvolto, nessun upload, nessuna telemetria di default. Ollama si lega su 127.0.0.1:11434 e non espone nulla all’esterno. Gli unici log sono in ~/.ollama/logs/, posso cancellarli quando voglio con un rm, e non c’è alcuna policy esterna da rileggere a ogni aggiornamento. Rispetto ai modelli cloud, dove ogni snippet passa per i server di chi offre il servizio (con retention variabile e, in qualche caso, riuso per training se non si fa opt-out), qui la differenza è radicale: il codice non esce mai dal mio host. La licenza Apache 2.0 di Qwen 2.5 Coder mi lascia libero di usarlo anche in contesti professionali senza ulteriori vincoli.

    In pratica

    Qwen2.5-Coder 14B è il modello che apro quando la review tocca codice mio o di progetti che gestisco direttamente. Per ragionamento esplicito su scelte architetturali passo a DeepSeek-R1 7B, per domande generiche e veloci sul laptop mi appoggio a qwen3:8b, e per risposte brevi e multimodali sul Mac Mini tengo Gemma3 4B. La scelta dipende da quanto contesto serve, da quanto tempo voglio aspettare, e da quale macchina è accesa in quel momento.


    Immagine generata con Cloudflare Workers AI / FLUX.

  • La resurrezione di monitoring: come ho salvato i dati da una microSD corrotta

    La resurrezione di monitoring: come ho salvato i dati da una microSD corrotta

    Una mattina di qualche settimana fa, mentre prendevo il caffè, mi accorgo che la dashboard Grafana non risponde. Stessa cosa per la web UI di Observium e per il logger centrale. Tre servizi giù insieme: non è coincidenza, è un host giù. Quell’host, nel mio homelab, è un Raspberry Pi 5 8GB in case dedicato che chiamo “monitoring” (qui lo chiamo rpi-monitoring) e che fa girare diversi servizi:

    Snipe-IT per asset management

    Observium Community come NMS che polla SNMP gli altri host

    InfluxDB v2 come time series database delle metriche

    rsyslog centrale che riceve log dalla LAN

    MariaDB condiviso fra Snipe-IT e Observium

    Tutto girava su una microSD 64 GB classe A1 generica. Dopo circa un anno di servizio la scheda si è bloccata: kernel pieno di mmc0: Card stuck being busy e poi EXT4-fs error a cascata. Ecco come ho recuperato i dati e ricostruito il servizio.

    Perché la microSD è morta

    Una microSD generica entry-level è progettata per fotografia e video: scritture sequenziali grosse, poche cancellazioni. Quello che le ho fatto fare era l’esatto contrario: SQL relazionale di Snipe-IT, polling SNMP Observium ogni cinque minuti su dieci host con scrittura MariaDB + RRD, ingestion InfluxDB v2 di metriche ogni dieci secondi, rsyslog centrale, journald del Pi, log rotation di Apache e nginx.

    Il TBW sopportabile da una microSD entry-level è qualche TB. Con quel carico ci si arriva in 8-12 mesi. Errore mio: non aver migrato a SSD via USB 3.0 prima.

    Step 1: bitwise dump con ddrescue

    La prima cosa che si fa con un dispositivo morente, prima di provare a recuperare file, è un dump bitwise dell’intera scheda. ddrescue è lo strumento giusto: legge sequenzialmente, prova a recuperare i settori illeggibili con strategie successive e mantiene un logfile delle aree da riprovare. Su un Raspberry Pi di servizio con uno slot USB ho infilato la microSD malata tramite un adattatore e ho lanciato:

    
    sudo apt install gddrescue
    sudo ddrescue -d -r 3 /dev/sda /mnt/ssd/monitoring.img /mnt/ssd/monitoring.log
    

    -d salta la cache del kernel e legge in direct I/O, -r 3 riprova ogni settore tre volte. Il dump è andato avanti per quasi cinque ore. Risultato: 62,8 GB su 64 GB recuperati, 1,2 GB irrecuperabili distribuiti soprattutto nell’area dove stavano i datafile MariaDB di un mese. Già un buon punto di partenza.

    Step 2: montare l’immagine read-only

    Con l’immagine in mano, l’ho associata a un loop device read-only per non rischiare di scriverci sopra:

    
    sudo losetup -r -fP /mnt/ssd/monitoring.img
    sudo losetup -a
    

    Il losetup -a mostra il device /dev/loop0 con partizioni loop0p1 (boot) e loop0p2 (rootfs). Ho montato la rootfs:

    
    sudo mkdir -p /mnt/recover
    sudo mount -o ro /dev/loop0p2 /mnt/recover
    

    Filesystem montato pulito, con qualche file segnato come corrotto in dmesg ma navigabile.

    Step 3: estrazione dati per servizio

    Ho copiato selettivamente i datadir dei servizi importanti su un SSD USB di lavoro:

    
    sudo rsync -aH --info=progress2 /mnt/recover/var/lib/mysql /mnt/ssd/recover/
    sudo rsync -aH --info=progress2 /mnt/recover/var/lib/influxdb /mnt/ssd/recover/
    sudo rsync -aH --info=progress2 /mnt/recover/var/lib/observium /mnt/ssd/recover/
    sudo rsync -aH --info=progress2 /mnt/recover/etc /mnt/ssd/recover/
    

    Per MariaDB il datafile più recente era nell’area corrotta. Ho recuperato il mysqldump notturno dal NAS, datato 7 ore prima del crash: perdita accettabile.

    Step 4: ricostruzione su nuovo hardware

    Ho montato un Raspberry Pi 5 8GB nuovo, microSD A2 ad alta affidabilità solo per OS, SSD USB 3.0 da 480 GB per /var/lib/observium, /var/lib/influxdb, /var/lib/mysql e /var/lib/observium/rrd tramite bind mount in fstab:

    
    /mnt/ssd/observium  /var/lib/observium  none  bind  0  0
    /mnt/ssd/influxdb   /var/lib/influxdb   none  bind  0  0
    /mnt/ssd/mysql      /var/lib/mysql      none  bind  0  0
    /mnt/ssd/rrd        /var/lib/observium/rrd  none  bind  0  0
    

    Ripristino del dump SQL, copia dei datadir InfluxDB e Observium dal SSD di recupero, riavvio servizi. Snipe-IT è tornato online con sette ore di asset persi (un paio di check-out ricostruibili a memoria). Observium ha ripreso polling con storia RRD intatta. InfluxDB ha ripreso con un buco di un paio d’ore.

    Un caso reale dentro il caso

    Mentre chiudevo il restore, intorno alle 22:30, ho notato che Observium non discoverava due switch aggiunti la settimana prima. La config era persa nel buco MariaDB? No: avevano cambiato community string dopo una rotazione che mi ero dimenticato di propagare. Credenziali corrette, switch tornati visibili. La documentazione delle credenziali deve vivere fuori dall’host che stai ripristinando, sempre.

    Cosa funziona bene

    ddrescue fa una cosa e la fa benissimo. Il logfile permette di riprovare incrementalmente sui settori illeggibili senza rifare l’intera immagine. Avere backup esterni (mysqldump notturno su NAS, snapshot config di /etc su altra macchina) ha trasformato un disastro potenziale in un fastidio di mezza giornata.

    Limiti

    L’unico vero limite era la microSD stessa: classe A1 generica per workload database-heavy è stato un errore di partenza. Le microSD A2 industrial sono molto più resistenti, ma per workload veri serve SSD via USB 3.0 o M.2 HAT.

    In pratica

    è la storia di scelte hardware sbagliate compensate da una disciplina di backup ragionevole. Oggi configuro SSD via USB 3.0 da subito su qualsiasi Pi che ospiti database o ingestion log, e tengo backup esterni con cadenza coerente con il valore dei dati. Una microSD corrotta non è più un disastro, è una procedura.


    Immagine generata con Cloudflare Workers AI / SDXL Base 1.

  • Troubleshooting connettività in un homelab eterogeneo

    Troubleshooting connettività in un homelab eterogeneo

    In un homelab che mescola Raspberry Pi, mini PC con Linux, un Mac, qualche IoT scemo e uno switch managed, prima o poi capita: “un host non risponde più, non capisco perché”. E sotto la frustrazione c’è quasi sempre la stessa cosa: una catena di ipotesi non verificate. Il modo per uscirne in tempi ragionevoli è una checklist mentale che parte dal basso e sale.

    Racconto una sessione di troubleshooting recente e i comandi che uso, ne approfitto per fissare il metodo per la prossima volta.

    L’incidente

    Un sabato pomeriggio, verso le 16:00, mi accorgo che un Raspberry Pi del homelab (lo chiamo rpi-monitoring) non risponde più al ping dal portatile. Dalle metriche Grafana risulta off dalla rete da circa un’ora. Niente alert sul telefono perché avevo silenziato Telegram nel pomeriggio. Ma il fatto è lì: l’host non c’è più.

    Step 1: dove sta il problema?

    La prima domanda non è “perché non risponde”, è “da quale layer in giù la cosa funziona”. Vado dal portatile in giù:

    
    ping -c 4 rpi-monitoring
    

    Risposta: Name or service not known. Quindi il DNS non risolve. Il primo strato già rotto. Provo a interrogare direttamente il resolver interno:

    
    dig +short rpi-monitoring @203.0.113.1
    

    Risposta vuota. Il DNS interno non ha più il record. Sospetto un DHCP-DNS che non ha rinnovato la lease. Provo per IP statico, perché ricordo che ha un fallback:

    
    ping -c 4 203.0.113.42
    

    Anche qui timeout. Il problema non è DNS, è che proprio non raggiungo l’host in IP.

    Step 2: layer 2 o layer 3?

    Ipotesi: il device non è connesso alla LAN. Lo verifico dallo switch managed (accesso via Tailscale dato che è raggiungibile sulla management interface):

    
    ssh admin@switch-managed
    show interfaces ethernet port5 status
    

    Port5 down. Cavo non collegato, NIC del device giù, o cavo rotto. Vado fisicamente al rack: cavo collegato sia sullo switch che sul Pi, ma spia LAN sul Pi spenta. Tolgo l’alimentatore e lo riattacco. La spia POWER resta spenta. Provo un altro alimentatore: idem. Sospetto SBC morto.

    Step 3: c’è alimentazione?

    Tiro fuori un multimetro economico, misuro la tensione del cavo USB-C dell’alimentatore: 5,15 V, dentro spec. Il problema non è l’alimentatore. Provo a infilare la microSD del Pi morto in un altro Raspberry Pi 5 di backup: avvio in 30 secondi, dmesg pulito. Il problema è hardware sul Pi originale. Probabilmente il regolatore di tensione lato SBC è andato.

    A questo punto la troubleshooting di rete è finita: non c’era niente da fare via comandi. Il device è hardware-dead, non network-dead. Ma se avessi saltato i primi step e fossi corso a sostituire l’host senza verificare DNS, switch e fisico, avrei potuto perdere un’ora a configurare un Pi nuovo per scoprire poi che lo switch aveva la porta down per un altro motivo.

    Comandi che uso spesso

    Senza ordine preciso, la cassetta degli attrezzi:

    
    ip link show
    ip addr show
    ip route show
    ss -tunlp
    arp -an
    ping -c 4 -W 2 host
    traceroute -n host
    mtr -n -c 10 host
    dig +short host @resolver
    nslookup host resolver
    nmap -sn 203.0.113.0/24
    tcpdump -i eth0 -nn host 203.0.113.42
    ethtool eth0
    sudo nft list ruleset
    

    Su Mac molti hanno equivalenti con flag diversi (ping con -W in millisecondi, traceroute senza -n per default).

    Un caso reale collaterale

    Stessa settimana, un altro incidente: latenza folle (centinaia di ms) e pacchetti duplicati ogni ping verso un host in LAN. Sintomo classico di ARP duplicato su VLAN. arp -an dal portatile mostrava due MAC diversi per lo stesso IP. Un Pi vecchio, smesso da mesi e tirato fuori per un test, era stato ricollegato con IP statico identico a uno già attivo. L’avevo dimenticato. Spegnimento del Pi vecchio, arp -d 203.0.113.42 per pulire la cache, latenza tornata a valori normali in dieci secondi. Anche quel caso si è risolto in cinque minuti perché ho seguito la checklist invece di indovinare.

    Cosa funziona bene

    Avere strumenti standard (ping, dig, mtr, tcpdump, ip) installati ovunque, su ogni Pi e mini PC. Niente tool esotici, niente script personalizzati. Funzionano dappertutto, e quando arriva il problema in mezzo alla notte non devi ricordarti come si lancia un binario custom.

    Limiti

    Il troubleshooting di rete su Wi-Fi è sempre più frustrante: client che migrano fra AP, mesh che fanno scelte opache, banda 2.4/5/6 GHz che cambia comportamento. Per quello tengo separati i client Wi-Fi dai server, e i server stanno tutti su ethernet. Niente Wi-Fi nei percorsi critici.

    In pratica

    Il metodo è più importante degli strumenti. Partire dal layer 1 (cavo, alimentazione, link) e salire (layer 2 MAC/switch, layer 3 IP, layer 4 porte, layer 7 applicazione) evita di girare a vuoto. Quasi tutti i problemi si risolvono nei primi tre layer, e il tempo speso a verificare l’ovvio si recupera ampiamente non andando dietro alle ipotesi sbagliate.


    Immagine generata con ComfyUI Mac M1 / RealVisXL V5 Lightning.