Nous Research의 Hermes Agent를 Ubuntu 리눅스 서버에 Docker 전용으로 설치하고, 같은 서버에서 이미 Docker로 돌고 있는 Mattermost에 봇으로 연결한 실제 과정을 정리한 글입니다. 공식 문서: https://hermes-agent.nousresearch.com/docs/user-guide/docker

이 글의 실제 예시 환경

  • 서버: Ubuntu (your-server), amd64, root 계정
  • Mattermost: 이미 설치 완료 (docker compose, nginx + HTTPS, 도메인 mattermost.example.com)
  • Mattermost 컨테이너: nginx_mattermost, docker-mattermost-1, docker-postgres-1
  • Mattermost 도커 네트워크 이름: mattermost (bridge)
  • Hermes 데이터 디렉터리: /root/.hermes (컨테이너의 /opt/data에 마운트)
  • Hermes compose: /root/hermes/docker-compose.yml
  • 생성한 봇: agents-bot (Display Name: Agent Bot)

0. M1 Mac 가이드와의 차이 (핵심)

이 글은 기존 Hermes Agent — M1 Mac Docker 설치(macOS)를 Ubuntu 서버용으로 옮긴 것입니다. 바뀌는 지점은 딱 3가지입니다.

항목 M1 Mac Ubuntu 서버 (이 글)
셸 / alias 파일 ~/.zshrc ~/.bashrc (root 기준)
아키텍처 arm64, 가끔 --platform 필요 amd64 네이티브, --platform 불필요
Mattermost 접근 주소 host.docker.internal:8065 http://mattermost:8065/ (도커 네트워크 공유)

⚠️ 가장 중요한 차이 — host.docker.internal이 여기선 안 됨. Ubuntu의 Mattermost가 nginx 버전으로 떠 있으면 8065 포트를 호스트에 노출하지 않습니다(호스트엔 nginx의 80/443만 열림). 8065는 mattermost 도커 네트워크 내부에서만 열려 있으므로, Hermes 컨테이너를 그 네트워크에 합류시키고 서비스 별칭 mattermost로 접속해야 합니다.


1. 사전 준비

1-1. Docker 확인

docker --version
docker compose version
# 없으면: curl -fsSL https://get.docker.com | sh

1-2. Mattermost 네트워크 · 컨테이너 확인 (연동의 전제)

docker network ls | grep mattermost
docker ps --format 'table {{.Names}}\t{{.Networks}}' | grep -i mattermost

기대 결과:

fa58cd368ffa   mattermost   bridge   local

nginx_mattermost      mattermost
docker-mattermost-1   mattermost
docker-postgres-1     mattermost
  • 네트워크 이름이 mattermost 인지 확인 (compose의 external 참조에 이 이름을 씀).
  • 앱 컨테이너 이름은 docker-mattermost-1 이지만, 같은 네트워크에서는 서비스 별칭 mattermost 로 접근 가능 → http://mattermost:8065.

2. 데이터 디렉터리 + 초기 setup 마법사 (최초 1회)

mkdir -p /root/.hermes

docker run -it --rm \
  -v /root/.hermes:/opt/data \
  nousresearch/hermes-agent setup

마법사 선택지 (이 환경 기준):

프롬프트 선택 이유
Select terminal backend Keep current (local) Hermes가 이미 컨테이너 안에서 돎 = 컨테이너 자체가 샌드박스. Docker 선택 시 Docker-in-Docker(소켓 마운트 필요)로 실패
Connect a messaging platform? Skip — set up later 이 setup 컨테이너는 mattermost 네트워크에 없어 연결 검증이 실패함. Mattermost는 뒤에서 .env로 직접 설정
  • API 키(ANTHROPIC_API_KEY 등)는 대화형으로 입력 → /root/.hermes/.env에 저장.
  • 완료 화면(✓ Setup Complete!) 뒤의 s6-rc: ... stopping 로그는 일회용 컨테이너 정상 종료 과정이며 에러가 아님.

