OpenBot AI 동료 VPS 직접 호스팅 및 구축 가이드
OpenBot을 VPS에 직접 호스팅하여 각 AI 동료에게 컨테이너와 브라우저를 할당하는 방법을 설명합니다. 게이트웨이의 정책 결정 프로세스와 에이전트별 RAM 메모리 소모량 등 실제 구축 시 고려해야 할 기술적 비용을 상세히 분석합니다.
OpenBot AI 동료를 직접 호스팅할 때 얻는 것
OpenBot AI 동료를 직접 호스팅하려면 제어 가능한 하드웨어에서 게이트웨이 서버 1대와 봇당 컨테이너 1개를 실행해야 합니다. 각 봇 컨테이너는 자체 Chromium 브라우저와 작업 공간 볼륨을 가지며, 세션 간에 유지되는 브라우저 프로필을 사용합니다. 봇이 컴퓨터, 파일, MCP(model context protocol) 서버 또는 UI 구성 요소에서 수행하는 모든 작업은 게이트웨이를 거칩니다. 게이트웨이는 작업 수행 전 정책을 확인하고 수행 후 기록을 남깁니다.
OpenBot은 CopilotKit이 MIT 라이선스로 github.com/CopilotKit/openbot에 공개했습니다. 첫 번째 태그된 릴리스인 v0.0.1은 2026년 8월 17일에 발표되었으며, 프로젝트는 현재 알파 단계로 활발히 개발 중입니다. 초기 단계의 미흡한 부분이 있을 수 있는 진지한 설계로 간주하십시오.
이 아키텍처의 흥미로운 점은 비용이 많이 드는 부분이기도 합니다. 에이전트마다 브라우저를 할당하는 것은 많은 사람이 계획할 때 간과하는 메모리 비용이므로, 설치 전에 규모 산정이 선행되어야 합니다.
게이트웨이가 모든 동작을 결정하는 방식
포트 3001에서 실행되는 API 서버는 봇의 컴퓨터로 향하는 유일한 경로입니다. 브라우저 동작이 실행되기 전에 게이트웨이는 페이지 스냅샷에서 대상을 확인하고, 컨텍스트에 대해 CEL(Common Expression Language) 정책 규칙을 평가하며, 결정 사항을 담은 감사 행을 기록한 뒤에야 컨테이너를 호출합니다. 그 이후 실행에 실패하면 두 번째 행을 기록합니다. 문서는 이 경계를 명확히 명시합니다. 컴퓨터는 정책을 결정하지 않으며, 서버 게이트웨이가 동작의 경계가 됩니다.
정책은 기본적으로 거부(deny-by-default)이며, 거부 규칙은 허용(allow) 규칙보다 먼저 평가됩니다. 규칙 구문보다 실패 방향이 더 중요합니다. 정책이 누락되면 아무것도 허용되지 않으며, 규칙이 손상되면 거부 규칙이든 허용 규칙이든 상관없이 차단되는 방향으로 실패합니다. 따라서 정책에 실수가 있더라도 봇이 계정에 무단으로 접근하는 대신 봇이 멈추는 결과가 발생합니다.
감사 추적은 PostgreSQL에 저장되므로 재시작 후에도 유지됩니다. 제어권 이양은 computer.help_requested, computer.control_taken, computer.control_released로 기록되며, 이를 통해 봇이 사람에게 도움을 요청하는 과정과 사람이 제어권을 다시 넘겨주는 과정을 확인할 수 있습니다. 비밀 정보는 값이 아닌 문자 수로 기록됩니다. 파일 작업은 경로와 크기만 기록하며 내용은 기록하지 않습니다. 브라우저 없이 동일한 제어 경계를 원한다면 승인을 통한 AI 에이전트 동작 제어에서 더 좁은 범위의 사례를 다룹니다.
봇 한 대당 필요한 RAM 및 디스크 용량
이 프로젝트는 arm64 아키텍처에서 봇 한 대를 구동할 때 측정된 수치를 공개합니다. 이 수치는 OpenBot이 제공하는 유일한 사이징 지표이며, 특정 아키텍처의 봇 한 대를 기준으로 하므로 용량 계획을 위한 확정된 수치가 아닌 시작점으로 활용해야 합니다.
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
}
]봇 한 대의 최대 메모리 사용량은 0.55 GB로 측정되었습니다. 문서상 최소 사양은 2 GB이며 권장 사양은 4 GB입니다. 측정값과 최소 사양 사이의 간극은 부하 발생 시 Chromium이 확장될 여유 공간을 의미합니다. 브라우저의 메모리 사용량은 대기 상태의 프로세스가 아니라 열려 있는 페이지 수에 따라 변하기 때문입니다. 유휴 상태의 CPU 점유율은 측정 범위 최상단에서도 코어의 0.06 수준으로 거의 0에 가깝습니다. 즉, CPU 자원은 주요 고려 대상이 아닙니다. 핵심은 디스크입니다. 이미지 크기만 5.3 GB이며 권장 볼륨은 10 GB입니다. 이미지 용량이 큰 이유는 Playwright의 Firefox 및 WebKit 바이너리가 Chromium과 함께 포함되어 있기 때문입니다.
위 수치만으로는 여러 대의 봇을 운영할 때의 비용을 산출할 수 없으며, 프로젝트 측에서도 이에 대한 수치를 제공하지 않습니다. 직접 측정하십시오. 봇 한 대를 시작하고 실제 페이지를 여는 작업을 수행하게 한 뒤, 작업 중인 컨테이너를 모니터링하십시오.
docker stats --no-stream
free -m봇 컨테이너의 MEM USAGE 열 값을 봇 한 대당 수치로 잡고, 여기에 게이트웨이와 PostgreSQL 사용량을 더한 뒤, 동시에 운영할 봇의 수만큼 곱하십시오. 유휴 상태의 봇도 브라우저 프로세스를 유지하므로, 이 배수는 작업 중인 봇뿐만 아니라 존재하는 모든 봇에 적용됩니다. 계산 방식은 코딩 에이전트 VPS의 RAM 및 CPU 사이징에서 사용하는 방식과 동일하며, 브라우저 관련 내용은 에이전트를 위한 VPS에서 헤드리스 브라우저 실행하기에서 다룹니다.
Chromium의 세부 설정 하나가 소규모 플랜에 영향을 줍니다. OpenBot은 Chromium을 --disable-dev-shm-usage 옵션으로 실행하므로, 브라우저는 /dev/shm 대신 /tmp에 데이터를 기록합니다. 이는 /dev/shm가 작은 호스트에서 발생하는 충돌을 방지하기 위함이며, 이로 인해 루트 파일 시스템에 가해지는 부하가 커집니다. 권장 디스크 용량이 이미지 크기보다 큰 이유가 바로 여기에 있습니다.
VPS에서 OpenBot을 직접 호스팅하는 방법
Docker, Bun 1.3 이상 버전, CopilotKit Intelligence 프로젝트, 그리고 모델 API 키가 필요합니다. 개발 문서에서는 서버에 lsof, python3, curl가 설치되어 있을 것을 요구합니다. 알파 프로젝트의 main는 예고 없이 변경될 수 있으므로 main 대신 태그가 지정된 릴리스를 복제하십시오.
git clone --branch v0.0.1 https://github.com/CopilotKit/openbot.git
cd openbot
cp .env.example .envIntelligence 프로젝트를 프로비저닝합니다. 다음 세 가지 명령은 런타임 키와 라이선스 토큰을 환경 파일에 기록합니다.
npx --yes copilotkit@latest login
npx --yes copilotkit@latest project select
npx --yes copilotkit@latest license --write저장된 자격 증명을 암호화할 키를 생성하고, 그 출력값을 .env에 KEY_ENCRYPTION_KEY으로 입력합니다. 같은 파일에 OPENAI_API_KEY을 추가하거나, BOT_PROVIDER을 일치하는 키와 함께 anthropic 또는 google으로 설정하십시오.
openssl rand -base64 32그런 다음 설치하고 시작합니다.
bun install
bash scripts/start.shscripts/start.sh은 Docker 서비스를 실행하고, 데이터베이스 마이그레이션을 수행하며, 서버와 앱을 시작한 뒤 상태를 확인합니다. 작업이 완료되면 앱은 3010 포트에서, API는 3001 포트에서 응답합니다. 이 스크립트는 포트 충돌을 보고하며 이미 실행 중인 일치하는 서비스는 그대로 두므로, 두 번 실행해도 안전합니다.
외부에 노출하기 전에 서버 자체에서 먼저 확인하십시오.
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3010
ss -ltnp | grep -E ':(3010|3001|4100|4500|5432)'첫 번째 명령에서 200가 출력되면 앱이 정상적으로 서비스 중인 것입니다. 두 번째 명령은 해당 포트가 어떤 주소에 바인딩되어 있는지 보여주며, VPS에서는 이 결과가 중요합니다. 127.0.0.1:3001으로 표시된 줄은 서버 내부에서만 접근 가능합니다. 0.0.0.0:3001로 표시된 줄은 서버로 라우팅할 수 있는 누구나 접근할 수 있음을 의미합니다.
단일 컨테이너 이미지
배포 문서에서는 애플리케이션, API, Chromium을 포함하여 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는 컨테이너 내부에서 PostgreSQL을 실행하며 시작 시점에 마이그레이션을 적용합니다. 명명된 볼륨(named volume)을 사용해야 재배포 시에도 감사 기록이 유지되며, 이를 사용하지 않으면 재빌드할 때마다 기록이 삭제됩니다. 만약 DATABASE_URL을 관리형 데이터베이스로 지정한다면, 해당 데이터베이스에서 vector 확장을 활성화해야 합니다. RDS, Cloud SQL, Azure Database와 같은 관리형 서비스는 해당 확장을 지원하지만 자동으로 활성화해주지는 않으므로, 초기화된 관리형 데이터베이스에 마이그레이션을 수행하면 vector 열 유형이 존재하지 않아 실패하게 됩니다.
데이터베이스가 외부 서비스인 경우 마이그레이션을 릴리스 단계에서 실행하십시오.
docker run --rm --env-file .env openbot \
sh -c "cd /app/server && bun x drizzle-kit migrate --config=drizzle.config.ts"해당 이미지는 의도적으로 브라우저 포트를 외부에 공개하지 않습니다. 또한 슈퍼바이저(supervisor)도 포함하지 않는데, 이는 슈퍼바이저가 Docker 소켓을 필요로 하지만 서버리스 플랫폼은 이를 제공하지 않기 때문입니다. 슈퍼바이저가 없으면 모든 봇이 하나의 브라우저를 공유하게 되어 로그인 정보도 공유하게 되며, 이는 봇별로 컨테이너를 실행하는 이유인 격리성을 제거합니다. 봇별로 개별 로그인이 필요한 경우, 해당 위험을 감수할 수 있는 호스트에서 COMPUTER_SUPERVISOR_URL와 SUPERVISOR_TOKEN을 설정하여 compose 스택을 실행하십시오. Docker 소켓에 접근할 수 있는 프로세스는 권한이 부여된 컨테이너를 시작할 수 있으므로, 사실상 호스트의 root 권한을 갖게 됩니다. 이것이 바로 코딩 에이전트에게 일회용 VM을 제공하는 것과 같은 맥락에서 OpenBot을 별도의 머신에서 운영해야 하는 타당한 이유입니다.
OPENBOT_SINGLE_USER를 노트북 설정으로 사용하는 이유
.env.example은 OPENBOT_SINGLE_USER=true와 함께 배포됩니다. 이 설정은 모든 요청을 단일 관리자의 것으로 간주하며 로그인 과정을 완전히 생략합니다. 노트북 환경에서는 해당 포트에 접근할 수 있는 클라이언트가 사용자 본인뿐이므로 편리합니다. 하지만 VPS 환경에서 이 설정을 사용하면, 포트 3010에 가장 먼저 접근한 사람이 시스템의 관리자 권한을 획득하게 됩니다. 이 시스템은 암호화된 자격 증명을 저장하며, 이미 사용자의 계정으로 로그인된 브라우저를 제어할 수 있습니다.
이를 운영하는 정석적인 방법은 두 가지입니다. OPENBOT_SINGLE_USER=true을 유지하고 모든 포트를 127.0.0.1에 바인딩한 뒤, SSH 터널이나 사설 네트워크 인터페이스를 통해서만 앱에 접근하는 것입니다.
ssh -N -L 3010:127.0.0.1:3010 -L 3001:127.0.0.1:3001 you@your-vps이 경우 앱은 사용자의 브라우저에서 http://localhost:3010로 접속되며, 이는 보안 컨텍스트로 간주됩니다. 따라서 로그인 쿠키와 라이브 화면에 필요한 브라우저 기능이 모두 정상적으로 작동합니다. 다른 방법은 싱글 유저 모드를 끄고 실제 ID 공급자(Identity Provider)를 설정하는 것입니다. Google, Microsoft Entra, Okta, SAML 및 OIDC가 지원됩니다. 어떤 공급자를 사용하든 32자 이상의 BETTER_AUTH_SECRET, OAuth 콜백을 위한 공용 API 기본 URL로 설정된 BETTER_AUTH_URL, INITIAL_ADMIN_EMAILS, 그리고 TRUSTED_ORIGINS가 필요합니다. 공급자 자격 증명은 완전히 구성되어야 합니다. 설정이 불완전하면 앱은 오픈 액세스로 전환되지 않고 시작 단계에서 중단됩니다.
앱을 공용 도메인으로 접속할 수 있게 설정했다면, 그 앞에 TLS(Transport Layer Security)를 배치하십시오. localhost를 제외한 환경에서 일반 http://으로 제공되는 페이지는 보안 컨텍스트가 아닙니다. 따라서 Secure로 표시된 쿠키가 저장되지 않으며, OpenBot의 버그처럼 보이는 로그인 실패 현상이 발생합니다.
하위 레벨 포트 방화벽 설정
OpenBot의 자체 보안 공지에 따르면 하위 레벨 서비스 엔드포인트는 토큰으로 보호되며, 이를 비공개로 유지하고 게이트웨이를 우회하는 용도로 사용하지 말아야 합니다. 토큰은 두 번째 보호 수단입니다. 첫 번째 보호 수단은 포트에 아예 접근할 수 없도록 만드는 것입니다.
에이전트 컴퓨터는 4100 포트에서 대기하며 COMPUTER_TOKEN를 요구합니다. 봇 엔드포인트는 4200 및 4201 포트에서 대기합니다. 슈퍼바이저는 호스트의 4500 포트와 컨테이너 내부의 4300 포트에서 대기합니다. PostgreSQL은 5432 포트에서 대기합니다. 이 중 어느 것도 공용 인터페이스에 노출되어서는 안 되며, 단일 사용자 배포 환경에서는 애플리케이션과 API 또한 마찬가지입니다.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status verbose방화벽만으로 충분하다고 가정하는 사용자들이 빠지기 쉬운 함정이 있습니다. -p 3001:3001을 사용하여 컨테이너 포트를 게시하면 Docker가 DNAT 규칙을 설치합니다. 이로 인해 트래픽은 FORWARD 경로에서 처리되며, ufw의 기본 거부 정책이 적용되는 INPUT 체인을 거치지 않게 됩니다. ufw status이 여전히 Status: active을 출력하더라도 포트는 열려 있는 상태로 유지됩니다. 게시된 포트를 -p 127.0.0.1:3001:3001과 같이 매핑 자체에서 루프백 주소에 바인딩하거나, compose 파일에서 호스트 주소를 설정하십시오. ufw status이 아닌 ss -ltnp를 사용하여 확인하십시오.
OpenBot은 오프라인 스택이 아닙니다
배포를 계획하기 전에 이 점을 명시하십시오. OpenBot은 영구적인 스레드와 대화 메모리를 서버 외부에서 유지하는 CopilotKit Intelligence 프로젝트에 의존합니다. 서버는 시작 시 INTELLIGENCE_API_URL, INTELLIGENCE_GATEWAY_WS_URL, INTELLIGENCE_API_KEY 및 COPILOTKIT_LICENSE_TOKEN를 검증하며, 이 네 가지가 모두 존재하지 않으면 시작에 실패합니다. 2026년 8월 기준으로 무료 플랜을 이용할 수 있으며, Intelligence 자체도 자체 호스팅이 가능하므로 퀵스타트 가이드보다 더 많은 작업을 수행하면 완전히 로컬 환경에 배포할 수 있습니다.
모델은 두 번째 외부 의존성입니다. 기본적으로 포함된 모델은 없습니다. BOT_PROVIDER는 openai, anthropic 또는 google을 허용하며, OPENAI_BASE_URL는 OpenAI 경로를 호환되는 모든 엔드포인트로 지정합니다. 토큰을 자신의 하드웨어 내에 유지하고 싶다면 LLM 자체 호스팅을 위한 VPS에서의 Ollama 실행 방법을 활용할 수 있습니다. 브라우저 제어는 모델에 많은 부하를 주므로, 로컬 모델을 도입하기 전에 실제 작업 환경에서 먼저 테스트하십시오.
현재는 복제본을 하나만 실행하십시오
게이트웨이는 페이지 스냅샷을 서버 프로세스 메모리에 캐싱합니다. 복제본이 2개일 경우, 한 프로세스가 생성한 스냅샷을 다른 프로세스에서는 볼 수 없습니다. 이로 인해 element-not-found 오류가 간헐적으로 발생하며, 마치 무작위로 오류가 나타나는 것처럼 보입니다. 배포 문서에는 복제본을 하나만 실행하고 플랫폼의 최대 인스턴스 수를 1로 고정하라고 명시되어 있습니다. 스냅샷 캐싱 기능이 데이터베이스로 이전되기 전까지는 이 제한이 유지됩니다. 그때까지는 서버를 추가하는 방식이 아니라, 서버의 사양을 높이는 방식으로 OpenBot을 확장해야 합니다. 봇 간의 격리는 여전히 봇별 컨테이너를 통해 이루어지며, 이는 self-hosted agent sandboxes가 한 에이전트의 오류가 다른 에이전트에 영향을 주지 않도록 격리하는 방식과 동일합니다.
실패 유형 및 확인 사항
.env 입력 후 즉시 시작이 종료됩니다. 서버는 서비스를 시작하기 전에 설정을 검증합니다. Intelligence 블록이 불완전하거나, KEY_ENCRYPTION_KEY이 누락되었거나, OAuth 제공자의 클라이언트 ID는 있지만 비밀 키가 없는 경우, 서버는 조용히 성능을 낮추는 대신 시작을 중단합니다. 첫 번째 오류 메시지를 읽고 해당 필드를 수정한 뒤 다시 시작하십시오.
관리형 데이터베이스에서 마이그레이션이 실패합니다. vector 확장이 기본적으로 활성화되어 있지 않아, PostgreSQL이 알 수 없는 열 유형을 마이그레이션이 시도하기 때문입니다. 슈퍼유저로 접속하여 CREATE EXTENSION vector;을 실행한 다음, 마이그레이션 단계를 다시 수행하십시오.
애플리케이션은 로드되지만 로그인이 유지되지 않습니다. 공개 주소에서 일반 http://를 통해 서비스를 제공하고 있습니다. 이는 보안 컨텍스트가 아니므로 Secure 쿠키가 폐기됩니다. 앞에 TLS를 배치하거나, 브라우저가 localhost을 인식할 수 있도록 SSH 터널을 사용하십시오.
봇들이 분리되어야 할 로그인을 공유합니다. 슈퍼바이저가 실행되고 있지 않아 봇별 컴퓨터가 생성되지 않으며, 모든 봇이 공유 브라우저를 사용하고 있습니다. COMPUTER_SUPERVISOR_URL이 설정되어 있는지, 그리고 슈퍼바이저가 Docker 소켓에 접근할 수 있는지 확인하십시오.
봇이 멈추고 도움을 요청합니다. 이는 설계된 대로 작동하는 것입니다. 감사 추적(audit trail)에 computer.help_requested이 기록되며, 사용자가 라이브 화면에서 제어권을 가져오면 양쪽 모두에 인계 기록이 남습니다.
FAQ
VPS 배포 시 OPENBOT_SINGLE_USER 설정을 켜두어도 안전합니까?
게이트웨이에 인터넷으로 접근할 수 없는 경우에만 안전합니다. OPENBOT_SINGLE_USER=true는 모든 요청을 로그인 절차 없이 단일 관리자로 간주하므로, 포트에 접근할 수 있는 사람은 누구나 해당 배포 환경과 저장된 자격 증명, 로그인된 브라우저 세션을 제어할 수 있습니다. 모든 포트를 127.0.0.1에 바인딩하고 SSH 터널이나 사설 네트워크 인터페이스를 통해서만 앱에 접근한다면 허용 가능합니다. 공용 인터페이스에서는 이 설정을 끄고 Google, Microsoft Entra, Okta 또는 OIDC를 BETTER_AUTH_SECRET, BETTER_AUTH_URL, INITIAL_ADMIN_EMAILS, TRUSTED_ORIGINS와 함께 구성하십시오.
OpenBot 봇 하나당 RAM이 얼마나 필요합니까?
arm64 환경에서 봇 하나당 공식적으로 발표된 최대 메모리 사용량은 0.55 GB이며, 문서상 최소 사양은 2 GB, 권장 사양은 4 GB입니다. 각 봇은 독립적인 Chromium 인스턴스를 유지하므로 여러 봇을 동시에 실행할 때의 수치는 별도로 명시되지 않았습니다. 실제 작업에서 봇 하나를 실행한 뒤 docker stats에서 해당 컨테이너의 메모리 사용량을 확인하고, 게이트웨이와 데이터베이스 사용량을 더한 다음 예상되는 동시 실행 봇 수만큼 곱하여 계산하십시오.
OpenBot을 자체 호스팅하려면 CopilotKit 계정이 필요합니까?
네, 필요합니다. OpenBot은 지속적인 스레드와 메모리를 위해 CopilotKit Intelligence 프로젝트에 의존하며, Intelligence API URL, 게이트웨이 WebSocket URL, API 키, 라이선스 토큰이 모두 설정되지 않으면 서버가 시작되지 않습니다. 2026년 8월 기준으로 무료 플랜을 이용할 수 있으며, Intelligence를 자체 호스팅할 수도 있으므로 추가 작업을 거치면 호스팅된 의존성을 제거할 수 있습니다. 또한 OpenBot에는 기본 제공되는 모델이 없으므로 직접 모델 API 키를 제공해야 합니다.
왜 봇을 공유하지 않고 각 봇마다 브라우저를 따로 사용합니까?
브라우저 프로필이 곧 신원이기 때문입니다. 브라우저를 공유하면 쿠키와 세션도 공유되므로, 한 봇이 특정 계정에 로그인하면 모든 봇이 해당 계정에 로그인된 상태가 됩니다. 봇별로 컨테이너를 분리하면 각 작업자에게 고유한 프로필과 로그인 정보를 부여할 수 있습니다. 봇마다 Chromium을 실행하는 것은 리소스 산정 시 가장 큰 비중을 차지하므로 메모리 비용이 발생합니다.
방화벽에서 어떤 OpenBot 포트를 열어야 합니까?
낮은 번호의 포트는 모두 열지 마십시오. 4100번의 에이전트 컴퓨터, 4200번과 4201번의 봇 엔드포인트, 4500번의 슈퍼바이저, 5432번의 PostgreSQL은 모두 비공개로 유지해야 합니다. 프로젝트는 토큰을 통해 이들을 보호하지만, 애초에 외부에서 접근할 수 없도록 유지할 것을 권장합니다. 사용자가 접근해야 하는 포트만 공개하십시오. -p 3001:3001으로 게시된 컨테이너 포트는 ufw의 기본 거부 규칙과 관계없이 접근 가능하다는 점을 유의하십시오. Docker의 DNAT 규칙이 해당 트래픽을 INPUT이 아닌 FORWARD 경로로 처리하기 때문입니다.