사람함께에이전트▾
사람 — 직접 정하고 책임지는 비즈니스 규칙과 흐름. 직접 읽어보세요.
함께 — 개념은 알아두고, 세부 규칙은 에이전트가 따릅니다.
에이전트 — 에이전트가 따르는 규칙과 레퍼런스. 필요할 때 찾아보세요.
일반▾
인터페이스▾
관측성▾
성능▾
네이티브▾
개발▾

Docker

빌드된 앱 하나와, 공장 한쪽에 놓인 작은 기기가 있다고 해 봅시다. 정전 뒤에도 데이터를 그대로 지닌 채 다시 올라와야 하고, 왜 멈췄는지도 볼 수 있어야 합니다.
이런 작은 edge 서버는 Akan 앱 컨테이너 하나로 시작합니다. 이미지의 기본값은 다음과 같습니다.
PORT=8282
앱이 8282 포트에서 요청을 받으므로 8282:8282로 게시합니다.
/workspace/sqlite
sqlite 파일이 여기 놓이므로, 재시작해도 남도록 볼륨을 마운트합니다.
AKAN_LOG_TO_FILE=0
파일 로깅이 꺼져 있으므로, stdout을 수집하거나 1로 켜고 로그 볼륨을 마운트합니다.
ca-certificatestzdata
설치되는 패키지는 이 둘뿐이므로, ffmpeg이나 Chromium은 docker.preRuns에 적습니다.
console.js
main.js 옆에 들어 있으므로 docker exec로 운영 콘솔을 엽니다.
파일 로깅을 끈 이유: 컨테이너의 쓰기 레이어는 컨테이너와 함께 사라지므로, 파일은 디스크만 채웁니다. 로그는 stdout으로 수집합니다.

최소 compose 파일

앱 하나를 위한 단순한 compose 파일입니다. myapp과 이미지 이름을 자기 앱에 맞게 바꾸세요.
docker-compose.yaml
  • restart: unless-stopped는 정전이나 재부팅 뒤에 컨테이너를 다시 띄웁니다.
  • 볼륨 두 개가 sqlite 파일과 로그 파일을 호스트에 남깁니다.
  • AKAN_PUBLIC_OPERATION_MODE: edge는 온프레미스 기기라는 표시입니다. 이미지 기본값은 cloud입니다.
  • AKAN_DATABASE_MODE: single은 모든 데이터를 볼륨의 SQLite 파일에 둡니다. 앱이 single만 선언했다면 빼도 됩니다.

컨테이너 환경변수

