Docker로 배포
Chorus는 공식 Docker 이미지 chorusaidlc/chorus-app을 제공합니다. 이 가이드에서는 두 가지 Compose 구성(내장 데이터베이스를 쓰는 스탠드얼론, 외부 PostgreSQL과 Redis를 쓰는 프로덕션), 기존 데이터베이스에 연결하는 docker run, 전체 환경 변수 참고 정보, 그리고 컨테이너가 시작할 때 하는 일을 설명합니다.
먼저 이미지를 가져옵니다(pull).
docker pull chorusaidlc/chorus-app:latest스탠드얼론 구성과 프로덕션 구성 중 어느 것을 고를지는 배포 개요를 참고하세요.
스탠드얼론(내장 데이터베이스)
섹션 제목: “스탠드얼론(내장 데이터베이스)”외부 데이터베이스가 필요 없습니다. 이미지에 PGlite(내장 PostgreSQL)가 포함되어 있으며 모든 구성 요소를 자동으로 시작합니다. 데이터는 Docker 볼륨에 저장되어 컨테이너를 재시작해도 유지됩니다.
docker-compose.local.yml을 만듭니다.
# Standalone Chorus — embedded PGlite, no external PostgreSQL or Redisservices: app: image: chorusaidlc/chorus-app:latest ports: - "8637:8637" environment: # No DATABASE_URL — entrypoint auto-starts embedded PGlite - REDIS_URL= # Leave empty: the container generates a random session secret on first # start and keeps it in the chorus-local-data volume. Set it explicitly # (openssl rand -base64 32) if you prefer to manage it yourself. - NEXTAUTH_SECRET=${NEXTAUTH_SECRET:-} - COOKIE_SECURE=false - DEFAULT_USER=${DEFAULT_USER:-admin@example.com} - DEFAULT_PASSWORD=${DEFAULT_PASSWORD:-changeme} volumes: - chorus-local-data:/app/data
volumes: chorus-local-data:그런 다음 실행합니다.
docker compose -f docker-compose.local.yml up -dhttp://localhost:8637을 열고 admin@example.com / changeme로 로그인합니다(DEFAULT_USER / DEFAULT_PASSWORD 환경 변수로 바꿀 수 있습니다).
스탠드얼론 모드에서 컨테이너는 다음을 수행합니다.
- PGlite를 외부에 노출되지 않는 내부 포트(
5433)에서 시작합니다. - 데이터를
chorus-local-dataDocker 볼륨에 저장하므로 재시작해도 유지됩니다. NEXTAUTH_SECRET을 비워 두면 첫 시작 시 무작위 세션 서명 비밀 키를 생성해 같은 볼륨에 저장합니다(세션 비밀 키 참고).- Redis를 비활성화합니다(메모리 내 이벤트 전달로 대체되므로 단일 인스턴스 전용입니다).
- 시작 시 Prisma 마이그레이션을 자동으로 실행합니다.
프로덕션(외부 PostgreSQL + Redis)
섹션 제목: “프로덕션(외부 PostgreSQL + Redis)”프로덕션 환경, 특히 여러 레플리카를 둘 때는 앱을 외부 PostgreSQL과 Redis에 연결하세요. docker-compose.yml을 만듭니다.
services: app: image: chorusaidlc/chorus-app:latest ports: - "8637:8637" environment: - DATABASE_URL=postgresql://chorus:chorus@db:5432/chorus - REDIS_URL=redis://default:chorus-redis@redis:6379 # Session-signing secret. Leave empty and the container generates one on # first start and persists it in the chorus-app-data volume (single # replica only). For production, or more than one app container, set it # explicitly in .env or the environment: openssl rand -base64 32 # Changing the secret invalidates Default Auth and Super Admin sessions. - NEXTAUTH_SECRET=${NEXTAUTH_SECRET:-} - COOKIE_SECURE=${COOKIE_SECURE:-false} - DEFAULT_USER=admin@example.com - DEFAULT_PASSWORD=your-password volumes: - chorus-app-data:/app/data depends_on: db: condition: service_healthy redis: condition: service_healthy
redis: image: redis:7-alpine command: redis-server --requirepass chorus-redis volumes: - redis-data:/data healthcheck: test: ["CMD", "redis-cli", "-a", "chorus-redis", "ping"] interval: 5s timeout: 3s retries: 5
db: image: postgres:16-alpine environment: POSTGRES_USER: chorus POSTGRES_PASSWORD: chorus POSTGRES_DB: chorus volumes: - chorus-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U chorus -d chorus"] interval: 5s timeout: 5s retries: 5
volumes: chorus-app-data: chorus-data: redis-data:세션 비밀 키를 직접 지정하려면 시작하기 전에 한 번 생성해 compose 파일과 같은 위치의 .env 파일에 적어 둡니다.
echo "NEXTAUTH_SECRET=$(openssl rand -base64 32)" >> .env그런 다음 실행합니다.
docker compose up -dhttp://localhost:8637을 열고 DEFAULT_USER / DEFAULT_PASSWORD에 설정한 인증 정보로 로그인합니다.
기존 PostgreSQL에 연결해 실행하기
섹션 제목: “기존 PostgreSQL에 연결해 실행하기”이미 PostgreSQL과 Redis가 실행 중이라면 컨테이너를 직접 시작합니다.
docker run -d \ -p 8637:8637 \ -e DATABASE_URL=postgresql://user:pass@your-db-host:5432/chorus \ -e REDIS_URL=redis://default:password@your-redis-host:6379 \ -v chorus-app-data:/app/data \ -e COOKIE_SECURE=false \ -e DEFAULT_USER=admin@example.com \ -e DEFAULT_PASSWORD=your-password \ chorusaidlc/chorus-app:latest이 예제에서는 생성된 비밀 키가 chorus-app-data에 보관되므로 컨테이너를 다시 만들어도 유지됩니다.
직접 관리하려면 고정된 NEXTAUTH_SECRET을 주입하고 시작할 때마다 같은 값을 사용하세요.
환경 변수
섹션 제목: “환경 변수”데이터베이스와 세션 비밀 키
섹션 제목: “데이터베이스와 세션 비밀 키”| 변수 | 설명 |
|---|---|
DATABASE_URL | PostgreSQL 연결 문자열. 형식: postgresql://user:password@host:port/dbname. 대신 개별 DB_* 변수를 설정할 수도 있습니다(아래 참고). 생략하면 엔트리포인트가 내장 PGlite 인스턴스를 자동으로 시작합니다. |
NEXTAUTH_SECRET | 세션 토큰에 서명하기 위한 비밀 키. 무작위 문자열을 사용하세요(예: openssl rand -base64 32). Docker에서는 비워 둘 수 있습니다. 이 경우 컨테이너가 첫 시작 시 생성해 /app/data/.secret에 보관합니다. 앱 컨테이너를 여러 개 실행할 때는 모든 레플리카에 같은 값을 명시적으로 설정해야 합니다. 세션 비밀 키를 참고하세요. |
데이터베이스(DATABASE_URL의 대안)
섹션 제목: “데이터베이스(DATABASE_URL의 대안)”DATABASE_URL이 설정되지 않은 경우, 엔트리포인트는 다음 개별 변수로 그것을 조립합니다.
| 변수 | 설명 |
|---|---|
DB_HOST | PostgreSQL 호스트 |
DB_PORT | PostgreSQL 포트(기본값: 5432) |
DB_USERNAME | PostgreSQL 사용자 이름 |
DB_PASSWORD | PostgreSQL 비밀번호 |
DB_NAME | 데이터베이스 이름 |
Redis
섹션 제목: “Redis”| 변수 | 설명 |
|---|---|
REDIS_URL | 전체 Redis 연결 문자열. 형식: redis://username:password@host:port. 개별 변수보다 우선합니다. |
REDIS_HOST | Redis 호스트(REDIS_URL이 설정되지 않은 경우 사용) |
REDIS_PORT | Redis 포트(기본값: 6379) |
REDIS_USERNAME | Redis 사용자 이름(기본값: default) |
REDIS_PASSWORD | Redis 비밀번호 |
| 변수 | 설명 |
|---|---|
DEFAULT_USER | 내장 로그인용 이메일 주소(OIDC를 우회합니다). 첫 로그인 시 사용자와 해당 워크스페이스를 자동으로 생성합니다. |
DEFAULT_PASSWORD | 기본 사용자의 비밀번호(평문으로 지정하며, 런타임에 bcrypt로 비교합니다). |
NEXTAUTH_URL | 앱의 외부 공개 기본 URL(기본값: http://localhost:8637). 리버스 프록시 뒤에서 실행할 때 설정합니다. |
COOKIE_SECURE | HTTP 전용 배포에서 보안 쿠키를 끄려면 "false"로 설정합니다(docker-compose에서는 기본값 "false"). 프로덕션에서 HTTPS로 배포할 때는 "true"로 설정합니다. |
| 변수 | 기본값 | 설명 |
|---|---|---|
LOG_LEVEL | info(프로덕션) / debug(개발) | 서버 최소 로그 레벨. 허용 값: trace, debug, info, warn, error, fatal, silent. Prisma 쿼리 로그를 억제하려면 info로 설정합니다. |
NEXT_PUBLIC_LOG_LEVEL | warn(프로덕션) / debug(개발) | 브라우저 최소 로그 레벨. 허용 값: debug, info, warn, error. |
프로덕션 Docker 이미지는 항상 JSON을 stdout으로 출력합니다(CloudWatch / ELK에 바로 연결 가능). 색상이 입혀진 보기 좋은 출력은 로컬 개발에서만 사용할 수 있습니다.
슈퍼 관리자
섹션 제목: “슈퍼 관리자”| 변수 | 설명 |
|---|---|
SUPER_ADMIN_EMAIL | 슈퍼 관리자 계정의 이메일(/admin 패널에 접근할 수 있습니다). |
SUPER_ADMIN_PASSWORD_HASH | 슈퍼 관리자 비밀번호의 bcrypt 해시. 생성 방법: node -e "console.log(require('bcryptjs').hashSync('your-password', 10))" |
시작 시 동작
섹션 제목: “시작 시 동작”컨테이너의 엔트리포인트는 시작할 때마다 같은 순서를 실행합니다.
NEXTAUTH_SECRET을 확인합니다. 값이 비어 있거나 예전에 배포된 예제에 들어 있던 자리 표시자 값 중 하나이면, 컨테이너는/app/data/.secret에서 비밀 키를 읽거나 새로 생성하고 무엇을 했는지 로그에 남깁니다(값 자체는 절대 출력하지 않습니다). 직접 명시적으로 설정한 비밀 키는 그대로 사용됩니다.DATABASE_URL이 설정되지 않고DB_*변수도 하나도 제공되지 않으면, 엔트리포인트는 내부 포트(5433)에서 내장 PGlite 인스턴스를 시작합니다. 외부 데이터베이스가 구성되어 있으면 PGlite는 시작하지 않습니다.prisma migrate deploy를 실행해 보류 중인 데이터베이스 마이그레이션을 적용합니다.- 데이터베이스가 아직 준비되지 않았으면 10초마다 재시도합니다(최대 30회, 약 5분).
- 마이그레이션이 성공하면 Next.js 서버가
8637포트에서 시작합니다.
세션 비밀 키(NEXTAUTH_SECRET)
섹션 제목: “세션 비밀 키(NEXTAUTH_SECRET)”NEXTAUTH_SECRET은 Chorus가 직접 발급하는 로그인 세션(내장 이메일/비밀번호 로그인과 슈퍼 관리자 세션)에
서명합니다. OIDC 토큰은 ID 제공자가 서명하며, 에이전트 API 키는 별도로 검증됩니다.
이 비밀 키를 교체하면 Chorus가 서명한 세션만 무효화됩니다. API 키나 ID 제공자의 OIDC 토큰은 바뀌지 않습니다.
컨테이너의 동작
섹션 제목: “컨테이너의 동작”- 명시적으로 설정한, 자리 표시자가 아닌 값: 파일을 읽거나 쓰지 않고 그대로 사용합니다.
- 미설정, 빈 값, 알려진 공개 자리 표시자:
/app/data/.secret을 재사용합니다. 파일이 없거나 비어 있으면 무작위 32바이트를 64자리 16진수로 생성해0600권한으로 저장합니다. 환경 변수의 자리 표시자에는 경고도 남깁니다. - 사용할 수 없는 저장된 키: 일반 파일이 아니거나 읽을 수 없는 경우, 공개 자리 표시자가 저장되어 있는 경우, 또는 생성·쓰기에 실패한 경우에는 마이그레이션 전에 시작을 중단합니다. 저장된 잘못된 자리 표시자를 조용히 받아들이거나 교체하지 않습니다.
키 값은 로그에 출력하지 않습니다. 자동 생성된 키를 유지하려면 /app/data를 영구 볼륨에 두세요. 여러 레플리카에는 같은 키를
명시적으로 주입해야 합니다. AWS CDK 스택은 이미 Secrets Manager를 통해 키를 공유합니다.
비밀 키 설정과 교체
섹션 제목: “비밀 키 설정과 교체”openssl rand -base64 32로 한 번 생성해 배포 환경의 비밀 저장소나 보호된 .env에 보관하고 이후 시작할 때 재사용하세요.
실제로 사용되는 키가 바뀌면 내장 이메일/비밀번호 로그인 사용자와 슈퍼 관리자가 다시 로그인해야 합니다. .secret을 삭제하면 자동 생성된
키만 교체되며, 명시적으로 설정한 키가 있으면 아무 영향이 없습니다.
이 자동 생성은 Docker 엔트리포인트와 npm chorus 런처가 수행합니다. next start나 node server.js로
직접 실행한다면 안전한 값을 직접 설정해야 합니다. 앱은 알려진 자리 표시자를 오류 로그에 기록하지만
교체하지는 않습니다. 키가 없으면 Chorus는 자체 로그인 세션을 발급할 수 없습니다.
이미지 세부 정보
섹션 제목: “이미지 세부 정보”- 기본 이미지:
node:22-alpine - 내부 포트:
8637 - 아키텍처:
linux/amd64,linux/arm64
다음 단계
섹션 제목: “다음 단계”- 프로덕션 배포: 글로벌 npm 패키지에서 실행하기와 전체 AWS CDK 안내.
- 셀프 호스팅 Chorus 운영하기: 첫 로그인 부트스트랩, 배포 측 인증, 그리고 업그레이드 전 백업.