3. Mattermost 봇 계정 + 토큰 생성 (연동 필수 선행)

Hermes가 Mattermost에 붙으려면 봇 계정과 Access Token이 필요합니다.

  1. Mattermost 관리자 로그인 → System Console → Integrations → Bot AccountsEnable Bot Account Creation = true (저장)

  2. 메인 화면 → 좌상단 메뉴 → Integrations → Bot Accounts → Add Bot Account

  3. 폼 입력:

    항목 비고
    Username 예: agents-bot 필수. 봇의 실제 핸들
    Display Name Agent Bot 선택 (표시 이름)
    Role Member 대화형 에이전트엔 충분 (System Admin 불필요)
    post:all Enabled 봇이 DM + 모든 채널에 응답 가능해짐 (필수)
    post:channels 체크 불필요 post:all이 상위 권한(DM 포함)이라 커버됨
  4. Create Bot Account → 다음 화면의 Token: ... 를 즉시 복사 (⚠️ 이 화면에서만 보임, 놓치면 재발급 필요)

post:all 인가: 사람이 봇에게 DM으로 말 걸면 봇이 DM으로 답장합니다. post:channels(공개 채널 전용)만으론 DM 응답이 막힙니다.


4. Hermes Docker Compose 작성

/root/hermes/docker-compose.yml:

services:
  hermes:
    image: nousresearch/hermes-agent:latest
    container_name: hermes
    restart: unless-stopped
    command: gateway run
    ports:
      - "8642:8642"              # OpenAI 호환 gateway API / 헬스체크
      - "127.0.0.1:9119:9119"    # 대시보드 — 외부 비공개, SSH 터널로만 접근
    volumes:
      - /root/.hermes:/opt/data  # 절대경로 사용 (compose는 ~ 확장 안 함)
    environment:
      - HERMES_DASHBOARD=1
      - HERMES_DASHBOARD_HOST=127.0.0.1
    shm_size: "1g"               # 브라우저 툴(Playwright)용
    mem_limit: 4g                # 일반 compose에서 실제 제한 걸리는 키
    cpus: 2.0
    networks:
      - mattermost               # Mattermost와 같은 네트워크에 합류

networks:
  mattermost:
    external: true               # Mattermost가 이미 만든 네트워크 재사용

핵심 포인트:

  • volumes는 절대경로 /root/.hermes — compose YAML은 셸이 아니라 ~를 확장하지 않습니다. ~/.hermes로 쓰면 ./~/.hermes 라는 엉뚱한 폴더가 생김.
  • networks.mattermost.external: true — "이 네트워크는 내가 만들지 말고 기존 mattermost를 붙여만 써라". Mattermost가 먼저 떠 있어야 함(없으면 network mattermost not found).
  • 대시보드 포트 127.0.0.1:9119:9119 — 서버 외부엔 안 열림. 접속은 SSH 터널 (섹션 8 참고).

생성 후 문법 검증:

cd /root/hermes && docker compose config

5. .env에 Mattermost 접속값 기입

/root/.hermes/.env(= 컨테이너 /opt/data/.env)를 편집:

nano /root/.hermes/.env

추가/수정:

MATTERMOST_URL=http://mattermost:8065/
MATTERMOST_TOKEN=<3번에서 복사한 봇 토큰>
MATTERMOST_ALLOWED_USERS=<허용할 username, 쉼표구분>
MATTERMOST_HOME_CHANNEL=            # 선택 (섹션 7 참고, 비워도 됨)
  • MATTERMOST_URLmattermost (호스트 아님). 내부 도커 DNS가 docker-mattermost-1로 라우팅.
  • MATTERMOST_TOKEN 은 봇 Access Token. 붙여넣을 때 공백/줄바꿈이 안 섞이도록 주의.
  • MATTERMOST_ALLOWED_USERS 는 봇과 대화 가능한 사용자 화이트리스트(보안 게이트). 값은 이메일이 아니라 username. 비워두면 버전에 따라 전체 잠금/전체 허용이 될 수 있어 본인 계정을 명시하는 게 안전.