이미지에는 빌드 때의 값이 이미 들어 있습니다. 그래서 컨테이너에는 달라지는 값만 주면 됩니다.
필수 — 빌드가 채워 둠
이 셋이 없으면 앱이 뜨지 않습니다.
AKAN_PUBLIC_APP_NAMEstring기본값 빌드 값
앱의 코드네임입니다.
AKAN_PUBLIC_REPO_NAMEstring기본값 빌드 값
workspace 이름입니다.
AKAN_PUBLIC_SERVE_DOMAINstring기본값 빌드 값
앱이 자기 origin을 만들어 내는 기준 도메인입니다.
자주 바꾸는 값
AKAN_PUBLIC_ENVlocal | testing | debug | develop | main기본값 빌드 값 (debug)
어느 배포인지입니다. Helm chart는 네임스페이스마다 이 값을 넣습니다. 이미지에는 빌드할 때 고른 환경의 서버 env만 들어 있으므로, 그 환경으로만 실행됩니다.
AKAN_PUBLIC_OPERATION_MODElocal | edge | cloud | module기본값 이미지에서는 cloud
어디서 도는지입니다. 이 페이지 예시의 온프레미스 기기는 edge입니다.
AKAN_DATABASE_MODEsingle | multiple | cluster기본값 앱이 선언한 유일한 모드
database.modes에 선언한 모드 중 하나를 고릅니다. 앱이 여러 모드를 선언했다면 꼭 줘야 합니다.
PORTnumber기본값 8282
gateway 또는 단독(solo) 프로세스가 바인딩하는 포트입니다.
AKAN_SQLITE_DIRstring기본값 이미지에서는 /workspace/sqlite
sqlite 파일이 놓이는 곳입니다. 마운트한 볼륨을 가리키게 하세요.
AKAN_LOG_TO_FILE0 | 1기본값 이미지에서는 0
순환 로그 파일입니다. 1로 두고 AKAN_LOG_DIR을 마운트하면 다시 켜집니다.
AKAN_LOG_DIRstring기본값 이미지에서는 /workspace/runtime/logs
파일 로깅을 켰을 때 순환 로그 파일이 놓이는 곳입니다.
AKAN_CONSOLE1기본값 미설정
production 계열 환경에서 console.js를 열 때 필요합니다.
데이터가 있는 곳
데이터와 업로드 파일이 어디 있는지 정합니다. 컨테이너에 주면 env.server.ts에서 번들된 같은 값보다 우선하므로, 이미지 하나로 모든 배포를 돌릴 수 있습니다.
SQLITE_DATABASE_PATHstring기본값 <AKAN_SQLITE_DIR>/<app>-<env>.dbsingle · multiple
SQLite 데이터베이스 파일입니다. multiple에서는 호스트의 모든 컨테이너가 이 파일 하나를 엽니다.
AKAN_SOLID_DB_PATHstring기본값 <AKAN_SQLITE_DIR>/<app>-<env>_solid.dbsingle
캐시, 큐, pubsub을 담는 SQLite 파일입니다.
REDIS_URIstringmultiple · cluster
캐시, 큐, pubsub을 맡는 Redis입니다. 꼭 필요하며, rediss://로 쓰면 TLS로 연결합니다.
POSTGRES_URLstringcluster
Postgres 데이터베이스입니다. 커넥션 풀 크기, SSL, prepared statement는 쿼리 문자열로 정합니다.
AKAN_STORAGE_SHAREDtruemultiple · cluster
업로드가 쌓이는 /workspace/local이 모든 인스턴스가 함께 마운트한 볼륨 하나라고 알립니다.
  • transaction 모드의 PgBouncer 뒤에서는 &prepare=false도 그 쿼리 문자열에 더합니다.
  • Postgres는 값을 나눠서 줄 수도 있습니다. URL이 없으면 POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DATABASE(또는 POSTGRES_DB), POSTGRES_USER, POSTGRES_PASSWORD를 읽습니다.

AKAN_REPLICA로 확장

AKAN_REPLICA는 쉼표로 구분한 세 개의 수이고, 자리마다 역할이 정해져 있습니다. 프로세스를 몇 개 띄울지, 그 앞에 gateway를 둘지를 함께 정합니다.
이 페이지에서 쓰는 말
replica
요청을 처리하거나, 백그라운드 작업을 돌리거나, 둘 다 하는 서버 프로세스 하나입니다.
gateway
PORT를 잡고 들어온 트래픽을 replica들에 나눠 주는 앞단 프로세스입니다.
solo
gateway 없이 replica 하나가 컨테이너의 유일한 프로세스로 도는 상태입니다.
internal
시그널이 선언하는 백그라운드 작업입니다. cron, interval, 큐 작업 등이 있습니다.
RSC worker
페이지를 렌더링하는 별도 프로세스입니다. 웹을 서빙하는 replica마다 하나씩 붙습니다.
세 자리
자리역할기본값
↳ 하는 일
1federation0
요청을 처리합니다. serverMode: "batch"로 고정한 internal은 돌리지 않습니다.
2batch0
internal만 돌리고 요청은 받지 않습니다. 하나라도 있으면 gateway가 반드시 남습니다.
3all1
요청 처리와 internal을 모두 맡는 범용 replica입니다.
값 예시
컨테이너가 요청을 처리하는지, serverMode: "batch"로 고정한 internal이 도는지를 값별로 정리했습니다.
AKAN_REPLICA
요청 처리
batch internal
solo — 프로세스 하나, gateway 없음
(미설정)
✓
✓
"0,0,1"과 같습니다. 범용 replica 하나입니다.
"1,0,0"
✓
federation replica 하나입니다.
"0,0,0"
✓
✓
범용 replica 하나로 바뀝니다. 0개를 요청할 수는 없습니다.
gateway가 앞에 있음
"2"
✓
federation replica 두 개입니다. 빠진 자리는 0으로 칩니다.
"0,1,0"
✓
batch replica 하나입니다. health check에 응답하도록 gateway가 남습니다.
✓처리함처리 안 함
solo 또는 gateway
프로세스 하나, 또는 gateway와 자식 프로세스
total = 1이고 batch = 0?
Solo: 컨테이너의 유일한 프로세스PORT에 바인딩
Gateway: PORT를 잡고 프록시
RSC worker
federation 자식unix socket
batch 자식요청을 받지 않음
RSC worker
  • solo는 gateway를 건너뜁니다. 요청 처리 replica가 하나면 나눌 대상이 없으므로, 그 replica가 프록시 홉 없이 컨테이너의 유일한 프로세스로 돕니다.
  • gateway 되살리기. AKAN_SOLO=false(또는 0)를 주세요. true는 아무 효과가 없습니다. akan start와, new AkanApp(...)에 replica를 넘긴 경우에도 gateway가 남습니다.
  • solo는 probe에 직접 응답합니다. /_akan/app/health, /_akan/app/metrics, /_akan/bench/ping이 gateway와 같은 형태로 돌아옵니다.
  • solo 프로세스를 감시하는 것은 오케스트레이터의 probe뿐입니다.

