자체 호스팅 빠른 시작
Docker Compose로 Melso를 시작하고 로그인한 뒤 첫 실행 컴퓨터를 연결합니다.
자체 호스팅 Melso는 두 부분으로 나뉩니다.
| 부분 | 실행하는 항목 | 설치 위치 |
|---|---|---|
| Melso 서비스 | Web, API, PostgreSQL | Docker가 설치된 컴퓨터 |
| 실행 컴퓨터 | Melso 데몬과 AI 코딩 도구 | 개발자가 실제로 작업하는 컴퓨터 |
두 부분을 같은 컴퓨터에 둘 수도 있고 분리할 수도 있습니다. 자체 호스팅으로 대체하는 것은 Melso Cloud 부분뿐입니다.
이 문서에서는 Docker Compose를 사용합니다. Kubernetes 배포는 저장소의 Self-hosting guide를 참고하세요.
시작하기 전에
Melso 서비스를 실행할 컴퓨터에는 다음 항목이 필요합니다.
- Docker Engine 또는 Docker Desktop, 실행 가능한
docker compose - Git, Make, curl, OpenSSL
- 로컬
3000,8080port가 사용 중이지 않음
먼저 Docker와 Compose를 사용할 수 있는지 확인합니다.
docker info
docker compose versionMelso는 Compose v2인 docker compose를 사용합니다. 이전 docker-compose v1은 지원하지 않습니다.
실행 컴퓨터에는 Claude Code, Codex, Cursor 같은 AI 코딩 도구가 하나 이상 설치되어 로그인된 상태여야 합니다. Melso CLI는 5단계에서 설치합니다.
1. Melso 시작
서비스를 실행할 컴퓨터에서 다음을 실행합니다.
git clone --depth 1 https://github.com/albertsalgueda/melso.git
cd multica
make selfhost처음 실행할 때 make selfhost가 다음 작업을 수행합니다.
.env.example에서.env생성JWT_SECRET, PostgreSQL 비밀번호,MULTICA_VCS_SECRET_KEY(자체 호스팅 Git 연동 암호화 키)를 임의 생성- PostgreSQL, Melso backend, Melso frontend 이미지 pull
- persistent data volume 생성 및 세 컨테이너 시작
- backend가 health check에 응답할 때까지 대기
이후 make selfhost를 다시 실행하면 기존 .env와 data volume을 계속 사용하며 키를 새로 만들지 않습니다.
make selfhost는 공개된 이미지를 pull하며 현재 checkout의 코드를 컴파일하지 않습니다. 로컬 소스 코드를 테스트하려면 make selfhost-build를 사용하세요.
2. 서비스 준비 상태 확인
컨테이너 상태를 확인합니다.
docker compose -f docker-compose.selfhost.yml pspostgres는 healthy, backend와 frontend는 실행 중으로 표시되어야 합니다. 그런 다음 backend, 데이터베이스, migration을 확인합니다.
curl -fsS http://localhost:8080/readyz정상 응답:
{"status":"ok","checks":{"db":"ok","migrations":"ok"}}Backend 컨테이너는 시작할 때마다 데이터베이스 migration을 먼저 실행하고 서비스를 시작하므로 migration 명령을 직접 실행할 필요가 없습니다.
3. 접근 방식 선택
로컬에서 접근
http://localhost:3000을 바로 엽니다. 이후 melso setup self-host를 실행할 때도 URL을 전달할 필요가 없습니다.
원격에서 접근
Docker Compose는 기본적으로 3000과 8080을 127.0.0.1에만 bind합니다. 0.0.0.0으로 바꿔 공개 인터넷에 직접 노출하지 말고 HTTPS reverse proxy를 사용하세요.
다음 두 도메인을 예로 사용합니다.
app.example.com: Melso Webapi.example.com: API, health check, 데몬 연결
먼저 .env에 공개 주소를 설정합니다.
FRONTEND_ORIGIN=https://app.example.com
MULTICA_APP_URL=https://app.example.com
MULTICA_PUBLIC_URL=https://api.example.com이 설정에서는 모든 브라우저 트래픽이 app 도메인을 거치고 cookie가 도메인을 넘지 않으므로 COOKIE_DOMAIN을 설정할 필요가 없습니다. 브라우저에서 api 도메인에 직접 접근하게 바꾼다면 반드시 설정해야 합니다. 환경 변수를 참고하세요.
그런 다음 DNS를 설정하고 Caddy로 로컬 port를 proxy합니다.
app.example.com {
# 브라우저 WebSocket을 backend로 직접 전달
@ws path /ws /ws/*
handle @ws {
reverse_proxy 127.0.0.1:8080 {
flush_interval -1
}
}
# 나머지 경로는 frontend로 전달하며 frontend가 API와 로그인 요청을 proxy
handle {
reverse_proxy 127.0.0.1:3000
}
}
api.example.com {
reverse_proxy 127.0.0.1:8080 {
flush_interval -1
}
}Caddy가 TLS 인증서를 발급하고 WebSocket을 전달합니다. .env를 수정한 뒤 up -d로 컨테이너를 다시 만들어 새 설정을 적용합니다.
docker compose -f docker-compose.selfhost.yml up -d
curl -fsS https://api.example.com/readyzdocker compose restart는 기존 컨테이너만 다시 시작하며 .env를 다시 읽지 않습니다. 설정을 수정한 뒤 .env를 다시 읽게 하려면 docker compose -f docker-compose.selfhost.yml up -d를 실행하세요.
4. 로그인 및 워크스페이스 만들기
로컬 http://localhost:3000 또는 앞에서 설정한 https://app.example.com을 열고 이메일을 입력해 인증 코드를 받습니다.
기본 설정에는 이메일 서비스가 없습니다. 인증 코드를 요청한 뒤 backend 로그에서 확인할 수 있습니다.
docker compose -f docker-compose.selfhost.yml logs backend \
| grep "Verification code"로그에는 다음과 비슷한 내용이 나타납니다.
[DEV] Verification code for you@example.com: 123456인증 코드를 입력하고 첫 워크스페이스를 만듭니다. Resend 또는 SMTP를 설정하면 이메일로 인증 코드가 전달되므로 멤버가 컨테이너 로그를 읽을 필요가 없습니다. 자세한 내용은 로그인 및 가입 설정을 참고하세요.
자체 호스팅 환경은 기본적으로 APP_ENV=production을 사용하므로 고정 인증 코드가 활성화되지 않습니다. 공개 인터넷 인스턴스에 MULTICA_DEV_VERIFICATION_CODE를 설정하지 마세요.
5. 실행 컴퓨터 연결
다음 명령은 Docker 서버가 아니라 AI 코딩 도구를 실행할 컴퓨터에서 실행합니다. 두 컴퓨터가 같을 수도 있습니다.
작업은 데몬을 실행하는 사용자의 모든 권한으로 동작하며, 그 사용자가 읽고 쓸 수 있는 모든 것에 접근할 수 있습니다. 개인 계정 대신 전용 Unix 사용자, 컨테이너, 또는 VM에서 데몬을 실행하세요. 보안 모델을 참고하세요.
먼저 Melso CLI를 설치합니다.
macOS / Linux
curl -fsSL https://downloads.melso.ai/install.sh | bashWindows PowerShell
irm https://downloads.melso.ai/install.ps1 | iexMelso 서비스가 같은 컴퓨터에 있다면 다음을 실행합니다.
melso setup self-hostMelso 서비스가 다른 컴퓨터에 있다면 앞에서 설정한 두 주소를 전달합니다.
melso setup self-host \
--server-url https://api.example.com \
--app-url https://app.example.com이 명령은 먼저 <server-url>/health를 확인한 뒤 브라우저를 열어 로그인을 완료합니다. 로그인 후 로컬 자격 증명을 저장하고 데몬을 시작합니다.
연결 상태를 확인합니다.
melso daemon status정상적인 출력에는 다음 내용이 표시됩니다.
Daemon: runningAgents에 로컬에 설치된 AI 코딩 도구 포함Workspaces가0보다 큼
6. 첫 실행 완료
Melso로 돌아옵니다. 런타임 목록에 온라인 런타임이 나타나면 에이전트를 만들고 첫 이슈를 할당합니다.
실행 로그가 "완료됨"으로 바뀌고 타임라인에 에이전트의 답변이 나타나면 자체 호스팅 서비스, 실행 컴퓨터, AI 코딩 도구가 연결된 것입니다. 자세한 단계는 빠른 시작의 3~5단계를 참고하세요.
자주 사용하는 관리 명령
다음 명령은 모두 multica 저장소 디렉터리에서 실행합니다.
# 상태 확인
docker compose -f docker-compose.selfhost.yml ps
# backend 로그 확인
docker compose -f docker-compose.selfhost.yml logs -f backend
# .env 변경 적용
docker compose -f docker-compose.selfhost.yml up -d
# 서비스를 중지하고 data volume 유지
docker compose -f docker-compose.selfhost.yml down공개된 이미지 업그레이드:
git pull --ff-only
docker compose -f docker-compose.selfhost.yml pull
docker compose -f docker-compose.selfhost.yml up -d
curl -fsS http://localhost:8080/readyzDocker Compose 설치를 업그레이드하는 방법은 두 가지이며, 기존 설치에서는 결과가 같습니다 — Makefile의 selfhost 타깃이 동일한 docker compose pull + up -d를 실행하고, 여기에 더해 .env가 없으면 생성하고 /health를 기다린 뒤 상태 요약을 출력합니다. 편한 쪽을 쓰세요.
cd multica
git pull
make selfhostcd multica
git pull
docker compose -f docker-compose.selfhost.yml pull
docker compose -f docker-compose.selfhost.yml up -dgit pull이 실제로 하는 일
git pull이 갱신하는 것은 docker-compose.selfhost.yml 자체입니다 — 새로 추가된 환경 변수, 새 서비스, 변경된 healthcheck. 새 Melso 버전을 받아오는 수단이 아닙니다.
실제로 어떤 버전이 뜰지는 docker compose pull이 결정합니다. 이 명령이 GHCR에 해당 태그가 지금 어떤 이미지를 가리키는지 물어보기 때문입니다. 그래서 몇 달 묵은 checkout에서도 오늘자 latest 이미지를 받을 수 있고, 반대로 git pull만 해서는 이미지를 받아 컨테이너를 다시 만들기 전까지 아무것도 바뀌지 않습니다.
MULTICA_IMAGE_TAG를 고정해 뒀다면 두 방법 모두 업그레이드되지 않습니다. 두 이미지 모두 ${MULTICA_IMAGE_TAG:-latest}로 해석되며(docker-compose.selfhost.yml:42, :125), .env.example에는 MULTICA_IMAGE_TAG=latest가 들어 있습니다. .env에서 특정 릴리스로 고정해 뒀다면 pull은 같은 태그를 다시 받아올 뿐이라 예전 버전에 그대로 머무릅니다 — 오류도, 경고도 없습니다. 업그레이드 전에 확인하세요.
grep MULTICA_IMAGE_TAG .env
# MULTICA_IMAGE_TAG=v0.4.5 ← 고정됨: 먼저 latest(또는 원하는 릴리스)로 수정.env는 덮어쓰이지 않습니다
make selfhost는 .env 파일이 없을 때만 생성합니다. 기존 설치에서 다시 실행해도 JWT_SECRET, Postgres 비밀번호, 이메일 설정, FRONTEND_ORIGIN은 그대로 유지됩니다.
먼저 Postgres를 백업하세요
migration은 앞으로만 진행되고 되돌릴 수 없으므로, 중요한 배포를 업그레이드하기 전에 덤프를 받아 두세요.
docker compose -f docker-compose.selfhost.yml exec -T postgres \
pg_dump -U multica multica > multica-backup.sql && gzip multica-backup.sqlpg_dump를 gzip으로 바로 파이프하지 마세요. 셸은 파이프라인에서 마지막 명령의 종료 상태를 반환하므로, pg_dump … | gzip > backup.sql.gz는 덤프가 실패해도 0으로 끝나면서 형식상 완전히 유효하지만 내용이 비어 있는 20바이트짜리 아카이브를 남깁니다. 먼저 파일로 리다이렉트하면 pg_dump 자신의 종료 상태가 적용되고, &&는 실제로 성공한 덤프만 압축합니다.
POSTGRES_USER / POSTGRES_DB를 기본값 multica에서 바꿨다면 .env의 실제 값으로 교체하세요. 데이터는 multica_pgdata라는 named volume에 있으며 docker compose down으로는 사라지지 않지만, down -v로는 사라집니다.
migration은 자동으로 실행됩니다
1단계와 마찬가지로, 백엔드 컨테이너는 트래픽을 받기 전에 시작 시점에 ./migrate up을 실행합니다(docker/entrypoint.sh). 별도의 업그레이드 명령은 없습니다 — 새 이미지를 띄우는 것 자체가 migration 단계입니다. 진행 상황을 보려면:
docker compose -f docker-compose.selfhost.yml logs -f backendMigration은 backend 시작 시 자동으로 실행됩니다. migration 103처럼 이전 데이터 backfill이 필요한 migration도 자동으로 backfill을 완료합니다. 드물게 자동 backfill이 실패하고 refusing to drop legacy daily rollups 오류가 나타나면 문제 해결을 참고하세요.
/health 말고 /readyz로 검증하세요
/health는 liveness 프로브라서 프로세스가 살아 있기만 하면 {"status":"ok"}를 반환합니다 — migration이 실패했더라도 마찬가지입니다. /readyz(server/cmd/server/router.go:680, /healthz는 별칭)는 데이터베이스와 적용된 migration 집합을 확인하므로, 잘못된 업그레이드를 잡아내는 것은 이쪽입니다.
curl -s localhost:8080/readyz
# {"status":"ok","checks":{"db":"ok","migrations":"ok"}}HTTP 200이면서 두 검사가 모두 ok가 아니라면 새 버전이 migration을 마치지 못한 것입니다. 트래픽을 보내기 전에 백엔드 로그를 확인하세요.
Kubernetes
Helm은 자체 업그레이드 경로가 있습니다. values 파일에서 images.backend.tag / images.frontend.tag를 원하는 릴리스로 설정한 뒤 helm upgrade를 실행하세요. 태그가 바뀌면 파드 스펙이 바뀌므로 Kubernetes가 새 이미지를 받아 Deployment를 롤아웃합니다 — 이것이 확실한 경로입니다.
kubectl -n multica rollout restart는 그 자체로는 업그레이드가 아닙니다. 차트는 pullPolicy: IfNotPresent로 배포되므로(deploy/helm/multica/values.yaml), 해당 태그를 이미 캐시한 노드는 예전 이미지를 재사용하고 restart를 해도 아무것도 바뀌지 않습니다 — MULTICA_IMAGE_TAG 고정과 같은 부류의 함정입니다. 부동 태그로 이 방식을 쓰려면 먼저 images.backend.pullPolicy / images.frontend.pullPolicy를 Always로 설정하세요. 저장소의 Self-hosting guide를 참고하세요.
docker compose down은 pgdata와 backend_uploads를 유지합니다. -v를 추가하면 데이터베이스를 포함한 이 data volume이 삭제됩니다. 인스턴스를 비우려는 것이 확실하지 않다면 docker compose down -v를 실행하지 마세요.
자주 묻는 문제
| 증상 | 먼저 확인할 사항 |
|---|---|
/readyz가 ok를 반환하지 않음 | docker compose -f docker-compose.selfhost.yml logs backend postgres를 실행합니다. |
| 인증 코드를 받지 못함 | 인증 코드를 한 번 요청한 뒤 backend 로그에서 Verification code를 찾습니다. |
setup self-host에 server 접근 불가가 표시됨 | 실행 컴퓨터에서 https://api.example.com/health를 요청해 DNS, TLS, reverse proxy에 모두 접근할 수 있는지 확인합니다. |
데몬의 Agents가 비어 있음 | AI 코딩 도구가 PATH에 있고 로그인되어 있는지 확인한 뒤 melso daemon restart를 실행합니다. |
| 이슈가 계속 큐에서 기다림 | melso daemon status를 실행해 데몬이 실행 중이며 워크스페이스에 연결되었는지 확인합니다. |
더 많은 경우는 문제 해결을 참고하세요.
다음 단계
- 로그인 및 가입 설정 — 이메일, Google 로그인, 가입 범위를 설정합니다.
- 환경 변수 — 전체 서버 설정을 확인합니다.
- Self-hosting guide — Kubernetes, 업그레이드, 수동 배포를 알아봅니다.
- 데스크톱 앱 — Desktop을 자체 호스팅 서비스에 연결합니다.