username을 모를 때(컨테이너 안에서 인증 없이 조회):

docker exec -it docker-mattermost-1 mmctl --local user list

mmctl은 호스트가 아니라 Mattermost 컨테이너 안에 있습니다. docker exec로 컨테이너의 바이너리를 직접 호출하세요(mmctl --local은 컨테이너 내부 무인증 관리 채널).

공인 URL 대안: MATTERMOST_URL=https://mattermost.example.com 도 동작하지만(nginx 경유), 인터넷을 한 바퀴 돌고 드물게 hairpin NAT 문제가 있을 수 있어 내부 주소를 권장.


6. 기동 및 연결 검증

cd /root/hermes
docker compose up -d

6-1. 네트워크 도달

docker exec hermes bash -c \
  'cat < /dev/null > /dev/tcp/mattermost/8065 && echo OPEN || echo CLOSED'
# => OPEN 이면 컨테이너 → mattermost:8065 도달 확인

6-2. 게이트웨이 상태 파일 (판단의 근거)

docker exec hermes cat /opt/data/gateway_state.json

아래처럼 "platforms":{"mattermost":{"state":"connected",...}} 이고 error_messagenull이면 성공:

{"gateway_state":"running", ...,
 "platforms":{"mattermost":{"state":"connected","error_code":null,"error_message":null}}}

docker logs hermes | grep mattermost가 비어 보여도 실패가 아닙니다(연결 로그가 tail 밖으로 밀렸을 뿐). 판단은 로그 문구가 아니라 gateway_state.jsonstate 필드로.

6-3. 실제 대화 테스트 (진짜 검증)

  1. https://mattermost.example.com 로그인 (allowed_users에 넣은 계정)
  2. 좌측 Direct Messages → +Agent Bot(봇) 검색 → DM 열기
  3. 아무 메시지 전송 → 봇이 답하면 end-to-end 완료

응답이 없으면 실시간 로그를 띄워 두고 다시 말 걸기:

docker logs -f hermes

7. 홈 채널 설정 (선택)

홈 채널 = Hermes가 cron 작업 결과·크로스플랫폼 메시지를 능동적으로 보내는 기본 채널. 사람이 DM으로 묻는 대화는 홈 채널 없이도 동작하므로 필수는 아님.

방법 A — 슬래시 명령 (가장 간단, 즉시 반영)

홈으로 쓸 DM/채널 입력칸에서:

/sethome

→ 그 대화가 홈 채널로 지정됨(재시작 불필요). 전용 채널로 할 거면 봇을 그 채널에 먼저 초대해야 함.

방법 B — .env로 관리 (소스 오브 트루스)

MATTERMOST_HOME_CHANNEL에는 채널 ID(26자)를 넣습니다(표시 이름/@유저명 아님).

채널 ID 얻기:

# 브라우저: 채널명 클릭 → View Info → Channel ID 복사
# 또는 터미널:
docker exec docker-mattermost-1 mmctl --local team list
docker exec docker-mattermost-1 mmctl --local channel list <팀이름> --json \
  | python3 -c 'import json,sys;[print(c["name"],c["id"]) for c in json.load(sys.stdin)]'

.env 기입 후 재시작 필요(.env는 시작 시점에만 읽힘):

nano /root/.hermes/.env      # MATTERMOST_HOME_CHANNEL=<채널ID>
cd /root/hermes && docker compose restart hermes

형식이 헷갈리면: 원하는 채널에서 /sethome을 한 번 실행 → Hermes가 저장한 값을 그대로 확인해 .env에 복사하면 형식 오류가 없음.

docker exec hermes sh -c 'grep -ri home /opt/data/config.yaml /opt/data/gateway_state.json 2>/dev/null'

8. 운영 · 편의 설정

8-1. hermes CLI를 호스트에서 실행 (alias, Ubuntu는 bashrc)

