서버 셋업
서버에 Bun 1.3+가 필요하고, Docker는 컨테이너 앱을 배포할 때만 있으면 됩니다. 그다음:
$ git clone https://github.com/chussum/arcturus.git && cd arcturus $ bun run server:up
이게 전부입니다 — 의존성 설치, 대시보드 빌드, pm2 기동까지 알아서 하고, 전 과정은 여러 번 실행해도 안전합니다. http://<host>:7777을 여세요.
첫 실행이면서 암호화된 .env.secrets가 아직 없으면 server:up이 프로덕션 시크릿 설정 방법을 세 가지로 물어봅니다 — 자동 생성, ARCTURUS_ENV_KEY·ARCTURUS_JWT_SECRET 직접 입력, 또는 환경 변수로 주입하는 방법 안내만 보기 — 그 다음 터미널에서 어드민 비밀번호를 입력받습니다.
프롬프트를 건너뛰려면 키(또는 ARCTURUS_ADMIN_PASSWORD)를 미리 설정해 두세요. 터미널이 없으면(CI/pm2) 반쯤 설정된 채 기동하지 않고 안내만 출력한 뒤 종료합니다.
Docker 설치 (Docker Desktop 불필요)
Arcturus는 Docker API 소켓에 직접 통신하므로 Docker Desktop이 전혀 필요 없습니다 — Desktop은 직원 250명 이상 또는 연매출 $10M 이상 기업에서 유료 구독이 필요하지만, Docker Engine과 Colima는 상업적 사용에도 무료인 오픈소스입니다.
# Linux 서버 — Docker Engine (Apache 2.0): $ curl -fsSL https://get.docker.com | sh $ sudo usermod -aG docker $USER # 이후 재로그인
# macOS — Colima (MIT) + docker CLI: $ brew install colima docker $ colima start
macOS에서 docker 명령은 Colima 소켓을 알아서 찾지만, Arcturus는 docker context 대신 DOCKER_HOST 환경 변수를 읽습니다.
apps/api/.env가 아닌 ~/.zshrc에 추가한 뒤 터미널을 새로 열거나 source ~/.zshrc를 실행하세요:
export DOCKER_HOST=unix://$HOME/.colima/default/docker.sock
서버 운영
| 명령 | 설명 |
|---|---|
| bun run server:up | 설치 → 빌드 → 기동 (여러 번 실행해도 안전) |
| bun run server:up -- --build | 위와 동일하지만 대시보드를 강제 재빌드 — git pull 후 코드 변경을 반영할 때 사용 |
| bun run server:down | 전체 중지 |
| bun run server:status | 프로세스 상태 |
| bun run server:logs | 로그 팔로우 |
시크릿 하드닝 (macOS)
기본 상태에서는 자동 생성된 키 두 개(세션 서명, env 암호화)가 data/ 아래 평문 파일로 남습니다 — 체험용으로는 충분하지만 오래 운영하는 서버에는 적합하지 않습니다. 명령 하나로(여러 번 실행해도 안전) 레포 루트의 dotenvx 암호화 .env.secrets로 옮기고, 복호화 키는 macOS Keychain에만 보관합니다:
$ bun run secrets:init
기존 값은 그대로 이전되므로 암호화된 앱 env 변수와 로그인 세션이 유지되며, 키도 파일도 없으면 새로 생성합니다. 첫 프로덕션 기동 때 server:up이 대신 실행해 주며, 생성 대신 값을 직접 입력하려면 --input을 붙이세요. 이후 시크릿을 추가·수정하려면 .env.secrets에 평문 KEY=값으로 적은 뒤 재암호화하고 재시작하세요:
$ bun run secrets:update $ bun run server:restart
다른 머신으로 이전하기 전에 Keychain 키를 백업하세요:
security find-generic-password -s arcturus-dotenvx -w
Linux에는 Keychain이 없으니 ARCTURUS_JWT_SECRET / ARCTURUS_ENV_KEY를 직접 주입하거나 파일 fallback을 그대로 쓰세요. 자세한 내용은 보안 참고에 있습니다.
CLI 설치
게이트웨이가 인스톨러와 플랫폼별 바이너리를 직접 서빙합니다. 서버에서 한 번만 빌드해 두면, 팀원 누구나 명령 한 줄로 설치합니다:
# 서버에서, 한 번만: $ cd apps/cli && bun run build:all
# 어느 머신에서든: $ curl -fsSL http://<host>:7777/install.sh | sh
대시보드에서 API 토큰을 만들고 (Dashboard → API Tokens) 로그인하세요:
$ arcturus login --server http://<host>:7777 --token <arc_...>
첫 배포
프로젝트 디렉터리에서 명령 한 번. 그다음 일어나는 일은 내용물에 따라 갈립니다:
- Dockerfile 없음 → 정적 사이트로 배포, 업로드가 끝나는 순간 라이브.
.html파일 하나면 자동으로 index가 됩니다. - Dockerfile 있음 → 이미지를 빌드해 컨테이너로 실행, 전용 포트와 함께 자동 라우팅.
$ arcturus deploy ✦ blog — packing 42 files (1.2 MB) ✦ uploading … done, release ready in 0.4s ● live at http://<host>:7778/alice/blog
기본 앱 이름은 디렉터리 이름입니다. 코드 옆에 arcturus.json을 두면 프로젝트별로 이름을 고정할 수 있습니다:
{ "name": "my-app" }
.env 자동 등록 (컨테이너 앱)
Dockerfile이 있는 디렉터리를 배포하면, 옆에 있는 .env 파일을 읽어 변수들을 앱 환경 변수로 암호화 저장하고 컨테이너에 주입합니다 — 손으로 입력하는 것과 똑같이. 단, 앱 최초 생성 시에만 적용되고, 재배포 때는 기존 env를 건드리지 않습니다(이후 변경은 arcturus env 또는 대시보드에서). 모든 .env* 파일(.env, .env.local, .env.production …)은 빌드 이미지에서 항상 제외되어 시크릿이 이미지 레이어에 남지 않습니다. 값은 읽으면서 정리됩니다: 앞뒤 공백을 잘라내고, 뒤에 붙은 인라인 주석(TOKEN=abc # 메모처럼 공백 뒤의 #)은 떼어냅니다 — 공백 없이 붙은 #(#fff, ab#cd, URL fragment)는 그대로 두고, 값을 따옴표로 감싸면 리터럴 #나 양끝 공백을 유지합니다. arcturus env --set도 동일하게 정리됩니다.
arcturus deploy 옵션
--name <appName>— 앱 이름 (기본값:arcturus.json의name, 없으면 디렉터리 이름)--name <owner>/<appName>— manage 권한이 있는 다른 계정의 앱에 배포 (해당 앱이 이미 존재해야 함)--dir <path>— 배포할 프로젝트 디렉터리 (기본값:.)--env-file <path>— 새 컨테이너 앱에 넣을 env 파일 (기본값: 디렉터리의.env)--no-env-file—.env파일을 읽지 않음
CLI 없이 쓰고 싶다면? 대시보드에서 zip 업로드를 받습니다 — 같은 파이프라인, 같은 결과.
일상 운영
환경 변수
저장된 값으로 컨테이너를 즉시 재생성 — 재빌드 없음. SQLite에 AES-256-GCM으로 암호화되어 보관됩니다.
$ arcturus env my-app --set DATABASE_URL=postgres://…
히스토리 & 롤백
앱마다 최근 5개 릴리스를 보관합니다(ARCTURUS_KEEP_RELEASES). 정적 릴리스는 버전 디렉터리, 컨테이너 릴리스는 배포별 이미지 태그 — 어느 쪽이든 롤백은 즉시입니다.
$ arcturus deployments my-app # 히스토리, live / rollback 가능 마커 $ arcturus rollback my-app <id> # 이전 릴리스로 복원
로그 & 정리
$ arcturus logs my-app # 컨테이너 로그 팔로우 $ arcturus destroy my-app # 앱 삭제
터미널에서 docker 직접 접근
컨테이너 앱은 docker CLI로 실행 중인 컨테이너에 바로 접근할 수 있어요 — 로그를 보거나 내부를 둘러볼 때 편합니다. 컨테이너 이름은 항상 arcturus--<사용자명>--<앱> 형식으로 정해지므로 따로 ID를 찾을 필요가 없어요.
호스트에서 — 서버에 SSH로 접속한 뒤:
$ docker ps --filter label=arcturus.managed=true # Arcturus 앱 컨테이너 전체 목록 $ docker logs -f arcturus--alice--my-app # 특정 앱 로그 팔로우 $ docker exec arcturus--alice--my-app ls -al # 일회성 명령 실행 $ docker exec -it arcturus--alice--my-app sh # 내부 셸 진입
서버에 로그인하지 않고 노트북에서 — 로컬 docker를 SSH로 서버에 연결하면 같은 명령을 그대로 쓸 수 있어요. <ssh-user>는 서버에 SSH로 접속하는 로그인 계정(Arcturus 사용자명이 아니라 OS 계정)으로 바꾸세요:
$ export DOCKER_HOST=ssh://<ssh-user>@<server> # 셸마다 한 번 $ docker logs -f arcturus--alice--my-app # …이제 서버 대상으로 실행 $ ssh <ssh-user>@<server> docker logs -f arcturus--alice--my-app # 또는 설정 없이 일회성
노트북에 docker CLI가 있어야 하고, 서버에서 docker를 실행할 수 있는 SSH 접근이 필요해요. 이미지에 셸이 있어야 합니다 — 대부분의 베이스는 sh가 되지만 distroless/scratch 이미지에는 셸이 없어요. 컨테이너는 이미지의 USER(보통 root)로 실행됩니다.
서버에 ARCTURUS_SSH_USER를 설정하면 대시보드의 터미널 접근 안내가 <ssh-user> 자리에 그 로그인 계정을 채워서 보여줘요.
팀 계정
admin이 대시보드에서 초대 링크를 발급하면, 멤버는 그 링크로 가입해 자기 앱을 직접 관리합니다. 역할은 단순하게 admin과 member 둘뿐.
앱 공유
앱 소유자(또는 admin)는 앱 상세 페이지에서 특정 팀원 또는 전체 팀과 앱을 공유할 수 있습니다. 두 가지 접근 권한:
- 읽기 전용 — 앱 카드, 상태, URL, 배포 히스토리를 볼 수 있어요. 환경 변수와 실시간 로그는 숨겨집니다.
- 관리 가능 — 읽기 전용의 모든 것 + 새 버전 배포, 환경 변수 편집, 메모리/라우팅 설정, 중지, 재시작, 롤백. 삭제와 공유 설정 변경은 소유자/admin만 가능해요.
manage 권한자는 공유받은 앱에 배포할 수 있습니다 — arcturus deploy --name <owner>/<app> 또는 대시보드 재배포 버튼.
삭제와 공유 설정은 소유자/admin만 가능해요.
라우팅 모드
앱은 게이트웨이 뒤 /<username>/<app-name>에서 서빙됩니다. 경로 프리픽스 프록시는 절대 경로로 에셋을 참조하는 앱을 깨뜨리기 때문에, Arcturus는 세 겹의 해법을 쌓습니다:
- 프리픽스 제거 +
X-Forwarded-Prefix— base path를 설정할 수 있는 앱은 그대로 동작. - Referer 폴백 — 매칭되지 않은 요청(
/static/app.js)은 브라우저가 보고 있던 앱으로 재라우팅. - 전용 포트 — 모든 컨테이너 앱은 고정 포트(
30000+)를 유지, base path 문제 없음.
새 컨테이너 앱은 “redirect” 라우팅 모드가 기본입니다 — 방문자를 전용 포트로 보내므로 base path 문제가 애초에 생기지 않습니다.
경로 기반 서빙을 원하면 앱 상세에서 “proxy”로 전환하세요.
전용 포트는 자동 할당되지만, 앱 상세의 포트 설정에서 원하는 포트(예: 8080)를 직접 지정할 수 있습니다 — 포트가 비어 있는지 확인하고 컨테이너를 새 포트로 다시 만들며, 자동 할당으로 되돌릴 수도 있습니다.
설정 레퍼런스
예시 파일을 복사해 수정하세요 — 모든 값이 선택이고, 합리적인 기본값이 적용됩니다:
$ cp apps/api/.env.example apps/api/.env $ cp apps/web/.env.example apps/web/.env
| 환경 변수 | 기본값 | 설명 |
|---|---|---|
| ARCTURUS_PORT | 7777 | 제어 평면 포트 (API + 대시보드) |
| ARCTURUS_APPS_PORT | 7778 | 배포된 앱 전용 오리진 (앱 코드를 제어 평면 쿠키에서 격리) |
| ARCTURUS_DATA_DIR | ./data | SQLite, 정적 릴리스, 빌드 워크스페이스 |
| ARCTURUS_PORT_POOL_START/END | 30000 / 30999 | 컨테이너 앱용 전용 포트 풀 |
| ARCTURUS_MAX_UPLOAD_MB | 256 | 업로드 크기 제한 |
| ARCTURUS_KEEP_RELEASES | 5 | 앱별 롤백용 보관 릴리스 수 |
| ARCTURUS_CLI_DIST | apps/cli/dist/cli | 서빙할 크로스컴파일 CLI 바이너리 경로 |
| ARCTURUS_DEV_ORIGINS | — | next dev에 추가 허용할 origin |
| ARCTURUS_SSH_USER | — | 대시보드 터미널 접근 안내에 채워 넣을 SSH 로그인 계정(Arcturus 사용자명이 아니라 호스트 OS 계정) |
보안 노트
시크릿과 키
- 앱 env 변수는 암호화해 저장합니다 (AES-256-GCM, SQLite). 키는
ARCTURUS_ENV_KEY를 쓰고, 없으면 한 번 생성해data/env-key에 둡니다. 예전에 평문으로 저장된 값이 있어도 다음 부팅 때 자동으로 암호화됩니다. - 파일 권한은 부팅 때마다 강제합니다 —
data/는0700, DB와 키 파일은0600. - 자동 생성된 평문 키 파일은 체험용 편의 장치입니다. 오래 운영하는 서버라면
bun run secrets:init으로 암호화 파일 + Keychain 보관으로 옮기세요 — 방법은 시크릿 하드닝에 있습니다.
ARCTURUS_ENV_KEY를 분실하거나 변경하면 기존 env 값은 복호화할 수 없습니다. 데이터 디렉터리와 함께 키도 백업하세요.
secrets:init 이후에는 키가 Keychain에 있습니다 — 아래로 꺼내 백업합니다:
security find-generic-password -s arcturus-dotenvx -w
계정과 토큰
- 로그인·가입·API 토큰 생성은 IP당 분당 10회로 제한됩니다. 비밀번호를 계속 찍어보는 공격을 막기 위해서입니다. 배포는 분당 20회입니다.
- 로그아웃하면 세션이 서버에서도 즉시 폐기됩니다. 쿠키 값이 어딘가로 새어 나갔더라도 로그아웃 뒤에는 쓸 수 없습니다.
- 비밀번호 변경과 어드민 재설정 링크는 모두 전 세션을 폐기합니다. 멤버는 대시보드 → Account에서 현재 비밀번호를 확인 후 변경할 수 있으며, 다른 세션은 즉시 무효화됩니다. 어드민은 Team 페이지에서 팀원에게 1회용 재설정 링크를 발급할 수 있고 — 1시간 유효, 1회 사용 후 해당 계정의 모든 세션이 폐기됩니다.
- API 토큰에 만료를 둘 수 있습니다. 생성할 때 30/90/365일 또는 무기한을 고릅니다. 만료된 토큰은 인증에서 거부되고, 수동 폐기는 언제든 가능합니다.
컨테이너 격리
- 배포 컨테이너는 권한을 최소화해 실행됩니다. 모든 Linux capability 제거,
no-new-privileges, CPU/PID 제한, 앱별 메모리 상한(대시보드에서 설정, 상한선은ARCTURUS_MAX_MEMORY_MB). 이미지 빌드에도 같은 메모리 상한이 걸립니다. - 팀원 앱끼리는 서로 닿지 못합니다. 모든 앱은 컨테이너 간 통신을 끈 전용 브리지 네트워크에서 돕니다. 단, 컨테이너에서 호스트로 나가는 방향까지 막지는 못하니 배포되는 코드는 팀이 신뢰할 수 있어야 합니다.
- 컨테이너는 기본적으로 이미지의
USER를 따릅니다 (대개 root).ARCTURUS_CONTAINER_USER(예1000:1000)를 설정하면 비root 사용자로 강제합니다. - 컨테이너 앱은 전용 호스트 포트로도 직접 열립니다. 게이트웨이 경로만이 아니라요 — “전용 포트로 이동” 접근 모드가 여기에 의존하는 의도된 동작입니다. 포트 풀은 팀 네트워크로 방화벽 처리하세요.
공급망
- 의존성 설치는 올라온 지 7일이 안 된 npm 버전을 건너뜁니다 (
bunfig.toml의minimumReleaseAge). 오염된 릴리스가 이 레포에 닿기 전에 발견·회수될 시간을 법니다. 급한 패치는minimumReleaseAgeExcludes로 패키지별 예외 처리하세요.
트러블슈팅
경로 프리픽스 아래에서 앱 에셋이 404
절대 경로 문제입니다 — 라우팅 모드를 보세요. 이 문제는 “proxy” 모드에서만 나타납니다. 컨테이너 앱은 “redirect”가 기본이니, 앱을 다시 “redirect”로 돌리면 전용 포트에서 서빙됩니다.
재시작 후 포트가 이미 사용 중
프로세스가 남아 EADDRINUSE가 나면, 이름이 아니라 포트 기준으로 정리하세요:
$ lsof -ti :7777 | xargs kill -9
admin 비밀번호를 잃어버렸어요
맨 첫 부팅 때 설정합니다 — 터미널에서 직접 입력하거나, 미리 ARCTURUS_ADMIN_PASSWORD로 지정합니다. 다른 어드민이 있다면 Team 페이지에서 1회용 재설정 링크를 보내줄 수 있습니다.
서버 이전 후 배포가 실패해요
data/ 디렉터리만 옮기고 env 키를 안 옮겼다면, env가 저장된 컨테이너 앱은 복호화에 실패합니다 — 백업에서 data/env-key(또는 ARCTURUS_ENV_KEY)를 복원하세요. 이전 머신에서 secrets:init을 썼다면 .env.secrets도 함께 복사하고 Keychain 키를 옮기세요: 이전 머신에서 security find-generic-password -s arcturus-dotenvx -w로 읽고, 새 머신에서 security add-generic-password -s arcturus-dotenvx -a "$USER" -w "<키>"로 저장합니다.