한 호스트에 컨테이너 여러 개

컨테이너 하나로 모자라면 같은 호스트에서 여러 개를 multiple 데이터베이스 모드로 띄웁니다. 호스트 볼륨의 SQLite 파일 하나를 함께 열고, 캐시·큐·pubsub은 Redis 하나를 같이 씁니다.
모드데이터베이스캐시 · 큐 · pubsub
↳ 도는 곳
singleSQLite 파일SQLite 파일
컨테이너 하나
multiple호스트 볼륨의 SQLite 파일 하나Redis
호스트 하나의 여러 컨테이너
clusterPostgresRedis
여러 서버
앱의 빌드가 이 모드를 선언해야 합니다. 예를 들어 akan.config.ts에 database: { modes: ["multiple"] }를 적습니다. 그다음 이런 compose 파일로 이미지를 띄웁니다:
docker-compose.yaml
  • 모든 replica가 데이터베이스 파일 하나를 씁니다. SQLITE_DATABASE_PATH가 컨테이너마다 app-data 볼륨의 같은 파일을 가리킵니다.
  • REDIS_URI는 꼭 필요합니다. 캐시, 큐, pubsub이 그 Redis에 있고, 배포된 앱에는 대신 쓸 기본값이 없습니다.
  • 업로드 파일은 모든 replica가 마운트한 볼륨에 둡니다. app-files를 /workspace/local에 붙이고 AKAN_STORAGE_SHARED로 알립니다. 이것도 오브젝트 스토리지도 없으면 앱은 컨테이너 하나의 디스크에 파일을 두기를 거부합니다.
  • 앞단의 리버스 프록시가 replica들에 요청을 나눕니다. replica는 따로 포트를 게시하지 않습니다.

웹 기능 덜어내기

API만 응답하는 배포에는 웹 절반이 필요 없습니다. 빌드에서 빼면 이미지가 줄고, 부팅 때 끄면 프로세스가 줄어듭니다.
설정
SSR
CSR
akan.config.ts — 빌드가 이미지에 넣는 것
web: true
✓
✓
기본값입니다. 두 가지를 모두 빌드합니다.
web: { csr: false }
✓
모바일 SPA 번들만 빼고 SSR은 남깁니다.
web: false
API 전용입니다. 라우트 산출물, CSR 번들, RSC worker 진입점, public/이 모두 빠집니다.
컨테이너 env — 부팅 때 좁히기
AKAN_CSR=false
✓
모바일 SPA 번들만 내립니다.
AKAN_SSR=false
RSC worker와 렌더 라우트를 내리고, CSR도 함께 내립니다.
✓제공꺼짐
  • env는 좁히기만 합니다. false나 0으로 끌 수는 있지만, 빌드에서 뺀 것을 다시 켤 수는 없습니다.
  • SSR 없는 CSR은 없습니다. CSR 번들은 SSR 빌드가 만든 스타일시트를 품고 있으므로, AKAN_CSR이 무엇이든 AKAN_SSR=false는 CSR까지 내립니다.
  • web: false는 이미지를 줄입니다. 이 문서 앱에서 재 보니 86MB가 6.2MB로 줄었습니다.
