Ollama: NUM_PARALLEL e MAX_QUEUE spiegati
Scopri quando la seconda richiesta Ollama resta in coda o riceve HTTP 503 e perché ogni slot parallelo usa VRAM aggiuntiva per la cache KV.
Cosa succede alla seconda richiesta Ollama mentre viene generata la prima
La concorrenza di Ollama è determinata da tre variabili d’ambiente e, nella configurazione predefinita, un modello caricato gestisce una richiesta alla volta. La seconda richiesta non viene rifiutata e non riceve una risposta parziale. Rimane in coda finché non si libera uno slot, quindi viene elaborata alla velocità normale.
Una richiesta in ingresso può avere tre esiti. Può essere avviata immediatamente in uno slot libero. Può rimanere in coda. Oppure, se la coda è già piena, il server può rifiutarla con HTTP 503. L’esito dipende da OLLAMA_NUM_PARALLEL, OLLAMA_MAX_QUEUE e OLLAMA_MAX_LOADED_MODELS.
La configurazione predefinita è sicura, ma spiega anche perché un secondo utente segnali che il server si è "bloccato" quando in realtà non c’è alcun problema. Aggiungere slot richiede due righe di modifica. Il problema principale è la memoria. Ogni slot parallelo richiede una propria cache key/value (KV cache), cioè l’area di memoria in cui il modello conserva i token già elaborati. Aggiungere slot senza aumentare la VRAM (memoria video della GPU) può trasformare una risposta lenta in un caricamento non riuscito.
Che cosa controllano OLLAMA_NUM_PARALLEL, OLLAMA_MAX_QUEUE e OLLAMA_MAX_LOADED_MODELS
Questi sono i valori predefiniti delle versioni attuali di Ollama ad agosto 2026. Verifica i tuoi invece di fare affidamento sul numero riportato qui, usando la riga del log mostrata più avanti.
OLLAMA_NUM_PARALLELindica quante richieste può gestire contemporaneamente un modello caricato. Il valore predefinito è 1, quindi le richieste vengono servite una alla volta.OLLAMA_MAX_LOADED_MODELSindica quanti modelli diversi possono rimanere residenti contemporaneamente. Il valore predefinito è 0, che significa che Ollama sceglie: tre modelli per GPU e tre su una macchina senza GPU.OLLAMA_MAX_QUEUEindica quante richieste possono rimanere in attesa. Il valore predefinito è 512. La richiesta che arriva quando la coda è piena viene rifiutata immediatamente.
Nel caso peggiore, la memoria necessaria è il prodotto dei primi due valori. Due modelli caricati con quattro slot ciascuno richiedono otto allocazioni di slot per la cache KV, tutte residenti contemporaneamente, e Ollama cercherà di soddisfare questa richiesta. Su una macchina con una sola GPU, di solito è preferibile mantenere un solo modello e assegnargli più slot, perché il calcolo resta semplice da fare a mente.
Perché ogni slot parallelo richiede VRAM
Quando Ollama carica un modello, avvia un processo runner separato. In questo caso sono importanti due degli argomenti che gli passa: -c è il contesto totale per cui il runner alloca una cache KV, mentre -np è il numero di sequenze parallele. Ollama imposta -c moltiplicando la lunghezza del contesto per richiesta per il numero di slot. Il runner divide quindi il totale in parti uguali tra gli slot, così ogni richiesta riceve comunque la lunghezza del contesto richiesta.
Questo è l'intero vincolo e spiega perché il parallelismo non è gratuito. Passare da uno slot a quattro richiede una cache KV quattro volte più grande, a parità di contesto per richiesta. Gli slot non condividono nulla e la quota di uno slot inattivo non viene assegnata a uno occupato, perché la suddivisione viene definita all'avvio del runner.
Puoi leggere i valori effettivi invece di quelli che avevi impostato:
journalctl -u ollama --no-pager -n 500 | grep "starting llama-server"Quella riga contiene la riga di comando completa del runner, inclusi -c e -np. Se -np è 1 dopo aver impostato la variabile, la configurazione non sta raggiungendo il server. La sezione successiva spiega il motivo.
Se i pesi del modello e la relativa cache KV non entrano nella VRAM, Ollama sposta alcuni layer nella RAM di sistema e quei layer vengono eseguiti dalla CPU. I layer eseguiti dalla CPU sono molto più lenti di quelli eseguiti dalla GPU, quindi ogni richiesta diventa più lenta, inclusa l'unica richiesta da cui eri partito. Aumentare il parallelismo può quindi ridurre il throughput invece di aumentarlo. Con un modello abbastanza grande, sono i soli pesi a determinare il risultato prima ancora di calcolare gli slot. Per questo self-hosting di qualcosa delle dimensioni di Kimi K3 è una questione legata al numero di schede disponibili, non al numero di slot configurati.
ollama psLa colonna PROCESSOR mostra 100% GPU quando l'intero modello entra nella memoria disponibile. Una suddivisione come 35%/65% CPU/GPU indica che una parte del modello viene eseguita dalla CPU. La colonna SIZE include la cache KV, quindi aumenta quando incrementi il numero di slot e ricarichi il modello. Aumenta OLLAMA_NUM_PARALLEL, riavvia, invia una richiesta ed esegui di nuovo ollama ps: in questo modo misuri il costo in memoria della modifica invece di stimarlo. Se la misurazione indica che il modello non entra più nella memoria disponibile, ricorda che i pesi costituiscono l'altra metà dello stesso budget. Passare da una build fp16 a una q8 o q4 spesso libera più VRAM di quella richiesta dallo slot aggiuntivo.
La lunghezza del contesto e il numero di slot si moltiplicano, quindi devono essere scelti insieme. Un contesto ampio con quattro slot equivale a quattro contesti ampi. Se stai regolando anche la finestra di contesto num_ctx del modello, modifica un solo parametro alla volta. Altrimenti non saprai quale dei due ha riempito la scheda.
Come impostare queste variabili in modo che persistano dopo un riavvio
Su Linux, Ollama viene eseguito come servizio systemd. Eseguire export OLLAMA_NUM_PARALLEL=4 nella shell non cambia nulla, perché systemd avvia il servizio con il proprio ambiente e non vede quello della shell. Usare un file drop-in.
sudo systemctl edit ollama.serviceAggiungere questo contenuto nell'editor che si apre:
[Service]
Environment="OLLAMA_NUM_PARALLEL=4"
Environment="OLLAMA_MAX_LOADED_MODELS=1"
Environment="OLLAMA_MAX_QUEUE=32"Quindi ricaricare la configurazione e riavviare il servizio:
sudo systemctl daemon-reload
sudo systemctl restart ollama
systemctl show ollama --property=Environmentsystemctl show mostra ciò che systemd passerà al processo. Se la variabile manca, il file drop-in non è stato salvato oppure daemon-reload non è stato eseguito. Verificare anche direttamente dal server:
journalctl -u ollama --no-pager | grep "server config" | tail -1All'avvio, Ollama registra l'intero ambiente in una riga il cui messaggio è server config. Quella mappa rappresenta il valore effettivo. È il modo più rapido per verificare se una variabile è stata applicata.
Un modello già caricato mantiene il numero di slot con cui è stato avviato, perché il valore viene fissato nel processo runner all'avvio. Il riavvio precedente scarica tutto; la richiesta successiva ricarica quindi il modello con la nuova impostazione e sostiene il tempo di caricamento una sola volta. Per quanto tempo il modello rimane residente dopo il caricamento è controllato separatamente ed è descritto in mantenere un modello Ollama caricato tra le richieste.
Come appaiono al client le richieste servite, accodate e rifiutate
Invia più richieste contemporaneamente e misura i tempi. Questo comando esegue otto richieste di streaming in parallelo e stampa lo stato e i tempi di ciascuna:
for i in $(seq 1 8); do
curl -s -o /dev/null \
-w "req$i http=%{http_code} ttfb=%{time_starttransfer}s total=%{time_total}s\n" \
http://127.0.0.1:11434/api/generate \
-d '{"model":"llama3.2:3b","prompt":"Explain what a KV cache is.","stream":true}' &
done
waitttfb è il tempo al primo byte dello stream. È simile al tempo al primo token (TTFT), perché il primo blocco trasmesso contiene il primo token.
Servite in parallelo. Ogni richiesta riporta un valore simile di ttfb e total aumenta contemporaneamente per tutte. La GPU viene condivisa tra gli slot in esecuzione. Di conseguenza, ogni risposta è più lenta rispetto all'esecuzione isolata, ma vengono completate più risposte al minuto. Questo è il comportamento che si ottiene aumentando OLLAMA_NUM_PARALLEL.
Accodate. Le prime richieste ricevono rapidamente una risposta, mentre quelle successive mostrano un valore elevato di ttfb seguito da una generazione normale. L'attesa dipende dalla coda, non dal modello. In una finestra di chat l'utente vede una lunga pausa senza testo, quindi l'output procede alla velocità normale. Questo andamento, lento all'avvio e poi rapido, indica una coda e non una GPU sovraccarica.
Rifiutate. Il client riceve http=503 quasi immediatamente e il corpo della risposta è:
{"error":"server busy, please try again. maximum pending requests exceeded"}Il messaggio indica che la coda era piena quando è arrivata la richiesta. Non fornisce alcuna informazione sulla VRAM o sul modello.
Un limite da considerare: Ollama non pubblica la profondità della coda. ollama ps e l'endpoint /api/ps indicano i modelli caricati, non le richieste in attesa. Per questo la coda va misurata dal lato client, osservando il tempo al primo byte, oppure contando le risposte 503 generate dal componente che si trova davanti al servizio.
Perché un valore MAX_QUEUE più basso è spesso l'impostazione migliore
Una coda da 512 richieste sembra ampia, ma con un solo slot è quasi inutile. La richiesta 300 resta in attesa dopo 299 generazioni completate. Nel migliore dei casi, l'attesa dura diversi minuti. Ogni client HTTP interrompe l'attesa molto prima. Il chiamante riceve quindi un timeout lato client, che non indica la causa del problema e non fornisce al monitoraggio un evento utile da segnalare.
Impostate la coda su un valore proporzionato al numero di richieste che il server riesce a completare entro il timeout del client. In questo modo, le richieste eccedenti ricevono immediatamente un errore 503. Un errore 503 è utile: un reverse proxy può riprovare, un client può aumentare progressivamente l'intervallo tra i tentativi, una dashboard può conteggiarlo e un operatore può leggerlo. Ricavate il valore dalle vostre misurazioni. Se una generazione richiede circa dieci secondi e il client attende sessanta secondi, ogni slot può completare circa sei richieste entro quel limite. Una coda molto più lunga produrrebbe soltanto timeout.
Quando inserire una coda davanti a Ollama
La coda integrata segue l'ordine di arrivo (FIFO) e non sa chi effettua la chiamata. Per un'applicazione che comunica con un solo server è sufficiente; aggiungere altra infrastruttura introdurrebbe soltanto nuovi punti di guasto. È opportuno anteporre un altro componente quando si verifica una delle seguenti condizioni.
- È necessaria una priorità. Una chat interattiva non dovrebbe attendere dietro a un processo batch di riepilogo. La coda di Ollama non supporta priorità, quindi i processi batch devono essere trattenuti all'esterno e inviati gradualmente.
- È necessaria un'equa distribuzione delle risorse. Un client può riempire la coda da solo e tutti gli altri ricevono quindi 503.
- È necessario che il lavoro sopravviva a un riavvio. La coda risiede nella memoria del server. Se si riavvia Ollama, tutte le richieste in attesa vengono perse.
- Sono necessari tentativi reali con backoff, registrati in un punto che sia possibile consultare in seguito.
La soluzione più semplice è un reverse proxy. In nginx, limit_conn limita il numero di connessioni simultanee e limit_req limita la frequenza di arrivo per client, quindi le richieste in eccesso vengono rifiutate dal proxy e non raggiungono mai la coda di Ollama. La soluzione più completa consiste in una coda di lavori con un database, davanti a un worker che chiama Ollama. È la scelta appropriata quando le richieste devono sopravvivere al riavvio di un processo. Dimensionare questa soluzione per il traffico reale è un'attività separata: pianificare un LLM self-hosted per utenti simultanei illustra i calcoli, mentre eseguire Ollama su un VPS descrive l'installazione di base presupposta da queste variabili.
Quando la risposta corretta è un server diverso
Esiste un limite che non puoi superare modificando la configurazione. Ollama divide la KV cache in slot uguali e fissi quando carica il modello. La memoria di uno slot inattivo non può essere utilizzata da uno slot occupato, e il numero di slot non può cambiare senza scaricare il modello. Questo comportamento è adatto a una singola persona, a un piccolo team o a un coding agent.
I server progettati per molti utenti simultanei funzionano in modo diverso. Allocano la KV cache in piccole pagine su richiesta e aggiungono le richieste in arrivo a un batch già in esecuzione, così la memoria segue la domanda effettiva invece di una suddivisione fissa. Se il tuo obiettivo è gestire molti utenti concorrenti su una singola GPU, questa differenza architetturale è più importante di qualsiasi valore di OLLAMA_NUM_PARALLEL. Il confronto tra Ollama e vLLM è il punto in cui prendere questa decisione. Non cambiare server per principio: un server diverso richiede più attività operative e, se il traffico proviene da poche persone, il comportamento integrato è la scelta corretta.
Misura il throughput e il time to first token
I valori pubblicati in token al secondo provengono dalla GPU, dal modello, dalla quantizzazione, dalla lunghezza del contesto e dal prompt utilizzati da qualcun altro. Nessuno di questi parametri coincide con i tuoi. Considera quindi ogni valore letto come una semplice indicazione e misura direttamente la macchina che stai utilizzando.
Ollama restituisce i tempi nell'oggetto JSON finale di ogni risposta. eval_count indica il numero di token generati e eval_duration il tempo impiegato per generarli, espresso in nanosecondi.
sudo apt install -y jq
curl -s http://127.0.0.1:11434/api/generate \
-d '{"model":"llama3.2:3b","prompt":"Explain what a KV cache is.","stream":false}' \
| jq '{prompt_eval_count, eval_count, eval_duration, tokens_per_second: (.eval_count / (.eval_duration / 1000000000))}'Esegui il test con uno slot, quindi ripetilo usando il livello di concorrenza previsto in condizioni reali. Confronta i due valori che determinano la soddisfazione degli utenti: il time to first token e il numero di token al secondo per richiesta. Il throughput per richiesta diminuisce sempre quando aggiungi slot. La domanda è se diminuisce oltre il limite che gli utenti sono disposti ad accettare. Misurare i token al secondo con un LLM locale descrive il metodo in modo più dettagliato, incluso come mantenere costante il prompt tra le esecuzioni.
Un endpoint pubblico con una coda ampia è un obiettivo per gli attacchi di tipo denial of service
L’impostazione `OLLAMA_HOST=0.0.0.0:11434` espone l’API su tutte le interfacce e Ollama non dispone di autenticazione integrata. Un endpoint aperto con la coda predefinita accetta 512 richieste in attesa da chiunque lo individui. Riempire quella coda costa quasi nulla a un attaccante: prompt lunghi, nessun accesso, nessun limite di frequenza e nessun costo. I tuoi utenti ricevono quindi risposte 503 o devono attendere a lungo, mentre la macchina rimane occupata per tutto il tempo.
Mantieni il listener sull’interfaccia di loopback e raggiungilo tramite un tunnel SSH o una rete privata, oppure configura autenticazione e rate limiting davanti all’endpoint. Proteggere un endpoint API di Ollama tratta entrambe le opzioni. Regola la coda solo dopo aver completato questa configurazione, perché la lunghezza della coda è un’impostazione di capacità e non protegge nulla.
FAQ
Perché la seconda richiesta a Ollama attende il completamento della prima?
Perché OLLAMA_NUM_PARALLEL è impostato su 1 per impostazione predefinita. Un modello caricato elabora una richiesta alla volta e le altre attendono in ordine. La richiesta in attesa mantiene aperta la connessione HTTP e non invia byte finché non si libera uno slot. Dal lato client, questo ha lo stesso aspetto di un modello lento. L'indicatore è la forma dei tempi: una lunga pausa seguita da testo alla massima velocità indica una coda, mentre un flusso lento fin dal primo token indica un modello lento. Aumenta il numero di slot con un drop-in di systemd e riavvia il servizio.
Che cosa significa "server busy, please try again. maximum pending requests exceeded"?
È l'errore di overflow della coda di Ollama, restituito con stato HTTP 503. Il numero di richieste già in attesa ha raggiunto OLLAMA_MAX_QUEUE, che per impostazione predefinita è 512. La richiesta più recente è stata quindi rifiutata invece di essere aggiunta alla coda. Non è un errore di memoria e non è un errore del modello. Aumentare la coda fa soltanto attendere più a lungo i client prima dello stesso rifiuto. Le soluzioni effettive sono aumentare il numero di slot, se hai VRAM sufficiente, ridurre il carico in ingresso oppure usare una coda intermedia in grado di ritentare le richieste e assegnare loro una priorità.
Aumentare OLLAMA_NUM_PARALLEL rende Ollama più veloce?
No. Consente di eseguire più richieste contemporaneamente, ma ciascuna richiesta è più lenta rispetto all'esecuzione isolata perché condividono la stessa GPU. Inoltre moltiplica la cache KV, perché Ollama avvia il runner con un contesto totale pari alla lunghezza del contesto moltiplicata per il numero di slot. Se il risultato non entra più nella VRAM, Ollama sposta alcuni layer sulla CPU e tutte le richieste diventano più lente, compresa una singola richiesta senza concorrenza. Dopo la modifica, controlla ollama ps e verifica che la colonna PROCESSOR riporti ancora 100% GPU.
Devo riavviare Ollama dopo aver modificato queste variabili?
Sì. Il server le legge all'avvio e un modello in esecuzione mantiene il numero di slot definito nel processo runner al momento dell'avvio. Modifica il drop-in con sudo systemctl edit ollama.service, quindi esegui sudo systemctl daemon-reload e sudo systemctl restart ollama. Verifica con systemctl show ollama --property=Environment, poi controlla la riga server config in journalctl -u ollama, che elenca l'ambiente effettivamente caricato dal server.
Quanti slot paralleli devo impostare?
Inizia da 1 e aumenta il valore di un'unità alla volta. Dopo ogni modifica, riavvia Ollama, invia una richiesta per caricare il modello ed esegui ollama ps. Interrompi l'aumento all'ultimo valore per cui PROCESSOR riporta ancora 100% GPU e la colonna SIZE conserva margine per il contesto più lungo che servi. A quel valore misura il tempo al primo token e i token al secondo con la concorrenza effettiva del tuo ambiente. Riduci quindi il valore di un'unità se la velocità per richiesta è scesa al di sotto del livello accettabile per gli utenti.