> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nora.my/llms.txt
> Use this file to discover all available pages before exploring further.

# Docker Compose

> 호스트 하나, 서비스 다섯, 명령어 하나. 데스크톱 앱이 쓰는 compose 파일을 직접 실행합니다.

`infra/onprem/docker-compose.yml` 은 데스크톱 앱이 그대로 실행하는 compose 파일입니다. 데스크톱 셸 없이 Nora 를 서버나 VM 에서 돌리고 싶다면 이 파일에 직접 대세요.

## 사전 조건

* **Docker Engine 20.10+** (`docker --version`).
* **Docker Compose plugin** (`docker compose version`).
* **디스크 여유 약 15 GB** — 컨테이너 이미지와 Postgres 볼륨을 담을 공간.
* **Docker 에 4 GB RAM** 이상 할당 (8 GB 권장).

## 빠른 시작

<Steps>
  <Step title="env 템플릿 복사">
    ```bash theme={null}
    cd infra/onprem
    cp .env.example .env.onprem
    ```
  </Step>

  <Step title="POSTGRES_PASSWORD 설정">
    실제 비밀번호가 있어야 compose 가 부팅됩니다. `.env.onprem` 을 열고 `CHANGE_ME_postgres_password` 를 실제 자격증명으로 바꾸세요. compose 파일에 접근할 수 있는 누구든 이 값을 읽을 수 있으므로 실 자격증명으로 다루어야 합니다.
  </Step>

  <Step title="스택 up">
    ```bash theme={null}
    docker compose --env-file .env.onprem up -d
    ```

    이제 `http://localhost:8090` 에서 Nora 가 서빙됩니다. 첫 부팅에서 SQL 마이그레이션이 자동으로 실행됩니다.
  </Step>
</Steps>

## 스택 구성

내부 Docker 네트워크에 다섯 개 서비스가 뜹니다. 호스트 포트를 publish 하는 건 `nora-server` 하나입니다.

| 서비스 | 이미지 | 컨테이너 이름 |
| - | - | - |
| `postgres` | `pgvector/pgvector:pg16` | `nora-onprem-postgres` |
| `redis` | `redis:7-alpine` | `nora-onprem-redis` |
| `qdrant` | `qdrant/qdrant:v1.12.4` | `nora-onprem-qdrant` |
| `nora-llm-bridge` | `ghcr.io/conscience-technology/nora-llm-bridge` | `nora-onprem-llm-bridge` |
| `nora-server` | `ghcr.io/conscience-technology/nora-server` | `nora-onprem-server` |

### 이미지 해석 순서

`nora-server` 와 `nora-llm-bridge` 는 같은 순서로 이미지를 해석합니다.

1. 이미지가 이미 로컬에 있으면 (tarball 로 load 했거나 이전 `up` 이 빌드한 결과) compose 가 그대로 사용합니다.
2. 로컬에 없지만 레지스트리에 닿을 수 있으면 GHCR 에서 pull 합니다.
3. 둘 다 아니면 폴백 `build:` 블록으로 소스에서 빌드합니다. 최초 소스 빌드는 브릿지는 약 2 분, 서버는 15 – 30 분 걸립니다.

프라이빗 레지스트리를 가리키려면 `NORA_IMAGE_REGISTRY` 를 세팅합니다. 에어갭 운영자가 tarball 을 사내 미러에 load 한 경우에 유용합니다.

## 필수 환경 변수

필수는 하나뿐입니다. 나머지는 합리적인 기본값이 있습니다.

* **`POSTGRES_PASSWORD`** — 번들된 Postgres 컨테이너의 superuser 비밀번호입니다. 실제 값 없이는 compose 가 부팅되지 않습니다.

가장 흔한 오버라이드:

| 변수 | 기본값 | 하는 일 |
| - | - | - |
| `NORA_HOST_PORT` | `8090` | `nora-server` 가 publish 할 호스트 포트. |
| `NORA_LICENSE_KEY` | *(비어 있음)* | 유료 라이선스 활성화. 비면 free tier (워크스페이스 1, 플로우 3, 하루 100 실행). |
| `NORA_PUBLIC_URL` | `http://localhost:${NORA_HOST_PORT}` | MCP 응답과 OAuth 콜백에 실리는 base URL. 프록시가 스택 앞단에 있을 때 세팅합니다. |
| `NORA_OAUTH_REDIRECT_BASE` | `NORA_PUBLIC_URL` 과 동일 | Google · GitHub OAuth 콜백 base. |
| `NORA_COOKIE_SECURE` | `0` | 리버스 프록시에서 TLS 를 종단한다면 `1` — HTTPS 위에서는 브라우저가 `Secure` 쿠키를 요구합니다. |
| `NORA_RUN_MIGRATIONS` | `1` | 부팅 시 `sqlx migrate run` 을 실행합니다. DBA 가 수동으로 실행하는 스테이지 업그레이드라면 `0`. |
| `NORA_VECTOR_STORE` | `pgvector` | `qdrant` 로 바꾸고 `NORA_QDRANT_URL` 을 세팅하면 검색이 Qdrant 서비스로 라우팅됩니다. |
| `NORA_AIRGAP` | *(비어 있음)* | 격리 배포는 `1`. 공용 인터넷이 여전히 닿으면 서버가 exit 78 로 종료합니다. |
| `NORA_DEPLOY_FLAVOR` | `onprem` | 오버라이드하지 마세요. SaaS 전용 표면이 이 값에 걸려 있습니다. |

