Browser OpenClaw su VPS: perché non funziona e come risolvere
«Ho problemi enormi con il mio VPS. Sto reinstallando OpenClaw per la terza o quarta volta.»
Questo messaggio è comparso nel Discord di OpenClaw il mese scorso. L’utente non era l’unico. Scorrendo i canali della community si trovano decine di variazioni sullo stesso tema: lo strumento browser fallisce, l’agente non riesce a raggiungere Chromium, il VPS esaurisce la memoria, ieri funzionava tutto e oggi no.
Se sta leggendo questo articolo perché ha appena cercato «OpenClaw browser non funziona VPS», è nel posto giusto. Questo post spiega le cinque cause di malfunzionamento, perché interagiscono in modi frustranti, e come le abbiamo risolte tutte nell’operatore Kubernetes di OpenClaw.rocks così che non debba mai più occuparsi della configurazione del browser.
L’errore che tutti vedono
Il messaggio di errore più comune nella community di OpenClaw è questo:
Can't reach the OpenClaw browser control service
(timed out after 15000ms)
Compare su Ubuntu, Debian, in Docker, su Hetzner, Hostinger, GCP e ovunque si tenti di eseguire OpenClaw con l’automazione del browser. I log del gateway dicono «Browser control service ready.» Ma quando l’agente prova a usare il browser, scade il timeout.
L’errore è generico. Le cause no. Esistono almeno cinque modalità di guasto distinte, e dall’esterno sembrano tutte identiche.
Guasto 1: Snap Chromium e il muro di AppArmor
Su un server Ubuntu 22.04+ appena installato, which chromium-browser restituisce /usr/bin/chromium-browser. Sembra corretto. Non lo è.
Da Ubuntu 22.04, il pacchetto Chromium predefinito è uno snap. Quando il gateway di OpenClaw tenta di avviare quel binario tramite un servizio systemd, lo strato di confinamento di AppArmor lo blocca. Il pacchetto snap non può aprire le porte di debug del Chrome DevTools Protocol (CDP) attraverso la sandbox. Il binario si avvia, sembra partire, poi fallisce silenziosamente nell’aprire la porta di cui OpenClaw ha bisogno.
Nei log vedrà «Failed to start Chrome CDP on port 18800», o a volte nulla. Il processo del browser si avvia e muore prima di scrivere una sola riga di log.
La soluzione è installare il pacchetto .deb di Google Chrome o usare il binario Chromium standalone di Playwright da ~/.cache/ms-playwright/. Entrambi aggirano la sandbox snap. Ma la soluzione non è ovvia. Il rilevamento del browser di OpenClaw scansiona i percorsi standard in ordine, trova prima il binario snap e tenta di usarlo. L’issue GitHub #4978 contiene una descrizione completa.
Guasto 2: Impostazioni headless che presuppongono un display
OpenClaw viene fornito con headless: false e noSandbox: false come impostazioni predefinite del browser. Ha senso su un Mac o un PC Windows con display. Su un VPS non c’è display. Senza configurazione esplicita, Chromium tenta di aprire una finestra su un server grafico che non esiste.
Due modifiche alla configurazione risolvono il problema:
openclaw config set browser.headless true
openclaw config set browser.noSandbox true
La maggior parte delle guide VPS lo menziona. Ma se segue le istruzioni di installazione standard di OpenClaw, nessuna delle due righe compare. Installa, prova il browser, fallisce. Cerca l’errore. Trova un articolo. Modifica la configurazione. Riavvia. E passa al guasto numero tre.
Guasto 3: L’OOM kill invisibile
Una singola scheda Chromium con alcune pagine aperte consuma da 2 a 4 GB di RAM. Su un VPS da 4 GB, non resta quasi nulla per OpenClaw stesso, il sistema operativo e qualsiasi altro servizio in esecuzione.
Quando Linux esaurisce la memoria, l’OOM killer del kernel termina il processo più avido di memoria. Quello è Chromium. Il processo muore. OpenClaw registra un timeout del browser. Nessun crash report, nessuno stack trace, nessuna indicazione che la memoria fosse il problema.
C’è anche una versione più sottile di questo problema. Chromium genera processi renderer figlio per ogni scheda. Se questi non vengono ripuliti correttamente, si accumulano. Una issue GitHub ha documentato 39 processi renderer orfani che consumavano 3,8 GB di RAM su un VPS che avrebbe dovuto avere ampio margine.
Si può verificare dopo il fatto con dmesg | grep -i "oom\|killed\|chromium", ma la maggior parte delle persone non pensa di guardare lì. Riavviano OpenClaw, funziona per qualche minuto, e poi succede di nuovo.
La pagina ufficiale dei requisiti server raccomanda 4 GB minimo per un setup di base. Ma quel numero presuppone l’assenza di automazione del browser. Con il browser attivato, 8 GB è il minimo realistico. Su Hetzner, è la differenza tra un cpx22 (5 $/mese) e un cpx42 (17 $/mese).
Guasto 4: La memoria condivisa mancante di Docker
Se esegue OpenClaw in Docker (comune per i deployment VPS), c’è un’altra trappola. Il /dev/shm predefinito di Docker è 64 MB. Chromium usa la memoria condivisa per la comunicazione tra processi. Quando si esaurisce, le schede crashano silenziosamente o mostrano pagine bianche.
La soluzione è una riga nel suo docker-compose.yml:
shm_size: '2gb'
Oppure montarla esplicitamente:
volumes:
- /dev/shm:/dev/shm
Questo non è documentato nella guida di setup di OpenClaw. È un comportamento specifico di Docker che riguarda qualsiasi applicazione che usa Chromium, ma se non ha mai distribuito browser headless in Docker, non saprebbe di doverlo cercare.
Guasto 5: Collisioni di porte di cui nessuno avvisa
Il servizio di controllo del browser di OpenClaw gira su una porta separata dal gateway. Per impostazione predefinita, è la porta del gateway più 2. Se il suo gateway è sulla 18789, il servizio di controllo del browser dovrebbe essere sulla 18791, e il relay delle estensioni sulla 18792.
In Docker, se espone solo la porta 18789, il servizio di controllo del browser è irraggiungibile dall’esterno del container. Peggio ancora, il gateway registra «Browser control service ready» anche quando la porta 18791 non si lega mai realmente. L’issue #17584 ha tracciato il problema fino al gateway che importava il modulo sbagliato: uno che registra il messaggio «ready» senza avviare il server HTTP. Il log dice che va tutto bene. La porta è morta.
In Kubernetes, se un altro container nello stesso pod usa già la porta 9222 (la porta CDP standard), la funzione ensurePortAvailable() di OpenClaw rileva la porta come occupata e rifiuta la connessione, anche se l’occupante è esattamente l’istanza Chromium con cui OpenClaw dovrebbe comunicare.
L’issue GitHub #10994 documenta un caso in cui il setup VPS dell’utente funzionava correttamente. Poi, dopo alcune azioni automatizzate, la porta CDP si è bloccata. L’unica soluzione era trovare e terminare i processi Chromium residui che trattenevano la porta.
Perché questi guasti si moltiplicano
Ciascuno di questi cinque problemi ha una soluzione nota. Ma interagiscono tra loro. Risolve il problema snap e si scontra con l’impostazione headless predefinita. Corregge la configurazione e Chromium parte. Funziona dieci minuti, poi l’OOM killer lo abbatte. Aggiunge spazio swap, e ora il limite di memoria condivisa di Docker causa errori di rendering silenziosi. Risolve quello e scopre che la porta 18791 non è esposta.
Il ciclo di debug è questo: cercare errore, trovare soluzione parziale, applicare soluzione, incontrare errore successivo, ripetere. Un utente Discord ha descritto di aver reinstallato l’intero sistema operativo «per la 3a o 4a volta». Un altro ha riferito che il browser era «instabile» dopo quello che credeva fosse un setup completo. Un terzo ha aperto un’issue intitolata semplicemente «Browser tool consistently fails on VPS».
Il problema non è che ogni singola soluzione sia difficile. Il problema è che ce ne sono cinque, dipendono dal suo specifico sistema operativo, gestore di pacchetti, container runtime e provider VPS, e un singolo errore nella catena significa che il browser non funziona.
Va riconosciuto al team di OpenClaw che ha corretto costantemente bug correlati. Il log «ready» fuorviante, la pulizia dei renderer orfani e la gestione della porta CDP sono tutti migliorati nelle versioni recenti. Ma le correzioni upstream affrontano sintomi nel codice di OpenClaw. Non possono installare il binario Chromium corretto sul suo server, configurare la memoria condivisa di Docker o allocare abbastanza RAM per l’automazione del browser. Quello resta una sua responsabilità.
Come abbiamo rotto Chromium tre volte (e cosa abbiamo imparato)
Eseguiamo ogni agente di OpenClaw.rocks su Kubernetes con il nostro operatore open source. Il sidecar del browser ci ha richiesto settimane prima di funzionare correttamente. Abbiamo incontrato la nostra versione di ogni problema descritto sopra, più alcuni che si manifestano solo in un contesto di orchestrazione container.
L’idea era semplice: eseguire Chromium come container separato accanto a OpenClaw, connesso tramite CDP. Una riga nel Custom Resource e il browser funziona, senza complicazioni.
spec:
chromium:
enabled: true
Ecco come quella singola riga ha preso forma.
Crash 1: Utente sbagliato, filesystem in sola lettura
Il nostro primo tentativo eseguiva il container Chromium come UID 1001 con un filesystem root in sola lettura. Best practice di sicurezza. E completamente rotto. L’immagine browserless si aspetta UID 999 (blessuser), e Node.js chiama os.userInfo() all’avvio, che fallisce con ENOENT quando l’UID non ha un’entry in /etc/passwd. Anche dopo aver corretto l’UID, il filesystem in sola lettura impediva a Chrome di scrivere file temporanei. Il sidecar crashava ad ogni avvio prima di scrivere una singola riga di log. L’issue #12 racconta tutta la storia.
Crash 2: Il muro di sicurezza WebSocket
Con il container finalmente in esecuzione, Chromium era attivo, CDP rispondeva sulla porta 9222, ma OpenClaw rifiutava di usarlo. Il gateway si legava all’IP LAN del pod (necessario per il routing del Kubernetes Service), e lo strato di sicurezza di OpenClaw bloccava le connessioni ws:// in chiaro verso indirizzi non-loopback: «SECURITY ERROR: Gateway URL uses plaintext ws:// to a non-loopback address. Both credentials and chat data would be exposed to network interception.»
Un controllo di sicurezza legittimo. Ma in un pod Kubernetes, tutti i container condividono un network namespace. Il traffico tra loro non lascia mai il nodo. Non potevamo modificare il controllo di sicurezza di OpenClaw, quindi abbiamo aggiunto un sidecar reverse proxy nginx che ascolta su tutte le interfacce e inoltra al gateway su loopback. Il gateway resta sicuro. Il Service resta raggiungibile. L’issue #135 documenta il debugging.
Crash 3: Collisione sulla porta 3000
Chromium era in esecuzione. Il gateway aveva il suo proxy. Ma il browser continuava a non connettersi. L’immagine browserless usa la porta 3000 di default, che collideva con il servizio di controllo browser interno di OpenClaw. E quando siamo passati alla porta 9222, la funzione ensurePortAvailable() di OpenClaw l’ha rilevata come «in uso da qualcos’altro» e ha rifiutato la connessione, perché in un network namespace condiviso, la porta del sidecar sembra locale.
La soluzione è stata usare l’indirizzo IP del pod (tramite la Kubernetes Downward API) invece di localhost per l’URL CDP. Un indirizzo non-loopback dice a OpenClaw di usare la modalità remote/attach-only: connettersi a ciò che è già in esecuzione invece di tentare di lanciare un proprio browser. La PR #183 ha risolto il problema.
Il risultato: cinque fix, applicati automaticamente
Ciascuno di questi crash ci ha insegnato qualcosa. L’operatore attuale codifica tutto questo sapere:
Isolamento sidecar. Chromium gira nel suo container. Niente snap. Niente Playwright. Niente flag headless. L’immagine browserless gestisce tutto.
Risorse dedicate. Il sidecar ha i propri limiti di CPU e memoria (250m-1000m CPU, 512Mi-2Gi RAM di default, configurabile). Se Chromium supera il suo limite di memoria, Kubernetes riavvia solo il container del browser. Il suo agente continua a funzionare.
Memoria condivisa. L’operatore monta automaticamente un volume di 1 GB in memoria a /dev/shm. Nessuna configurazione Docker shm_size necessaria.
Routing delle porte. L’IP del pod per l’URL CDP attiva la modalità remota. OpenClaw si connette al sidecar invece di tentare di reclamare la porta. Il conflitto ensurePortAvailable() scompare.
Rilevamento anti-bot integrato. Molti siti web rilevano il Chromium headless predefinito e bloccano l’automazione. L’operatore supporta extraArgs sul sidecar Chromium, così può passare flag come --disable-blink-features=AutomationControlled e user agent personalizzati senza costruire un’immagine container personalizzata:
spec:
chromium:
enabled: true
extraArgs:
- "--disable-blink-features=AutomationControlled"
- "--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
- "--window-size=1920,1080"
Sicurezza senza compromessi. Tutte le capability Linux rimosse, escalation dei privilegi disabilitata, seccomp RuntimeDefault, UID 999 non-root. Nessun --no-sandbox come root.
Cosa significa per Lei
Se esegue OpenClaw su un VPS e il browser funziona, complimenti. Ha navigato attraverso cinque modalità di guasto distinte e ne è uscito dall’altra parte. Mantenga il suo setup. Faccia un backup della configurazione.
Se il browser non funziona, o funziona a intermittenza, o è stanco di fare debug di Chromium su un VPS da 5 dollari, consideri quanto vale il suo tempo.
OpenClaw.rocks esegue ogni agente su Kubernetes con l’operatore descritto sopra. Il sidecar del browser è preconfigurato. La memoria è allocata. Le porte sono instradate. La sicurezza è rafforzata. Non installa nulla, non configura nulla e non fa debug di nulla.
Il suo agente ottiene un browser che funziona. Ogni volta. Pronto all’uso.
I cinque fix in sintesi
Se vuole restare in self-hosting, ecco la checklist completa:
- Sostituire Chromium snap con il
.debdi Google Chrome o il binario standalone di Playwright - Attivare la modalità headless:
openclaw config set browser.headless trueebrowser.noSandbox true - Allocare 8 GB+ di RAM o aggiungere 4 GB di swap per l’automazione del browser
- Montare la memoria condivisa in Docker:
shm_size: '2gb' - Esporre tutte e tre le porte: gateway, controllo browser (gateway + 2) e relay estensioni (gateway + 3)
Oppure salti la checklist. Ottenga il suo assistente su OpenClaw.rocks e lasci a noi l’infrastruttura.