Hermes를 호스트에 네이티브 설치하지 않고, docker exec를 짧은 이름으로 감싸 호스트에서 hermes 한 단어로 컨테이너 안 CLI에 접속합니다.

echo "alias hermes='docker exec -it hermes /opt/hermes/.venv/bin/hermes'" >> ~/.bashrc
source ~/.bashrc

사용:

hermes            # 대화형 CLI 접속
hermes doctor     # 진단
hermes --tui      # TUI 인터페이스
hermes config     # 현재 설정 보기

alias 구조: 첫 hermes컨테이너 이름(compose의 container_name: hermes), 뒤의 /opt/hermes/.venv/bin/hermes컨테이너 안의 실행 파일. 즉 "hermes 컨테이너에 들어가 그 안의 hermes 바이너리를 실행".

전제·주의:

  • 컨테이너가 실행 중이어야 함(docker exec 기반). 꺼져 있으면 Error: No such container: hermescd /root/hermes && docker compose up -d 먼저. 상태 확인: docker ps --filter name=hermesUp ... 이면 OK.
  • 이 CLI 세션은 게이트웨이(Mattermost 봇 서비스)와 별개의 대화 세션입니다. 게이트웨이는 봇으로 계속 돌고, hermes는 사람이 직접 대화하는 용도 — 둘 다 같은 /opt/data를 공유.
  • alias 없이 매번 풀 명령으로도 가능: docker exec -it hermes /opt/hermes/.venv/bin/hermes
  • M1 글 6·7번의 "깨진 네이티브 stub"(~/.local/bin/hermes)은 과거 install.sh 네이티브 설치를 시도한 Mac에서만 생깁니다. Docker로만 설치한 이 서버엔 해당 없음. 혹시 which hermes가 alias가 아닌 경로를 가리키면 그때만 정리.

8-2. 대시보드 접속 (외부 비공개 → SSH 터널)

대시보드는 127.0.0.1:9119에 바인딩되어 서버 외부엔 열려 있지 않습니다. 로컬 PC에서 터널을 뚫어 접속:

# 로컬 PC 터미널에서
ssh -L 9119:localhost:9119 root@<서버IP>
# 그 후 브라우저: http://localhost:9119

8-3. 로그 · 업그레이드

docker logs -f hermes                      # 실시간 로그
docker compose pull && docker compose up -d   # 업그레이드 (설정 자동 마이그레이션)

9. 데이터 영속화 구조 (/root/.hermes = 컨테이너 /opt/data)

경로 내용
.env API 키 · Mattermost 토큰/URL/설정
config.yaml 모델 등 설정
sessions/ 대화 이력
memories/ 영속 메모리
skills/ 설치된 확장 (기본 번들 72개)
cron/ 예약 작업
logs/ 런타임 로그
gateway_state.json 게이트웨이/플랫폼 연결 상태

컨테이너는 지워도 /root/.hermes만 있으면 그대로 복구됩니다.


요약 체크리스트

  • docker --version / docker compose version 확인
  • docker network ls | grep mattermost — 네트워크 이름 mattermost 확인
  • mkdir -p /root/.hermes + ... hermes-agent setup (terminal=local, messaging=skip)
  • Mattermost 봇 생성 (agents-bot, Role=Member, post:all Enabled) → 토큰 복사
  • /root/hermes/docker-compose.yml 작성 (절대경로 볼륨 + external: mattermost)
  • docker compose config 로 문법 검증
  • /root/.hermes/.envMATTERMOST_URL=http://mattermost:8065/ + 토큰 + allowed_users
  • cd /root/hermes && docker compose up -d
  • 네트워크 OPEN + gateway_state.jsonmattermost.state=connected 확인
  • Mattermost에서 봇 DM 테스트 → 응답 확인 (end-to-end)
  • (선택) 홈 채널: /sethome 또는 .envMATTERMOST_HOME_CHANNEL + restart
  • (선택) ~/.bashrc alias, 대시보드 SSH 터널

참고 링크