OAuth · SSO 자격증명은 그대로 통과합니다: `GOOGLE_OAUTH_CLIENT_ID` · `GOOGLE_OAUTH_CLIENT_SECRET`, `GITHUB_OAUTH_CLIENT_ID` · `GITHUB_OAUTH_CLIENT_SECRET`, 그리고 `ANTHROPIC_API_KEY` 같은 프로바이더 API 키.

## laptop 래퍼

compose 플래그를 매번 외우기 싫다면 `infra/onprem/nora-laptop.sh` 가 같은 스택에 대한 친절한 표면을 제공합니다.

```bash theme={null}
./nora-laptop.sh start        # 전체 스택 부팅 (첫 실행 시 .env.onprem 생성)
./nora-laptop.sh stop         # 모두 정지 (데이터 볼륨은 유지)
./nora-laptop.sh restart      # stop → start
./nora-laptop.sh status       # 컨테이너 상태와 endpoint URL
./nora-laptop.sh endpoint     # URL 만 출력 (스크립트용)
./nora-laptop.sh logs [svc]   # 로그 follow (전체 또는 특정 서비스)
./nora-laptop.sh reset        # 정지하고 데이터 볼륨까지 삭제 (파괴적)
```

<Tip>
  `start` 는 첫 실행에서 `POSTGRES_PASSWORD` 를 임의값으로 생성해 `.env.onprem` 에 넣어 둡니다. 파일을 먼저 편집하지 않고 그대로 부팅해도 됩니다. 라이선스 키를 붙이거나 OAuth 를 연결할 때 나중에 `.env.onprem` 을 수정하세요.
</Tip>

## 데이터 위치

* **`pgdata` 볼륨** 은 Postgres 데이터를 담습니다 — 워크스페이스, 플로우, 메모리, 트레이스 등 모든 트랜잭션 상태.
* **`qdrantdata` 볼륨** 은 Qdrant 벡터 인덱스를 담습니다. `NORA_VECTOR_STORE=qdrant` 일 때만 사용됩니다.
* **`.env.onprem`** 은 여러분의 설정을 담습니다.

전체 복원을 위해 세 가지를 함께 백업하세요. `docker volume inspect nora-onprem_pgdata` 로 드라이버가 마운트한 경로를 확인할 수 있습니다.

## 업그레이드

<Steps>
  <Step title="새 태그로 지정">
    ```bash theme={null}
    export NORA_IMAGE_TAG=onprem-v0.2.0
    ```

    릴리스 파이프라인이 GHCR 에 태그를 publish 합니다.
  </Step>

  <Step title="pull 후 재시작">
    ```bash theme={null}
    docker compose --env-file .env.onprem pull
    docker compose --env-file .env.onprem up -d
    ```

    새 서버의 첫 부팅에서 마이그레이션이 자동으로 실행됩니다.
  </Step>
</Steps>

<Warning>
  마이그레이션은 additive-only 입니다. 옛 서버는 새 스키마에 붙지 못하므로, 다운그레이드가 필요하면 매칭되는 DB 스냅샷을 준비해 두세요.
</Warning>

## 언인스톨

```bash theme={null}
# 모두 정지하고 볼륨까지 삭제 — 파괴적, 모든 데이터 사라짐
docker compose --env-file .env.onprem down -v

# 나중 복원을 위해 볼륨 유지
docker compose --env-file .env.onprem down
```

컨테이너 이미지는 Docker 이미지 스토어에 남습니다. 디스크 회수는 `docker image prune` 으로 처리하세요.

## 관련

<CardGroup cols={2}>
  <Card title="에어갭 tarball" icon="box-archive" href="/ko/local-app/deploy/airgap">
    같은 compose 파일과 이미지를 하나의 아카이브에 담습니다.
  </Card>

  <Card title="Kubernetes (Helm)" icon="dharmachakra" href="/ko/local-app/deploy/kubernetes">
    같은 5 서비스를 인그레스 · TLS 가 붙은 클러스터로.
  </Card>

  <Card title="데스크톱 앱" icon="laptop" href="/ko/local-app/desktop/install">
    이 compose 파일을 네이티브 창으로 감쌉니다.
  </Card>
</CardGroup>
