2026년 한 대 임대 Mac mini에서 OpenClaw dev·스테이징·프로드 격리(LaunchAgent, 포트, 시크릿)
팀은 노트북과 같은 macOS를 쓰려고 홍콩·일본·한국·싱가포르·미국에서 Mac mini M4 호스트에 OpenClaw 자동화를 올립니다. 하지만 스테이징 프롬프트가 프로덕션 MCP 스킬을 바꿔 버리면 신뢰는 금방 무너집니다. 이 글은 (1) 한 대 미니에도 세 겹 벽이 필요한 이유, (2) 환경이 무엇을 건드릴 수 있는지 수치화하는 영향 범위 표, (3) 사람이 grep할 수 있는 폴더+포트 명명 규약, (4) launchd 아래 반복 가능한 LaunchAgent 패턴, (5) GPU를 복제하지 않고 시크릿과 JSONL 로그를 나누는 법, (6) 자동화를 머지하기 전 프리플라이트입니다. 연관 글: 큐 크기는 병렬 에이전트 동시성, 자격 증명은 시크릿과 키체인, JSONL은 로깅·로테이션.
Apple Silicon 미니 한 대에도 “한 폴더 YOLO”보다 세 환경이 나은 이유
재무는 환경을 청구서로 보고, 플랫폼 팀은 장애 도메인으로 봅니다. Slack 웹훅과 프로덕션 Terraform 상태에 뿌리 내린 LaunchAgent 하나는 머지 충돌 한 번이면 재앙입니다. 물리 박스 하나에서 dev / staging / prod를 나누는 비용은 낮습니다—M4의 CPU 여유는 넉넉하고, 비싼 것은 파일 공유 실수입니다. 격리하면 독립 롤백, 고객 웹훅을 건드리지 않는 리얼한 소크 테스트, “새 스킬 시도”와 “청구 봇 발화”를 감사 추적으로 분리할 수 있습니다.
조직이 커질수록 폴더 이름만으로 권한을 추론하지 않도록 RACI를 표 옆에 두세요. 사고가 나면 코드 디버깅 전에 버전·롤백 플레이북으로 배포를 멈춥니다.
- 숫자 가드레일: 환경 루트마다 APFS 여유 20 GB 이상—부하 테스트 때 JSONL 스파이크가 다른 에이전트를 멈추지 않게 합니다.
- 사람 가드레일: 스테이징 알림과 프로덕션 인시던트에 같은 Slack 채널을 쓰지 마세요—소음이 호출 무시를 가르칩니다.
- 자동화 가드레일: API 할당이 일주일 안정될 때까지 스테이징
maxConcurrentTasks는 프로덕션의 절반으로 캡합니다.
영향 범위 표: 각 환경이 무엇에 손댈 수 있는가
| 환경 | 허용되는 부작용 | 티켓 없이 금지 | 전형적인 API 키 범위 |
|---|---|---|---|
| Dev | 로컬 브랜치, 샌드박스 SaaS 프로젝트 | 고객 PII 내보내기 | 읽기 전용 미러 또는 단기 PAT |
| Staging | 클론 데이터로 전체 스킬 그래프 | 프로덕션 DNS 변경 | 별도 Stripe 테스트 모드 + 비프로덕션 AWS 역할 |
| Prod | 고객 워크플로, 결제 웹훅 | 실험적 컴파일러 | 최소 권한 역할 + 분기별 로테이션 |
이 행을 아키텍처 다이어그램 옆에 걸어 두면 신입이 폴더 이름만 보고 권한을 추측하지 않습니다.
근육 기억으로 칠 수 있는 폴더·포트 계약
~/openclaw-dev, ~/openclaw-staging, ~/openclaw-prod처럼 형제 디렉터리를 만들고 스테이징을 프로드 안에 넣지 마세요. 각각 안에 config, skills, logs, tmp를 미러링하면 지원 스크립트를 동일하게 둡니다. 게이트웨이는 127.0.0.1에 띄우고 간격 둔 포트를 씁니다: 예를 들어 dev 18789, staging 18799, prod 18809—중간 숫자가 페어 디버깅 때 임시 포트 포워드를 흡수합니다.
내부 위키에 다섯 열을 고정하세요: 환경 이름, 절대 경로, TCP 포트, LaunchAgent 레이블, Unix 소유 사용자. “어느 plist가 막혔지?”에 대한 답은 한 번의 grep이어야 합니다.
LaunchAgent 패턴: 환경당 plist 하나, 거대한 단일 plist 금지
com.yourorg.openclaw.dev.plist, ...staging.plist, ...prod.plist처럼 세 파일로 나눕니다. 각 plist는 WorkingDirectory, 비기본 PATH용 EnvironmentVariables, 해당 환경 logs 트리를 가리키는 StandardOutPath/StandardErrorPath를 설정합니다. 초기 롤아웃에서는 크래시 루프가 제공자를 두드리지 않게 ThrottleInterval을 10초로 둡니다.
예시 골격(설치 경로는 자르세요):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>Label</key><string>com.example.openclaw.staging</string>
<key>WorkingDirectory</key><string>/Users/automation/openclaw-staging</string>
<key>ProgramArguments</key><array>
<string>/opt/homebrew/bin/openclaw</string>
<string>gateway</string>
<string>--port</string><string>18799</string>
</array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
</dict></plist>
127.0.0.1에 바인딩하고 원격 접근은 SSH 포트 포워딩이나 검토된 엣지 프록시로—게이트웨이 포트를 공인 인터넷에 노출하지 마세요.
교차 오염 없이 시크릿·로그·로테이션
API 키는 환경별 키체인 항목이나 권한 0400의 .env에 나눠 저장합니다. 프로덕션 키를 돌릴 때 스테이징이 같은 경로를 “잠깐” 읽게 하면 그 임시가 영구가 됩니다. 로그는 JSONL 이벤트마다 env=staging 같은 접두를 붙이고 파일명도 분리해 로테이션 작업이 프로덕션 기록을 자르지 않게 합니다.
환경마다 http://127.0.0.1:<port>/healthz(또는 게이트웨이 동등물)를 헬스 프로브로 두세요. 임계값은 다르게: dev는 실험 중 5% 깜빡임을 허용하고, prod는 연속 두 번 실패 시 페이지합니다.
스킬을 프로덕션으로 올리기 전 프리플라이트
- 설정 diff: 스테이징
openclaw.json(또는 동등물)이 명시된 키를 제외하고 프로덕션과 일치하는지 확인합니다. - 드라이 리플레이: 익명화한 페이로드로 최근 프로덕션 유사 이벤트 50건을 스테이징에서 재생합니다.
- 포트 검증:
lsof -nP -iTCP -sTCP:LISTEN에 루프백 의도 리스너만 있는지 봅니다. - 디스크:
df -h로 프로덕션 로그 볼륨에 최소 20 GB 여유가 있는지 확인합니다. - MCP 서버: 각 환경의 도구 매니페스트가 서로 다른 작업 디렉터리를 가리킵니다.
- 롤백 리허설: 유지보수 창에서 prod plist에 대해
launchctl unload/load를 연습합니다. - 서명: 변경 티켓에 리뷰어 이니셜—자동화도 앱 배포와 같은 엄격함을 받아야 합니다.
FAQ
프로덕션을 root로 돌려야 하나요? 아니요—파일 ACL이 있는 전용 서비스 계정이 root보다 낫습니다. root는 TCC 프롬프트와 감사를 복잡하게 만듭니다.
Node 런타임 하나를 공유할 수 있나요? 버전은 같아도 NODE_OPTIONS와 글로벌 npm 접두는 환경별로 두어 Homebrew PATH 글의 문제를 피하세요.
GitOps는? 브랜치(main, staging, dev)를 미러링하고 가능하면 읽기 전용 클론으로 디렉터리에 매핑—설정 버전 관리 참고.
격리 후에도 ProxyMac Mac mini가 OpenClaw 다중 환경에 맞는 이유
환경이 분리되면 한 대의 Mac mini M4가 동시 게이트웨이에 통합 메모리 헤드룸, 노트북과 동일한 네이티브 macOS, HK / JP / KR / SG / US 전반의 예측 가능한 과금을 제공합니다. 스테이징이 Swift 패키지를 컴파일하는 동안 프로덕션 에이전트가 웹훅에 응답하는 시나리오에 적합합니다. 티어는 요금 페이지, 접근 패턴은 도움말 센터, launchctl이 텍스트로 설명을 거부할 때는 VNC로 Activity Monitor를 확인하세요.