요청 처리 replica 하나만 도는 API 전용 컨테이너는 이렇게 띄웁니다.
Terminal
  • AKAN_SSR=false는 RSC worker 프로세스를 없앱니다. 이 문서 앱에서는 프로세스 3개 350MB가 2개 120MB로 줄었습니다.
  • "1,0,0"은 federation replica 하나입니다. serverMode: "batch"로 고정한 internal은 이 컨테이너에서 돌지 않습니다.

이미지 구성하기

Dockerfile은 직접 쓰지 않습니다. akan.config.ts의 docker 키로 이미지를 정합니다. 이미지가 설치하는 것은 ca-certificates와 tzdata뿐이라서, ffmpeg이나 Chromium이 필요한 앱은 직접 적어야 합니다.
imagestring | { amd64?, arm64? }기본값 oven/bun:1-slim
베이스 이미지입니다. 객체 형태는 아키텍처마다 고르고, 빠진 아키텍처는 기본값을 씁니다.
preRuns(string | { amd64?, arm64? })[]기본값 []
bun install 전에 실행할 명령입니다. 네이티브 의존성에 필요한 시스템 패키지 설치 등이 들어갑니다.
postRuns(string | { amd64?, arm64? })[]기본값 []
bun install 뒤, 앱 파일을 복사하기 전에 실행할 명령입니다.
commandstring[]기본값 ["bun", "main.js"]
컨테이너의 CMD입니다.
ffmpeg이 필요하고, arm64에서만 도는 단계가 하나 있는 앱의 예시입니다.
apps/myapp/akan.config.ts
  • RUN 없이 명령만 적습니다. Akan이 단계마다 RUN을 붙이고, 객체 형태는 TARGETARCH가 맞을 때만 실행합니다.
  • lib도 자기 단계를 보탭니다. lib을 마운트한 앱은 그 단계를 앞쪽에 중복 없이 물려받습니다. lib이 베이스 이미지나 command를 고르지는 않습니다.
  • 안 쓰는 폰트는 덜어냅니다. assets.pruneFonts는 기본으로 켜져 있고, dist에 복사된 public/에서만 덜어냅니다. 원본 트리는 건드리지 않습니다.
  • keepFonts는 자기 public/ 기준입니다. glob은 그것을 선언한 앱이나 lib의 폴더 기준입니다. lib은 assets 중 이 키만 정할 수 있습니다.

콘솔 열기

이미지에는 main.js 옆에 console.js가 들어 있어서, 컨테이너 안에 파일을 만들지 않고도 운영 콘솔을 열 수 있습니다. AKAN_CONSOLE=1은 exec 명령에만 주세요.
Terminal
  • 플래그가 필요한 이유. AKAN_PUBLIC_ENV가 main이거나, 운영 모드가 cloud·edge이거나, NODE_ENV가 production이면 콘솔이 열리지 않습니다.
  • 그래서 이미지에서는 늘 필요합니다. 이미지가 NODE_ENV=production과 AKAN_PUBLIC_OPERATION_MODE=cloud를 넣어 두기 때문입니다. compose 파일에는 넣지 말고, exec 한 번에만 콘솔이 열리게 하세요.
  • 앱의 백그라운드 작업을 두 번 돌리지 않습니다. 콘솔 프로세스는 서비스는 띄우지만 internal 작업과 큐 worker는 돌리지 않으므로, 그 일은 실행 중인 컨테이너가 계속 맡습니다.

꿀팁

  • 처음 compose 파일은 단순하게. 앱이 정말 필요로 할 때만 서비스를 더하세요.
  • edge 장비를 바꾸기 전에 sqlite 볼륨을 백업하세요. 그 안에 <app>-<env>.db와 <app>-<env>_solid.db가 있습니다.
  • 재시작이 반복되면 stdout을 보세요. 마운트한 폴더가 아니라 docker logs myapp을 읽으세요. 직접 켜지 않았다면 이미지의 파일 로깅은 꺼져 있습니다.
이어서 읽기

MIT 라이선스 하에 배포되었습니다.

내 AI에 이 문서 연결하기

MCPhttps://akanjs.com/mcp
Copyright © 2026 Akan.js 모든 권리 보유.시스템 관리자bassman