Docker Compose로 Rocket.Chat 직접 구축하기
Docker Compose를 사용하여 VPS에 Rocket.Chat을 설치하는 방법을 설명합니다. MongoDB 레플리카 세트 설정, TLS 적용, 데이터 백업 및 OOM 킬러로 인한 컨테이너 종료 문제를 방지하는 권장 서버 사양을 상세히 안내합니다.
구축할 시스템
사용자가 직접 소유하는 사내용 비공개 채팅 서비스입니다. Docker Compose를 사용하여 VPS에서 Rocket.Chat을 실행하고 TLS로 연결을 종료하며, 모든 메시지는 백업 및 이전이 가능한 MongoDB 데이터베이스에 저장됩니다. Rocket.Chat은 채널, 다이렉트 메시지, 스레드, 파일 공유, 음성 및 영상 통화 기능을 제공하는 성숙한 오픈 소스 Slack 및 Teams 대안이며, 사용자가 직접 임대하고 제어하는 하드웨어에서 구동됩니다. 물론 이것이 유일한 선택지는 아닙니다. 아직 선택 중이라면 Mattermost, Rocket.Chat, Synapse 및 Zulip 비교 문서를 통해 RAM, 데이터베이스, 모바일 푸시, SSO, 업그레이드 등 나중에 문제가 될 수 있는 요소들을 비교해 보시기 바랍니다. 이 애플리케이션은 단일 컨테이너로 구성되어 몇 분 안에 실행됩니다. 실제로 문제가 발생하는 지점은 대부분 옆에 있는 데이터베이스이므로, 이 가이드의 상당 부분은 MongoDB, 특히 처음 접하는 사용자들이 가장 놀라는 요구 사항인 'Rocket.Chat은 독립형(standalone) MongoDB에서는 실행되지 않는다'는 점에 할애합니다. 비록 "세트"가 단일 노드라 할지라도 반드시 레플리카 세트(replica set) 구성이 필요합니다.
사전 요구 사항 및 RAM 용량 산정 방식
서버 사양을 현실적으로 결정해야 합니다. 소규모 팀을 위한 최소 사양은 2 vCPU 및 4 GB RAM입니다. Rocket.Chat의 Node.js 프로세스는 단독으로 약 1 GB에서 1.5 GB의 메모리를 점유합니다. MongoDB의 WiredTiger 캐시는 기본적으로 (전체 RAM - 1 GB)의 50% 또는 0.256 GB 중 더 큰 값을 사용합니다. 이 기본값은 mongod가 서버 자원을 독점한다고 가정합니다. 전체 RAM을 기준으로 계산하며 Node.js가 이미 사용 중인 메모리는 고려하지 않습니다. 따라서 4 GB 서버에서는 1.5 GB, 2 GB 서버에서는 약 0.5 GB를 캐시로 할당하며, 여기에 Node.js의 힙 메모리, 캐시 외의 mongod 메모리, 운영체제 점유 메모리가 추가됩니다. 2 GB VPS에서는 부팅 직후에는 두 프로세스가 공존할 수 있으나, 실제 트래픽이 발생하면 충돌이 일어납니다. MongoDB는 캐시를 늘리고 Node.js는 힙을 확장하며, 커널은 페이지 부족 상태에 빠집니다. 이때 OOM(Out-of-Memory) 킬러가 가장 큰 프로세스인 mongod을 강제 종료합니다. 컨테이너는 Killed을 출력하고 Docker가 이를 재시작하는 과정이 반복되어, 부하를 견뎌야 할 채팅 서버가 몇 분마다 중단되는 현상이 발생합니다. 2 GB 사양은 두 명 정도가 가볍게 테스트하는 용도로는 적합하지만, 팀용 서버로는 부족합니다. 4 GB에서 시작하십시오. 수십 명의 동시 접속자, 화상 통화, 증가하는 업로드 기록을 고려한다면 8 GB를 권장합니다. 서버에서 운영할 다른 서비스도 고려해야 합니다. 동일한 VPS에 Notion 스타일의 AFFiNE 워크스페이스를 추가하면 4개의 컨테이너가 메모리를 두고 경쟁하게 되므로, 해당 서비스들의 RAM은 Rocket.Chat의 할당량과는 별도로 추가 확보해야 합니다.
시작하기 전에 세 가지를 준비해야 합니다. 첫째, VPS의 공인 IP를 가리키는 A 레코드가 설정된 도메인 이름이 필요합니다. Rocket.Chat의 실시간 기능과 모바일 클라이언트는 IP 주소가 아닌 안정적인 호스트네임을 요구합니다. 둘째, 서버 방화벽과 제공업체의 네트워크 방화벽(대부분의 제어판에서 별도로 관리됨) 모두에서 80번 및 443번 포트가 열려 있어야 합니다. 셋째, root 또는 sudo 권한을 가진 최신 Ubuntu 24.04 KVM VPS가 필요합니다. 채팅 서버를 첫 번째 서비스로 운영할지 고민 중이라면, 2026년 자가 호스팅 가치 판단 가이드를 통해 장단점을 확인하십시오.
Docker 엔진 및 Compose 플러그인 설치
Ubuntu가 제공하는 docker.io 패키지나 오래된 독립형 docker-compose Python 바이너리가 아닌 Docker 공식 apt 저장소를 사용하십시오. 최신 Compose는 docker compose와 같이 하이픈 없이 공백을 사용하는 Docker 플러그인입니다. 구형 docker-compose v1은 지원이 종료되었으며 아래의 healthcheck 및 의존성 구문을 올바르게 처리하지 못합니다.
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin두 구성 요소가 모두 설치되었는지 확인하십시오:
sudo docker version
sudo docker compose versiondocker compose version 명령이 버전 정보를 출력하는지가 확인의 핵심입니다. 최신 릴리스는 Docker Compose version v5.x를 출력합니다(2026년 10월 10일 기준 최신 버전은 2026년 10월 2일에 릴리스된 v5.6.0입니다). 이전 설치 버전은 v2.x를 출력할 수도 있으며, Docker는 두 버전 모두 지원합니다. Docker는 Compose v5를 기존 v2 및 v3 compose 파일 형식과의 혼동을 피하기 위해 v5로 번호를 매긴 것일 뿐 기능적으로는 v2와 동일하다고 설명하므로, 이 가이드에서는 어느 쪽을 사용해도 무방합니다. 만약 docker: 'compose' is not a docker command 오류가 발생한다면 플러그인이 설치되지 않은 것이며, 이후 혼란스러운 오류를 겪게 될 것이므로 여기서 해결하십시오.
Compose 파일: 단일 노드 레플리카 세트로 구성된 MongoDB
이 부분에서 실수가 잦으므로 주의 깊게 읽어야 합니다. Rocket.Chat은 MongoDB change streams를 사용하여 연결된 클라이언트에게 실시간으로 새 메시지를 전달하며, change streams는 레플리카 세트에서만 사용할 수 있습니다. Rocket.Chat을 일반 독립형 mongod에 연결하면 연결은 되지만 change stream을 열지 못해 무한 재시작 루프에 빠집니다. 해결책은 복잡하지 않습니다. 일반적인 MongoDB 컨테이너를 실행하되 --replSet 옵션을 추가하여 시작한 뒤, 단일 멤버 세트로 초기화하면 됩니다.
작업 디렉터리를 생성하고 compose.yml를 작성합니다.
services:
mongodb:
image: mongo:8.0
restart: always
command: ["mongod", "--replSet", "rs0", "--bind_ip_all", "--oplogSize", "128"]
volumes:
- mongodb_data:/data/db
- mongodb_config:/data/configdb
healthcheck:
test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 10s
retries: 12
rocketchat:
image: registry.rocket.chat/rocketchat/rocket.chat:8.5.1
restart: always
depends_on:
mongodb:
condition: service_healthy
environment:
MONGO_URL: "mongodb://mongodb:27017/rocketchat?replicaSet=rs0"
MONGO_OPLOG_URL: "mongodb://mongodb:27017/local?replicaSet=rs0"
ROOT_URL: "https://chat.example.com"
PORT: "3000"
ports:
- "127.0.0.1:3000:3000"
volumes:
mongodb_data:
mongodb_config:여기서 몇 가지 설정은 의도적으로 선택되었습니다. Rocket.Chat 포트는 0.0.0.0이 아닌 127.0.0.1:3000으로 게시됩니다. 애플리케이션 자체에는 TLS가 없으므로 같은 서버에 있는 리버스 프록시만 접근해야 합니다. 모든 인터페이스에 바인딩하면 평문 로그인 페이지가 공용 인터넷에 그대로 노출됩니다. MongoDB는 호스트에 게시되지 않으며, Compose 내부 네트워크에서 MONGO_URL이 사용하는 호스트 이름인 mongodb를 통해서만 접근할 수 있습니다. MONGO_URL는 세트 이름을 명시하는 ?replicaSet=rs0를 포함합니다. MongoDB는 드라이버 간 일관된 동작을 위해 이 설정을 권장합니다. 이를 생략한다고 해서 당장 문제가 생기지는 않습니다. directConnection은 기본적으로 false을 사용하므로, Rocket.Chat이 해석할 수 있는 호스트 이름으로 세트를 초기화하기만 하면 드라이버가 레플리카 세트를 발견하고 프라이머리 노드로 작업을 전송합니다. 다음 섹션에서 이 과정을 다룹니다. MONGO_OPLOG_URL은 oplog가 위치한 local 데이터베이스를 가리킵니다. 최신 Rocket.Chat은 change streams를 선호하지만, 이 설정을 유지해도 무방하며 구형 코드 경로와의 호환성을 보장합니다. depends_on은 condition: service_healthy을 사용하므로, Compose는 MongoDB가 ping에 응답할 때까지 기다린 후 Rocket.Chat을 시작합니다. 이것이 바로 헬스 체크의 목적입니다.
두 이미지 모두 mongo:8.0과 같이 실제 버전 태그를 고정하고, 8.5.1와 같은 명시적인 Rocket.Chat 릴리스를 사용하십시오. :latest는 절대 사용하지 마십시오. 이는 관리되지 않는 docker pull을 예기치 못한, 되돌릴 수 없는 업그레이드로 변질시킵니다. 버전을 고정하기 전에 현재 안정적인 Rocket.Chat 릴리스와 지원되는 MongoDB 버전을 확인하십시오. Rocket.Chat은 릴리스마다 기계가 읽을 수 있는 정보 문서를 게시합니다. curl -s https://releases.rocket.chat/8.5.1/info | jq '{compatibleMongoVersions, lts}'은 8.5.1 버전에 대해 compatibleMongoVersions: ["8.0"]을 반환하므로, mongo:8.0만이 지원되는 엔진입니다. 또한 해당 릴리스가 지속적인 관리가 필요 없는 서버에 고정할 가치가 있는 장기 지원(LTS) 빌드인지 알려주는 lts 플래그도 포함되어 있습니다. 모든 프로젝트가 버전이 지정된 이미지를 게시하는 것은 아닙니다. 그런 경우에는 소스 코드에서 버전을 고정해야 합니다. openGym 운동 추적기 자체 호스팅의 경우, 변경되는 브랜치를 따라가는 대신 특정 git 태그를 체크아웃하여 빌드하는 방식을 의미합니다.
레플리카 세트 초기화
스택을 실행합니다:
sudo docker compose up -d레플리카 세트가 아직 존재하지 않으므로 Rocket.Chat은 즉시 충돌하고 Docker는 이를 계속 재시작합니다. 이는 예상된 동작입니다. 수동으로 한 번 생성하십시오:
sudo docker compose exec mongodb mongosh --eval 'rs.initiate({_id: "rs0", members: [{_id: 0, host: "mongodb:27017"}]})'올바른 결과는 { ok: 1 }입니다. 몇 초 안에 단일 노드가 스스로를 프라이머리로 선출합니다. 다음 명령으로 확인하십시오:
sudo docker compose exec mongodb mongosh --quiet --eval 'rs.status().members[0].stateStr'PRIMARY가 출력되어야 합니다. 이 페이지 전체에서 가장 중요한 세부 사항은 host: "mongodb:27017" 인수입니다. 멤버 목록 없이 rs.initiate()만 실행하면, MongoDB는 컨테이너의 내부 호스트 이름인 a1b2c3d4e5f6과 같은 임의의 해시 값으로 레플리카 세트를 알립니다. 자체 컨테이너에서 연결하는 Rocket.Chat은 해당 이름을 해석할 수 없으므로, MongoDB 드라이버는 DNS 확인에 실패하고 MongoServerSelectionError: getaddrinfo ENOTFOUND a1b2c3d4e5f6를 기록하며 무한 루프에 빠집니다. 항상 MONGO_URL과 일치하는 명시적인 서비스 이름을 사용하여 초기화하십시오.
첫 부팅: 서비스 시작 모니터링
세트가 프라이머리가 되면, Rocket.Chat은 다음 재시작 시 정상적으로 연결되며 초기 실행 마이그레이션을 시작합니다. 다음 로그를 확인하십시오.
sudo docker compose logs -f rocketchat기다려야 할 줄은 시작 배너입니다.
+--------------------------------------------+
SERVER RUNNING
Rocket.Chat Version: 8.5.1
NodeJS Version: 22.22.3 - x64
+--------------------------------------------+첫 부팅은 애플리케이션이 데이터베이스 마이그레이션을 수행하고 인덱스를 생성하므로 시간이 다소 소요됩니다. 문제가 있다고 판단하기 전에 1~2분 정도 기다려 주십시오. 만약 로그에 MongoServerSelectionError: Server selection timed out after 30000 ms이 ReplicaSetNoPrimary 유형의 토폴로지 설명과 함께 반복된다면, 레플리카 세트가 초기화되지 않은 것입니다. 만약 임의의 해시값과 함께 getaddrinfo ENOTFOUND이 반복된다면, 잘못된 호스트로 초기화된 것입니다. 어느 경우든 이전 단계로 돌아가십시오. SERVER RUNNING가 표시되면 Rocket.Chat이 127.0.0.1:3000에서 리스닝 상태가 된 것이며, 이제 실제 호스트 이름을 설정하고 TLS를 적용할 차례입니다.
TLS 적용하기
Rocket.Chat을 평문 HTTP 상태로 노출하지 마십시오. http://를 통해 한 번만 로그인해도 관리자 비밀번호가 경로상의 모든 이에게 노출됩니다. 동일한 서버 내의 리버스 프록시에서 TLS를 종료하고 127.0.0.1:3000로 전달하십시오. 두 가지가 중요합니다. 첫째, Rocket.Chat은 실시간 서비스이므로 WebSocket 업그레이드 헤더를 프록시가 전달하지 않으면 오작동합니다. 둘째, 컨테이너의 ROOT_URL 설정이 사용자가 입력하는 공개 HTTPS 주소와 정확히 일치해야 합니다.
먼저 애플리케이션으로 프록시를 수행하고 업그레이드 헤더를 전달하는 평문 HTTP nginx 서버 블록을 작성하십시오. 이를 /etc/nginx/sites-available/rocketchat로 저장하고 sites-enabled에 심볼릭 링크를 생성한 뒤 리로드하십시오:
server {
listen 80;
server_name chat.example.com;
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}현재는 80번 포트로 유지하십시오. listen 443 ssl; 설정이 없고 인증서가 없는 블록은 sudo nginx -t를 통과하지 못합니다. nginx를 리로드(sudo nginx -t && sudo systemctl reload nginx)한 다음 인증서를 발급받으십시오. Ubuntu에서 가장 깔끔한 방법은 Certbot과 nginx를 이용한 Let's Encrypt TLS 인증서 발급을 따르는 것입니다. certbot --nginx는 위 블록을 제자리에서 수정하여 listen 443 ssl;, ssl_certificate 라인, 그리고 80에서 443으로의 자동 리다이렉트를 추가하며 갱신 작업까지 예약해 줍니다. 이미 하나의 프록시 뒤에서 여러 컨테이너를 운영 중이라면 여러 Docker 앱을 위한 Traefik 자동 TLS 설정이 더 깔끔한 선택입니다. rocketchat 서비스에 라우터와 서비스 레이블을 추가하면 Traefik이 nginx 블록 없이도 인증서를 요청하고 갱신합니다. 어떤 방식을 선택하든 compose.yml에서 ROOT_URL을 https://chat.example.com로 설정하고 sudo docker compose up -d을 다시 실행하여 컨테이너가 변경 사항을 반영하도록 하십시오. 서버를 공용 인터넷이 아닌 내부 네트워크에서만 접근 가능하게 하려면 VPS에 직접 호스팅하는 WireGuard VPN을 앞단에 두고 프록시를 터널 주소에 바인딩하십시오.
최초 실행 설정 마법사
https://chat.example.com에 접속하면 Rocket.Chat이 간단한 설정 마법사를 안내합니다. 첫 번째는 관리자 계정으로, 실명, 사용자 이름, 이메일, 강력한 암호를 입력합니다. 이 계정이 유일한 계정이므로 분실하지 않도록 주의하십시오. 다음은 조직 및 서버 정보로, 이름, 산업 분야, 규모, 사이트 이름, 기본 언어를 입력합니다. 이는 부가적인 정보이므로 적절히 입력하고 다음으로 넘어갑니다. 그 후 가장 중요한 선택 단계가 나옵니다. Rocket.Chat Cloud에 워크스페이스를 등록할지, 아니면 독립형(standalone)으로 유지할지 결정해야 합니다.
등록을 선택하면 Rocket.Chat 게이트웨이를 통한 모바일 푸시 알림과 애드온 마켓플레이스를 사용할 수 있지만, Rocket.Chat 클라우드와 제어 평면 연결이 생성됩니다. 또한 게이트웨이를 통해 발송되는 푸시 알림은 워크스페이스의 월간 제한량에 포함됩니다. 독립형을 선택하면 서버를 완전히 비공개로 유지하고 외부 의존성을 없앨 수 있습니다. 하지만 공식 iOS 및 Android 앱에서는 푸시 알림을 받을 수 없게 됩니다. Rocket.Chat 문서에 따르면 해당 앱들은 클라우드 게이트웨이와만 연동되며, 게이트웨이는 등록되지 않은 워크스페이스에서는 실행되지 않기 때문입니다. 이를 해결하려면 모바일 앱을 직접 화이트 라벨링하여 빌드해야 합니다. 푸시 설정에서 게이트웨이를 끄고, 자체 APNs 인증서와 Firebase Cloud Messaging 자격 증명을 입력한 뒤, 사용자가 공식 앱 대신 직접 빌드한 앱을 설치하게 하면 됩니다. 개인정보 보호가 최우선이고 사용자가 웹 앱을 주로 사용하거나, 직접 모바일 앱을 배포할 준비가 되어 있다면 독립형을 선택하십시오. 공식 앱에서의 푸시 알림이 필수적이라면 등록을 선택하십시오. 이 설정은 나중에 관리자(Admin) 메뉴에서 변경할 수 있습니다.
사용자를 초대하기 전에 보안을 강화하십시오
Rocket.Chat은 기본적으로 open registration이 활성화된 상태로 배포됩니다. 기본적으로 등록 양식(Registration Form)은 Public로 설정되어 있어, URL을 아는 사람이라면 누구나 계정을 생성할 수 있습니다. 공개 호스트네임에서는 이는 열린 문과 다름없습니다. Admin → Settings → Accounts → Registration으로 이동하여 Registration Form을 Disabled으로 설정하십시오. 이렇게 하면 관리자가 직접 계정을 생성하거나 초대 링크를 통해서만 가입할 수 있으며, Secret URL로 설정할 수도 있습니다. 해당 메뉴에 있는 동안, 특별히 공개 읽기 전용 채널이 필요한 경우가 아니라면 Allow Anonymous Read와 Allow Anonymous Write를 끄십시오. 모든 계정을 직접 생성하는 것이 번거롭고 팀이 사용하는 서비스가 Rocket.Chat 하나뿐이 아니라면, Rocket.Chat의 OAuth 로그인을 자체 호스팅된 Authentik SSO 서버로 연결하십시오. 이렇게 하면 서비스마다 일일이 관리할 필요 없이 한 곳에서 가입자와 탈퇴자를 관리할 수 있습니다.
파일 업로드 저장 위치도 결정해야 합니다. 기본 File Upload 저장소는 GridFS이며, 이는 모든 이미지와 첨부 파일을 MongoDB 내부에 저장합니다. 설정은 간단하지만, 사용자가 스크린샷을 붙여넣을 때마다 데이터베이스와 모든 mongodump의 크기가 제한 없이 커진다는 의미입니다. Admin → Settings → File Upload에서 저장소를 로컬 파일 시스템이나 S3 호환 버킷으로 변경하고 적절한 최대 파일 크기를 설정할 수 있습니다. 팀이 가끔 스크린샷을 공유하는 수준을 넘어 전체 사진 라이브러리를 교환한다면, 이는 채팅 데이터베이스가 아닌 전용 사진 서버에 저장해야 합니다. PhotoPrism과 Immich 비교 문서를 통해 각 서비스가 사용하는 RAM 용량과 필요한 백업 명령을 확인하십시오. 소규모 팀이라면 GridFS를 사용해도 괜찮지만, 시간이 지날수록 백업 데이터가 무거워진다는 점을 인지해야 합니다.
mongodump을 이용한 백업
모든 데이터는 mongodb_data 볼륨에 저장됩니다. 실행 중인 데이터베이스의 볼륨을 단순히 복사하지 말고, mongodump을 사용하여 일관된 덤프를 생성한 뒤 호스트의 파일로 스트리밍하십시오.
sudo docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > rocketchat-$(date +%F).archive.gz이 단일 gzipped 아카이브가 사용자, 채널, 메시지, 설정 등 전체 작업 공간을 포함합니다. GridFS에 업로드를 그대로 두었다면 파일까지 포함됩니다. 업로드를 파일 시스템이나 S3로 옮겼다면 해당 저장소는 별도로 백업해야 합니다. 새로운 스택에 복원하려면 먼저 레플리카 세트를 초기화한 후 다음 명령을 실행하십시오.
sudo docker compose exec -T mongodb mongorestore --archive --gzip --drop < rocketchat-2026-07-15.archive.gz아카이브를 서버 외부인 오브젝트 스토리지, 다른 서버 등 VPS 장애 시 함께 소실되지 않을 곳으로 복사하고, cron을 통해 매일 밤 덤프를 실행하십시오. 복원해 본 적 없는 백업은 백업이 아니라 희망 사항일 뿐입니다. 실제 상황이 닥치기 전에 일회용 VPS에서 복원 과정을 연습하여 정상 작동 여부를 확인하십시오.
업그레이드: 태그 고정, 릴리스 노트 확인, MongoDB 매트릭스 준수
업그레이드를 지루하게 만드는 두 가지 규칙이 있습니다. 첫째, Rocket.Chat은 한 번에 한 메이저 버전씩 업그레이드하십시오. Rocket.Chat은 부팅 시 스키마 마이그레이션을 실행하며, 업그레이드 문서에서는 메이저 버전을 건너뛰면 심각한 호환성 문제가 발생할 수 있다고 경고합니다. 소프트웨어가 이를 막아줄 것이라고 기대하지 마십시오. 6.x에서 8.x로 바로 건너뛰는 것을 안전하게 거부한다는 보장은 없으며, 마이그레이션이 중간에 실패하면 잠금 상태가 유지되어 수동 복구가 필요합니다. 이미지 태그를 다음 메이저 버전의 최신 릴리스로 변경하고, 해당 릴리스의 주요 변경 사항을 확인한 뒤 docker compose up -d을 실행하십시오. 로그에서 마이그레이션이 완료되는 것을 확인한 후 다음 단계로 넘어가십시오. 둘째, MongoDB 지원 매트릭스를 준수하십시오. 각 Rocket.Chat 릴리스는 특정 버전의 MongoDB만 지원하며, curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions에서 지원 버전을 확인할 수 있습니다. MongoDB를 7.0에서 8.0으로 업그레이드하는 경우와 같이 메이저 버전을 올릴 때는 한 단계씩 진행하고, 각 단계마다 기능 호환성 버전(feature-compatibility version)을 설정하십시오. MongoDB 8.0에서는 해당 명령 실행 시 confirm: true 옵션이 필수입니다. 이를 생략하면 확인 플래그를 포함하여 다시 실행하라는 메시지와 함께 거부됩니다.
sudo docker compose exec mongodb mongosh --eval 'db.adminCommand({setFeatureCompatibilityVersion: "8.0", confirm: true})'두 구성 요소 중 어느 것을 업그레이드하든 그전에 반드시 mongodump를 수행하십시오. 이것이 유일한 보험 정책입니다.
실패 모드 및 정확한 문자열
Rocket.Chat이 docker compose up 직후 재시작 루프에 빠지고 docker compose logs rocketchat에 MongoServerSelectionError가 가득 찹니다. MongoDB는 실행 중이지만 드라이버가 프라이머리 노드를 선택하지 못하는 상태이며, 정확한 문자열을 통해 어떤 실수를 했는지 알 수 있습니다. 토폴로지 유형이 ReplicaSetNoPrimary인 Server selection timed out after 30000 ms은 rs.initiate()를 실행하지 않아 세트에 아직 구성이 없음을 의미합니다. 무작위 해시가 뒤따르는 getaddrinfo ENOTFOUND은 명시적인 host: "mongodb:27017" 없이 초기화했기 때문에 MongoDB가 해결할 수 없는 컨테이너 호스트 이름을 알렸음을 의미합니다. sudo docker compose exec mongodb mongosh --eval 'rs.status()'로 진단하십시오. MongoServerError: no replset config has been received 오류가 발생하면 세트를 초기화하고, name이 무작위 해시인 멤버가 표시되면 서비스 이름을 사용하여 다시 초기화하십시오.
웹 UI는 로드되지만 로그인이 무한 로딩되며 완료되지 않습니다. 브라우저 콘솔을 열면 WebSocket connection to 'wss://chat.example.com/websocket' failed이 표시됩니다. 이는 거의 항상 ROOT_URL 불일치이거나 업그레이드 헤더를 전달하지 않는 프록시 때문입니다. ROOT_URL이 https://를 포함한 정확한 공용 주소와 일치하는지, 그리고 nginx location 블록이 proxy_http_version 1.1을 사용하여 Upgrade 및 Connection "upgrade"을 설정했는지 확인하십시오. 둘 중 하나를 변경한 후 docker compose up -d를 다시 실행하십시오.
컨테이너가 계속 죽고 docker compose ps에서 Restarting 상태를 보입니다. docker compose logs가 중간에 끊기고 sudo dmesg | tail에 oom-killer로부터의 Out of memory: Killed process 12345 (mongod)가 표시됩니다. 종료 코드는 137입니다. 서버의 RAM이 부족한 상태입니다. 근본적인 해결책은 최소 4 GB 이상의 더 큰 VPS를 사용하는 것입니다. 임시 방편으로 스왑을 추가하고 command 내 --wiredTigerCacheSizeGB를 사용하여 MongoDB 캐시를 제한하십시오. 기본값보다 낮게 설정해야 합니다. 예를 들어 4 GB 서버에서는 1(기본값 1.5), 2 GB 서버에서는 0.3(기본값 약 0.5)으로 설정하십시오. 하지만 스왑은 실제 부하가 걸릴 때 OOM 발생을 지연시킬 뿐입니다.
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfiledocker compose up가 Error response from daemon: driver failed programming external connectivity ... bind: address already in use 오류로 실패합니다. 이미 다른 프로세스가 3000번 포트를 점유하고 있습니다. 보통 정상적으로 종료되지 않은 이전 Rocket.Chat 컨테이너나 다른 애플리케이션이 원인입니다. sudo ss -ltnp | grep :3000로 해당 프로세스를 찾아 중지하거나 컨테이너를 중지하십시오. 또는 매핑의 호스트 측 포트를 127.0.0.1:3001:3000로 변경하고 프록시의 proxy_pass 설정을 그에 맞춰 업데이트하십시오.
FAQ
Rocket.Chat은 정말로 MongoDB replica set이 필요한가요?
네, 데이터베이스 노드가 하나뿐인 단일 서버 환경에서도 필요합니다. Rocket.Chat은 MongoDB change streams를 사용하여 메시지를 실시간으로 전달하는데, change streams는 replica set에서만 지원하는 기능이므로 standalone mongod로는 이를 열 수 없습니다. 여러 대의 장비가 필요한 것은 아니며, --replSet rs0로 MongoDB 컨테이너를 시작한 뒤 rs.initiate()을 사용하여 단일 멤버로 구성된 세트를 초기화하면 됩니다. 이 단계를 건너뛰면 드라이버가 primary를 찾지 못해 Rocket.Chat이 MongoServerSelectionError: Server selection timed out 오류를 내며 재시작을 반복하고 부팅을 완료하지 못합니다.
자체 호스팅 Rocket.Chat에는 어느 정도의 RAM이 필요한가요?
실질적인 최소 사양으로 4 GB를, 활발하게 사용하는 팀이라면 8 GB를 계획하십시오. Rocket.Chat의 Node 프로세스는 약 1에서 1.5 GB를 사용하며, MongoDB의 기본 WiredTiger 캐시는 (전체 RAM - 1 GB)의 절반을 차지하도록 설정되어 있는데, 이는 시스템에 다른 프로그램이 없다고 가정할 때의 크기입니다. 2 GB 서버에서는 이 캐시와 Node의 힙, 그리고 mongod 자체의 오버헤드가 충돌하며, 실제 부하가 발생하면 out-of-memory killer가 mongod를 종료시키고 로그에 Killed와 종료 코드 137을 남깁니다. 2 GB는 테스트 사용자 몇 명으로 소프트웨어를 평가할 때만 충분합니다.
Rocket.Chat을 HTTPS 뒤에 두려면 어떻게 해야 하나요?
TLS를 종료하고 127.0.0.1:3000로 전달하는 리버스 프록시를 동일한 VPS에서 실행하고, 컨테이너의 ROOT_URL를 공개 https:// 주소로 설정하십시오. 프록시는 WebSocket 업그레이드 헤더를 전달해야 하며, 그렇지 않으면 로그인이 진행되지 않습니다. nginx와 Certbot을 사용하는 것이 단일 애플리케이션 구성으로는 가장 간단하며, 여러 컨테이너를 하나의 프록시 뒤에서 운영하고 인증서 관리를 자동화하려면 Traefik이 더 깔끔합니다.
자체 호스팅 Rocket.Chat은 어떻게 백업하나요?
볼륨을 그대로 복사하는 대신 mongodump를 사용하여 일관된 데이터베이스 덤프를 생성하십시오: docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > backup.archive.gz. 해당 아카이브에는 사용자, 채널, 메시지, 설정 정보가 포함되며, 스토리지를 GridFS로 설정했다면 업로드된 파일도 포함됩니다. 이 파일을 서버 외부로 복사하고 cron을 사용하여 매일 밤 자동화하십시오. 또한 복구 작업이 실제로 작동하는지 확인하기 위해 테스트용 서버에서 mongorestore을 연습해 보아야 합니다.
MongoDB를 손상시키지 않고 Rocket.Chat을 업그레이드하려면 어떻게 해야 하나요?
Rocket.Chat은 부팅 시 마이그레이션을 수행하므로 한 번에 하나의 메이저 버전씩 업그레이드하십시오. 메이저 버전을 건너뛰면 심각한 호환성 문제가 발생할 수 있다는 문서의 경고를 유념하고, 고정된 이미지 태그를 변경하기 전에 각 릴리스 노트를 읽어보십시오. 대상 릴리스가 지원하는 MongoDB 버전을 curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions로 확인하고, MongoDB를 이동할 때는 한 번에 하나의 메이저 버전씩 단계를 밟으며 각 단계마다 confirm: true를 사용하여 setFeatureCompatibilityVersion을 설정하십시오. 항상 먼저 mongodump을 수행하십시오.