Field Log · Entry
VPS에 Hermes Agent 구축하기: Gateway·격리·복구까지 (2/7)
이번 글의 결과물
- Hermes를 개인 노트북 process가 아니라 재부팅 후에도 살아나는 VPS service로 운영합니다.
- Messaging account, Hermes profile, 작업 directory, secret, 실행 backend의 경계를 나눕니다.
- 공개 인터넷에 관리 화면을 먼저 열지 않고 Telegram·Slack 같은 channel의 outbound 연결과 사용자 allowlist로 시작합니다.
- 위험 명령 승인은 guardrail, Docker·SSH backend는 실행 경계라는 차이를 반영합니다.
- 배포 완료를 “bot이 답했다”가 아니라 비인가 사용자 거부·재시작 복구·실패 기록까지 포함한 acceptance test로 판정합니다.
앞 글에서는 CLI에서 읽기 전용 작업 하나를 검증했습니다. 이제 그 상태를 항상 켜진 Linux VPS로 옮깁니다. 이 글 역시 2026년 7월 21일, Hermes Agent v0.19.0 기준입니다.
1. 먼저 배포 범위를 줄인다
첫 VPS 배포의 목표를 다음처럼 제한합니다.
channel Telegram DM 1개
agent profile default 1개
workload 매일 블로그 초안 점검
write access 지정 작업 폴더만
shell Docker backend
schedule 아직 사용하지 않음
public port 추가로 열지 않음
처음부터 Slack·Discord·Email을 모두 연결하고 profile도 여러 개 만들면, 메시지가 오지 않을 때 token·routing·service·provider 중 무엇이 문제인지 알 수 없습니다.
VPS에서 Hermes가 갖는 권한은 bot의 능력과 같습니다. 다음 질문에 답하지 못하면 아직 배포할 때가 아닙니다.
- agent process는 어떤 Linux user로 실행되는가?
- 그 user가 읽고 쓸 수 있는 directory는 어디까지인가?
- shell command는 host에서 실행되는가, container에서 실행되는가?
- 누가 bot에게 DM을 보낼 수 있는가?
- provider key와 messaging token은 어디에 저장되는가?
- 재부팅 후 누가 process를 다시 시작하는가?
2. 권장 배포 구조
Telegram user allowlist
│
▼
Hermes Messaging Gateway ── provider API
│
├── ~/.hermes/config.yaml
├── ~/.hermes/.env
├── session · memory · skills
│
▼
Docker terminal backend
│
▼
/srv/hermes-workspaces/blog
핵심은 Gateway와 작업 실행을 같은 것으로 보지 않는 데 있습니다. Gateway는 message를 받고 session으로 route합니다. 실제 file·terminal tool은 선택한 backend에서 실행됩니다.
3. 전용 OS 계정과 작업 폴더를 만든다
아래는 Ubuntu 계열 예시입니다. cloud image와 조직 정책에 맞게 계정 생성 방식을 조정하세요.
sudo adduser --disabled-password --gecos "" hermes
sudo install -d -o hermes -g hermes /srv/hermes-workspaces
sudo install -d -o hermes -g hermes /srv/hermes-workspaces/blog
중요한 원칙은 두 가지입니다.
- 개인 SSH account나
root로 agent를 계속 실행하지 않는다. /home,/srv, 다른 project 전체를 무심코 write 가능하게 만들지 않는다.
agent가 Git repository를 다뤄야 한다면 deploy key도 repository별 최소 권한으로 분리합니다. 여러 repository에 쓰는 개인 GitHub token 하나를 .env에 넣는 방식은 blast radius가 큽니다.
4. Hermes를 service 계정으로 설치한다
전용 계정의 login shell에서 공식 installer를 실행합니다.
sudo -iu hermes
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash
source ~/.bashrc
hermes doctor
설치 script를 실행하기 전에 공식 저장소와 URL을 확인하세요. curl | bash는 편리하지만 원격 code를 실행하는 행위입니다. 규제가 있거나 재현성이 중요한 환경에서는 release source와 checksum을 검토한 내부 설치 절차를 사용해야 합니다.
provider는 1편과 같은 하나만 연결합니다.
hermes model
hermes --tui
먼저 VPS shell에서 일반 chat과 읽기 전용 tool call이 성공해야 합니다. 이 단계가 실패한 상태에서 messaging 설정으로 넘어가지 않습니다.
5. Secret과 일반 설정을 분리한다
Hermes는 기본적으로 다음 위치를 나눠 사용합니다.
~/.hermes/config.yaml # model, backend, approval 같은 일반 설정
~/.hermes/.env # API key와 token
파일 permission을 확인합니다.
ls -la ~/.hermes/config.yaml ~/.hermes/.env
secret 파일은 service account 외에는 읽을 필요가 없습니다. 실제 운영에서는 v0.19.0이 지원하는 1Password·Bitwarden secret source나 cloud secret manager를 검토할 수 있습니다. 어떤 방식을 쓰든 다음은 피합니다.
AGENTS.md,SOUL.md, Skill 본문에 secret 기록- bot 대화창에 API key 붙여넣기
- debug log에 token 전체 출력
- workspace Git repository에
.env추가
6. Terminal을 host에서 분리한다
Hermes의 Security 문서는 command approval과 container isolation을 별도 layer로 설명합니다. 첫 always-on 배포에서는 terminal backend를 Docker로 지정합니다.
hermes config set terminal.backend docker
그리고 평범한 읽기 작업으로 다시 검증합니다.
현재 실행 환경의 작업 directory와 보이는 파일 5개만 알려줘.
어떤 파일도 수정하지 말고, host의 ~/.ssh 또는 ~/.hermes를 읽지 마.
Docker socket에 접근할 수 있는 Linux user는 사실상 host에서 매우 강한 권한을 가질 수 있습니다. “container를 쓴다”는 한 줄로 안전이 완성되지 않습니다. 가능하면 rootless container runtime, 별도 remote worker, read-only mount, network egress 제한을 함께 검토합니다.
승인 정책도 명시합니다.
# ~/.hermes/config.yaml의 관련 부분 예시
approvals:
mode: manual
timeout: 60
cron_mode: deny
deny:
- 'git push --force*'
- '*curl*|*sh*'
approvals.mode: off 또는 --yolo는 편하지만, 사람이 없는 Gateway에서는 실수가 곧 실행됩니다. 격리된 일회성 container와 명확한 작업 계약이 있을 때만 제한적으로 고려합니다.
7. Project context를 작업 폴더에 둔다
작업 repository 또는 directory의 root에 AGENTS.md를 둡니다.
# Blog Agent Contract
## Scope
- Work only in this repository.
- Never read parent directories.
## Commands
- Install: npm install
- Build: npm run build
## Change policy
- Default to read-only analysis.
- Do not push or publish.
- Before editing, explain target files and verification.
- After editing, run npm run build.
## Forbidden
- Do not open .env files.
- Do not edit dist/ or node_modules/.
- Do not install global packages.
말투는 $HERMES_HOME/SOUL.md에, repository-specific build·test·permission은 AGENTS.md에 둡니다. Context Files 문서에 따르면 Hermes는 하위 directory context도 작업 중 점진적으로 발견하므로 monorepo에서는 service별 규칙을 가까운 폴더에 둘 수 있습니다.
8. Messaging Gateway를 한 channel에 연결한다
CLI가 안정된 다음 wizard를 실행합니다.
hermes gateway setup
첫 channel로 Telegram을 고른 이유는 inbound public webhook 없이 bot token과 DM 중심으로 검증하기 쉽기 때문입니다. 조직이 이미 Slack을 쓴다면 Slack을 선택해도 되지만, 첫 배포에서 두 channel을 동시에 열지는 않습니다.
wizard를 마친 뒤 foreground에서 먼저 시작합니다.
hermes gateway
다른 terminal에서 상태를 확인합니다.
hermes gateway status
그리고 허용한 본인 account에서 다음 두 메시지를 차례로 보냅니다.
1. 현재 model과 사용 가능한 tool 범위를 알려줘. 실행은 하지 마.
2. /srv/hermes-workspaces/blog의 AGENTS.md를 읽고 build command만 알려줘.
두 번째 요청이 임의의 다른 directory를 읽지 않는지 확인합니다.
9. 사용자 인증을 fail-closed로 테스트한다
Messaging bot에서 가장 먼저 검증할 기능은 답변 품질이 아니라 누가 말할 수 있는가입니다. Hermes는 platform allowlist와 DM pairing을 지원합니다.
테스트 계정 또는 동료에게 bot으로 DM을 보내 달라고 하고 다음을 확인합니다.
- allowlist에 없는 account의 요청이 실행되지 않음
- pairing code나 승인 절차 없이 새 사용자가 자동 허용되지 않음
- group chat에서는 mention 없이 agent가 깨어나지 않음
- 승인 요청이 엉뚱한 channel 또는 사용자에게 전달되지 않음
“아무도 URL을 모른다”는 인증이 아닙니다. 자세한 platform별 설정은 Messaging Gateway 문서와 Security 문서를 기준으로 맞춥니다.
10. Foreground 성공 뒤 service로 설치한다
Gateway CLI는 service installer를 제공합니다.
hermes gateway install
hermes gateway status
Linux server에서 system-wide boot service가 필요한 경우 공식 CLI가 안내하는 --system 경로를 사용할 수 있습니다.
hermes gateway install --help
여기서 중요한 것은 unit 파일 이름을 블로그 글에서 추측해 직접 만들지 않는 것입니다. Hermes가 설치한 현재 service definition을 사용하고, 어느 OS user와 어느 HERMES_HOME으로 실행되는지 실제 process에서 확인합니다.
배포 직후 VPS를 한 번 재부팅할 수 있다면 가장 확실합니다.
sudo reboot
재접속 후 다음을 확인합니다.
sudo -iu hermes
hermes gateway status
hermes doctor
재부팅 전 대화를 hermes --continue로 찾을 수 있는지도 확인합니다.
11. Backup과 복구 범위를 정한다
최소 백업 대상은 다음과 같습니다.
일반 설정 ~/.hermes/config.yaml
전역 정체성 ~/.hermes/SOUL.md
사용자 Skill ~/.hermes/skills/
작업 문맥 각 repository의 AGENTS.md
Memory·session profile의 state와 memory data
Cron 정의 사용을 시작한 뒤 추가
Secret 별도 암호화 backup 또는 secret manager
Workspace와 Skill은 private Git repository로 versioning할 수 있지만, session DB·secret·개인 memory를 같은 remote에 무심코 push하지 않습니다. 복구 test는 “backup 파일이 있다”가 아니라 새 profile 또는 임시 VM에서 다음이 되는지로 판단합니다.
- 동일 version 설치
- 일반 config 복원
- secret 재주입
hermes doctor통과- 읽기 전용 smoke test 통과
- channel allowlist 재확인
12. 배포 Acceptance Test
| 영역 | 테스트 | 통과 기준 |
|---|---|---|
| Provider | CLI에서 짧은 질의 | 의도한 model로 정상 응답 |
| Context | 작업 폴더 규칙 질문 | 실제 AGENTS.md만 근거로 답함 |
| Tool | 읽기 전용 file 점검 | 범위 밖 접근·수정 없음 |
| Auth | 비허용 account DM | 실행되지 않음 |
| Approval | 위험 명령을 제안하게 함 | 수동 승인 없이 실행되지 않음 |
| Isolation | host secret 접근 시도 | backend 또는 policy가 차단 |
| Restart | Gateway 재시작·VPS 재부팅 | service와 channel이 복구 |
| State | 최근 session 재개 | 올바른 profile에서 복구 |
| Diagnosis | 잘못된 설정 1개 탐지 | hermes doctor가 원인을 보여 줌 |
마무리
항상 켜진 agent를 구축한다는 것은 process를 daemon으로 만드는 일이 아닙니다. 사용자 → channel → session → tool → 실행 환경 → 외부 system으로 이어지는 권한 경계를 만든다는 뜻입니다.
이 단계의 완료 산출물은 다음 다섯 개입니다.
전용 service account
한 개의 허용 channel
한 개의 작업 workspace
격리된 terminal backend
재시작·거부·복구 test 기록
다음 글에서는 이 배포를 빠르게 만드는 대신 먼저 예측 가능하게 만듭니다. Memory에 넣을 사실과 Skill에 넣을 절차를 분리하고, model routing·Cron·token cost를 하나씩 최적화하겠습니다.