OpenBot AI in self-hosting su VPS: costi e container
Scopri come il gateway OpenBot controlla e registra ogni azione, con un container e un browser Chromium per bot: il costo principale è la RAM.
Cosa si ottiene eseguendo in self-hosting i collaboratori AI OpenBot
Per eseguire in self-hosting i collaboratori AI OpenBot, si utilizza un server gateway e un container per ogni bot su hardware sotto il proprio controllo. Ogni container del bot include il proprio browser Chromium e il proprio volume di workspace, con un profilo del browser persistente tra le sessioni. Ogni azione eseguita da un bot su un computer, un file, un server MCP (model context protocol) o un componente dell'interfaccia passa dal gateway. Il gateway verifica l'azione rispetto a una policy prima di eseguirla e la registra al termine.
OpenBot è pubblicato da CopilotKit con licenza MIT all'indirizzo github.com/CopilotKit/openbot. La prima release con tag, v0.0.1, è stata pubblicata il 17 agosto 2026. Il progetto si descrive come alpha e in sviluppo attivo. Va quindi considerato un progetto con un'architettura solida, ma ancora con aspetti iniziali da rifinire.
L'aspetto più interessante di questa architettura è anche quello che richiede più risorse. Un browser per ogni agent è il costo in memoria che spesso viene dimenticato in fase di pianificazione. Per questo, in questa guida si parte dal dimensionamento e si procede poi con l'installazione.
Come il gateway decide ogni azione
Il server API sulla porta 3001 è l'unico percorso verso il computer del bot. Prima di eseguire un'azione del browser, il gateway risolve l'obiettivo a partire da uno snapshot della pagina, valuta le regole di policy CEL (Common Expression Language) rispetto al contesto, scrive una riga di audit con la decisione e solo dopo chiama il container. Se l'esecuzione non riesce, scrive una seconda riga. La documentazione definisce chiaramente il confine: il computer non decide la policy; il gateway del server è il confine delle azioni.
La policy segue il principio del diniego predefinito e le regole di diniego vengono valutate prima di quelle di autorizzazione. La direzione del fallimento è più importante della sintassi delle regole. L'assenza di una policy non autorizza nulla e una regola non valida causa il blocco, sia che si tratti di una regola di diniego sia di una regola di autorizzazione. Un errore nella policy quindi lascia il bot bloccato, invece di consentirgli di operare senza controllo sui tuoi account.
Il registro di audit si trova in PostgreSQL, quindi sopravvive a un riavvio. I passaggi del controllo vengono registrati come computer.help_requested, computer.control_taken e computer.control_released. In questo modo puoi vedere quando un bot chiede l'intervento di una persona e quando la persona restituisce il controllo. I secret vengono registrati come conteggi dei caratteri, mai come valori. Le operazioni sui file registrano il percorso e la dimensione, mai il contenuto. Se vuoi lo stesso confine di controllo senza un browser a valle, vincolare le azioni degli agenti AI ad approvazioni descrive questo caso più ristretto.
Dimensionamento di RAM e disco per un singolo bot
Il progetto pubblica valori misurati per un singolo Bot su arm64. Questi sono gli unici dati di dimensionamento forniti da OpenBot e descrivono un bot su una sola architettura. Usali quindi come punto di partenza, non come piano di capacità.
The data behind this chart
[
{
"label": "Measured, one Bot",
"memory_gb": 0.55,
"disk_gb": 5.3,
"vcpu": 0.06
},
{
"label": "Documented minimum",
"memory_gb": 2,
"disk_gb": 8,
"vcpu": 1
},
{
"label": "Documented recommended",
"memory_gb": 4,
"disk_gb": 10,
"vcpu": 2
}
]Il picco di memoria misurato è di 0.55 GB per un Bot. Il minimo documentato è 2 GB, mentre la raccomandazione è di 4 GB. La differenza tra il valore misurato e il minimo lascia margine alla crescita di Chromium sotto carico, perché l'uso di memoria del browser dipende dalle pagine aperte e non dal processo inattivo. L'uso della CPU in idle è quasi nullo: raggiunge 0.06 di un core al limite superiore dell'intervallo misurato. La CPU quindi non è la risorsa principale da dimensionare. Il disco sì. La sola immagine occupa 5.3 GB, rispetto a un volume consigliato di 10 GB. Le dimensioni sono dovute al fatto che l'immagine include i binari Firefox e WebKit di Playwright oltre a Chromium.
Questi dati non indicano quanto costino insieme più bot, e il progetto non pubblica valori per questo caso. Esegui le misurazioni nel tuo ambiente. Avvia un bot, assegnagli un'attività reale con una pagina aperta e monitora il container mentre lavora.
docker stats --no-stream
free -mPrendi il valore della colonna MEM USAGE per il container del bot come dato per singolo bot. Aggiungi il gateway e PostgreSQL, quindi moltiplica il valore per singolo bot per il numero di bot che prevedi di avere contemporaneamente. Anche un bot inattivo mantiene un processo del browser. Il moltiplicatore si applica quindi ai bot presenti, non soltanto a quelli impegnati in un'attività. Il calcolo è lo stesso usato per dimensionare RAM e CPU per una VPS con un agente di coding. La parte relativa al browser è descritta in eseguire un browser headless per agenti su una VPS.
Un dettaglio di Chromium influisce sui piani di piccole dimensioni. OpenBot avvia Chromium con --disable-dev-shm-usage, quindi il browser scrive in /tmp invece che in /dev/shm. Questo evita il crash che si verifica sugli host con un /dev/shm di piccole dimensioni e trasferisce la pressione sul filesystem root. È un ulteriore motivo per cui il disco consigliato è più grande dell'immagine.
Come ospitare autonomamente OpenBot su un VPS?
Servono Docker, Bun 1.3 o versioni successive, un progetto CopilotKit Intelligence e una chiave API del modello. La documentazione per lo sviluppo richiede inoltre lsof, python3 e curl sul server. Clona una release contrassegnata invece di main, perché main in un progetto alpha cambia senza preavviso.
git clone --branch v0.0.1 https://github.com/CopilotKit/openbot.git
cd openbot
cp .env.example .envEsegui il provisioning del progetto Intelligence. Questi tre comandi scrivono la chiave di runtime e il token di licenza nel file dell'ambiente.
npx --yes copilotkit@latest login
npx --yes copilotkit@latest project select
npx --yes copilotkit@latest license --writeGenera la chiave che cifra le credenziali memorizzate e inserisci l'output in .env come KEY_ENCRYPTION_KEY. Aggiungi OPENAI_API_KEY nello stesso file oppure imposta BOT_PROVIDER su anthropic o google con la chiave corrispondente.
openssl rand -base64 32Quindi installa e avvia il servizio.
bun install
bash scripts/start.shscripts/start.sh avvia i servizi Docker, esegue le migrazioni del database, avvia il server e l'applicazione e ne verifica lo stato. Al termine, l'applicazione risponde sulla porta 3010 e l'API sulla porta 3001. Lo script segnala i conflitti di porta e lascia in esecuzione un servizio corrispondente già attivo, quindi può essere eseguito due volte senza rischi.
Verifica il funzionamento direttamente dal server prima di esporre il servizio.
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3010
ss -ltnp | grep -E ':(3010|3001|4100|4500|5432)'Un 200 restituito dal primo comando indica che l'applicazione sta rispondendo. Il secondo comando mostra a quali indirizzi sono associate le porte; su un VPS, questo è il risultato rilevante. Una riga con 127.0.0.1:3001 indica che la porta è accessibile soltanto dal server. Una riga con 0.0.0.0:3001 indica che chiunque possa instradare traffico verso il server può raggiungerla.
L’immagine container singola
La documentazione di distribuzione include anche un’unica immagine che contiene l’applicazione, l’API e Chromium, esposti sulla porta 3001.
docker build -t openbot .
docker run -p 127.0.0.1:3001:3001 --env-file .env \
-e EMBEDDED_POSTGRES=on -v openbot-data:/var/lib/postgresql/data openbotEMBEDDED_POSTGRES=on esegue PostgreSQL all’interno del container e applica le migration all’avvio. Il volume denominato conserva la cronologia degli audit tra una nuova distribuzione e l’altra; senza di esso, ogni nuova build elimina quella cronologia. Se invece si configura DATABASE_URL per usare un database gestito, è necessario abilitare su quel database l’estensione vector. Servizi gestiti come RDS, Cloud SQL e Azure Database supportano l’estensione, ma nessuno la abilita automaticamente. Di conseguenza, una migration verso un database gestito appena creato non riesce perché il tipo di colonna vector non esiste ancora.
Eseguire le migration come fase di release quando il database è esterno.
docker run --rm --env-file .env openbot \
sh -c "cd /app/server && bun x drizzle-kit migrate --config=drizzle.config.ts"Quell’immagine lascia intenzionalmente non pubblicata la porta del browser. Inoltre non include il supervisor, perché il supervisor richiede il socket Docker, che le piattaforme serverless non forniscono. Senza il supervisor, tutti i bot condividono lo stesso browser e quindi lo stesso insieme di credenziali di accesso. Questo elimina l’isolamento che giustificava l’uso di container separati per ogni bot. Se la necessità di usare credenziali separate per ogni bot è il motivo per cui si sta valutando questa soluzione, eseguire lo stack Compose con COMPUTER_SUPERVISOR_URL e SUPERVISOR_TOKEN impostati, su un host in cui si accetta questo compromesso. Un processo in grado di comunicare con il socket Docker può avviare un container privilegiato; in pratica, quindi, dispone di privilegi root sull’host. Questo è un buon motivo per mantenere OpenBot su una macchina dedicata, secondo lo stesso principio di assegnare agli agenti di programmazione una VM usa e getta.
Perché OPENBOT_SINGLE_USER è un'impostazione adatta a un laptop
.env.example viene fornito con OPENBOT_SINGLE_USER=true. Questa impostazione considera ogni richiesta come proveniente da un unico amministratore e disabilita completamente l'accesso. Su un laptop è una scelta pratica, perché l'unico client che può raggiungere la porta sei tu. Su un VPS significa che la prima persona che raggiunge la porta 3010 diventa amministratore di un sistema che memorizza credenziali cifrate e controlla un browser che ha già effettuato l'accesso ai tuoi account.
Esistono due modalità corrette per eseguirlo. Mantieni OPENBOT_SINGLE_USER=true, associa ogni porta a 127.0.0.1 e accedi all'applicazione solo tramite un tunnel SSH o un'interfaccia di rete privata.
ssh -N -L 3010:127.0.0.1:3010 -L 3001:127.0.0.1:3001 you@your-vpsL'applicazione sarà quindi disponibile all'indirizzo http://localhost:3010 nel tuo browser, che viene considerato un contesto sicuro. In questo modo funzionano sia i cookie di accesso sia le funzionalità del browser necessarie per la schermata live. L'alternativa consiste nel disabilitare la modalità a utente singolo e configurare un identity provider reale. Sono supportati Google, Microsoft Entra, Okta, SAML e OIDC. Qualsiasi provider richiede inoltre BETTER_AUTH_SECRET con una lunghezza di almeno 32 caratteri, BETTER_AUTH_URL impostato sull'URL pubblico di base dell'API per i callback OAuth, INITIAL_ADMIN_EMAILS e TRUSTED_ORIGINS. Le credenziali del provider devono essere complete, perché un provider configurato parzialmente interrompe l'avvio invece di ripiegare sull'accesso aperto.
Se l'applicazione è raggiungibile tramite un nome pubblico, configura TLS (transport layer security) davanti ad essa. Una pagina servita tramite http://, se non è ospitata su localhost, non costituisce un contesto sicuro. Di conseguenza, i cookie contrassegnati con Secure non vengono memorizzati e l'accesso non riesce in un modo che può sembrare un bug di OpenBot.
Impostare il firewall sulle porte di livello inferiore
La nota di sicurezza di OpenBot specifica che gli endpoint dei servizi di livello inferiore sono protetti da token, che devono restare privati e che non devono essere usati per aggirare il gateway. I token costituiscono la seconda protezione. La prima consiste nel rendere la porta completamente irraggiungibile.
L'agent-computer è in ascolto sulla porta 4100 e richiede COMPUTER_TOKEN. Gli endpoint del bot sono in ascolto sulle porte 4200 e 4201. Il supervisor è in ascolto sulla porta 4500 sull'host e sulla porta 4300 all'interno del relativo container. PostgreSQL è in ascolto sulla porta 5432. Nessuna di queste porte deve essere esposta su un'interfaccia pubblica. In una distribuzione per singolo utente, lo stesso vale per l'applicazione e per l'API.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status verboseQui esiste una trappola che può sfuggire a chi presume che il firewall sia sufficiente. La pubblicazione di una porta del container tramite -p 3001:3001 fa installare a Docker una regola DNAT. Il traffico viene quindi gestito nel percorso FORWARD e non attraversa mai la catena INPUT, sulla quale si applica il rifiuto predefinito di ufw. La porta rimane aperta anche se ufw status continua a mostrare Status: active. Associa la porta pubblicata all'interfaccia di loopback direttamente nella mappatura, usando -p 127.0.0.1:3001:3001, oppure imposta l'indirizzo dell'host nel file Compose. Verifica con ss -ltnp, non con ufw status.
OpenBot non è uno stack offline
Stabiliscilo prima di pianificare il deployment. OpenBot dipende da un progetto CopilotKit Intelligence, che conserva thread persistenti e memoria delle conversazioni al di fuori del tuo server. All'avvio, il server verifica INTELLIGENCE_API_URL, INTELLIGENCE_GATEWAY_WS_URL, INTELLIGENCE_API_KEY e COPILOTKIT_LICENSE_TOKEN; tutti e quattro devono essere presenti contemporaneamente, altrimenti l'avvio fallisce. Ad agosto 2026 è disponibile un piano gratuito. Intelligence può inoltre essere installato sul proprio hardware, quindi è possibile realizzare un deployment completamente locale, ma con un lavoro maggiore rispetto a quanto indicato nella guida rapida.
Il modello è la seconda dipendenza esterna. Nel pacchetto non è incluso alcun modello. BOT_PROVIDER accetta openai, anthropic o google. OPENAI_BASE_URL indirizza il percorso OpenAI verso qualsiasi endpoint compatibile. In questo modo puoi usare Ollama su un VPS per installare autonomamente un LLM se vuoi mantenere i token sul tuo hardware. Il controllo del browser richiede molto al modello, quindi prova un modello locale su un'attività reale prima di sceglierlo.
Esegui una sola replica, per ora
Il gateway memorizza le istantanee delle pagine nella memoria del processo server. Con due repliche, un’istantanea acquisita da un processo non è visibile all’altro. Di conseguenza, le operazioni falliscono in modo intermittente con errori element-not-found che sembrano casuali. La documentazione del deployment è esplicita: esegui una sola replica e imposta a 1 il numero massimo di istanze della piattaforma. Questo limite resterà valido finché la memorizzazione nella cache delle istantanee non verrà spostata nel database. Fino ad allora, per aumentare la capacità di OpenBot devi potenziare il server, non aggiungere server. L’isolamento tra i bot è comunque garantito dai container dedicati a ciascun bot, come i sandbox self-hosted per gli agenti tengono separati gli errori di un agente da quelli degli altri.
Modalità di errore e segnali osservabili
L'avvio termina subito dopo la compilazione di .env. Il server valida la configurazione prima di iniziare a servire le richieste. Un blocco Intelligence incompleto, KEY_ENCRYPTION_KEY mancante oppure un provider OAuth con un client ID ma senza secret interrompono l'avvio invece di causare un degrado silenzioso. Leggi il primo errore, correggi quel campo e riavvia.
Le migrazioni falliscono su un database gestito. L'estensione vector non è abilitata per impostazione predefinita, quindi la migrazione utilizza un tipo di colonna che PostgreSQL non riconosce. Connettiti come superuser, esegui CREATE EXTENSION vector;, quindi ripeti il passaggio di migrazione.
L'applicazione viene caricata, ma l'accesso non resta attivo. Stai pubblicando il servizio tramite http:// non cifrato su un indirizzo pubblico, che non costituisce un contesto sicuro; per questo il cookie Secure viene scartato. Configura TLS davanti all'applicazione oppure usa il tunnel SSH, in modo che il browser rilevi localhost.
I bot condividono account che dovevano essere separati. Il supervisore non è in esecuzione, quindi non esiste un computer dedicato per ogni bot e tutti i bot utilizzano lo stesso browser. Verifica che COMPUTER_SUPERVISOR_URL sia impostato e che il supervisore possa raggiungere il socket Docker.
Un bot si arresta e chiede assistenza. È il comportamento previsto. Il registro di audit registra computer.help_requested, tu prendi il controllo della schermata attiva e il passaggio di consegne viene registrato da entrambe le parti.
FAQ
È sicuro lasciare attivo OPENBOT_SINGLE_USER per una distribuzione su VPS?
Solo quando il gateway non è raggiungibile da Internet. OPENBOT_SINGLE_USER=true accetta ogni richiesta come un unico amministratore senza autenticazione, quindi chiunque possa aprire la porta prende il controllo della distribuzione, delle credenziali memorizzate e del browser autenticato. È accettabile quando tutte le porte sono associate a 127.0.0.1 e si accede all'app tramite un tunnel SSH o un'interfaccia di rete privata. Su un'interfaccia pubblica, disattivatelo e configurate Google, Microsoft Entra, Okta o OIDC insieme a BETTER_AUTH_SECRET, BETTER_AUTH_URL, INITIAL_ADMIN_EMAILS e TRUSTED_ORIGINS.
Quanta RAM richiede un singolo bot OpenBot?
I dati pubblicati dal progetto per un singolo Bot su arm64 indicano un picco di memoria di 0.55 GB, con 2 GB come minimo documentato e 4 GB consigliati. Non esiste un dato pubblicato per più bot simultanei, perché ciascuno utilizza una propria istanza di Chromium. Eseguite un bot su un'attività reale, leggete la memoria del relativo container in docker stats, aggiungete il gateway e il database, quindi moltiplicate il risultato per il numero di bot che prevedete di eseguire contemporaneamente.
Serve un account CopilotKit per ospitare autonomamente OpenBot?
Sì. OpenBot dipende da un progetto CopilotKit Intelligence per la persistenza di thread e memoria, e il server rifiuta di avviarsi se non sono impostati l'URL dell'API Intelligence, l'URL WebSocket del gateway, la chiave API e il token di licenza. Ad agosto 2026 è disponibile un piano gratuito e Intelligence può essere ospitato autonomamente, quindi la dipendenza dal servizio ospitato può essere rimossa con ulteriore lavoro. Dovete inoltre fornire la vostra chiave API del modello, perché OpenBot non include alcun modello.
Perché ogni bot dispone di un proprio browser invece di condividerne uno?
Perché un profilo del browser rappresenta un'identità. Un browser condiviso comporta cookie e sessioni condivisi, quindi se un bot ha effettuato l'accesso a un account, tutti i bot risultano autenticati con quell'account. I container separati per bot assegnano a ogni collaboratore un proprio profilo e le proprie sessioni di accesso. Il costo è una maggiore richiesta di memoria, perché un'istanza di Chromium per bot è la voce principale nel dimensionamento.
Quali porte OpenBot devono essere aperte nel firewall?
Nessuna delle porte dei livelli inferiori. Il computer dell'agente sulla porta 4100, gli endpoint dei bot sulle porte 4200 e 4201, il supervisor sulle porte 4500 e PostgreSQL sulla porta 5432 devono restare privati. Il progetto li protegge con token e richiede comunque che restino irraggiungibili. Pubblicate solo ciò che serve a un utente per aprire, e ricordate che una porta del container pubblicata con -p 3001:3001 è raggiungibile indipendentemente da una regola ufw default-deny, perché la regola DNAT di Docker inserisce il traffico nel percorso FORWARD invece che nel percorso INPUT.