Deploy 가이드
Git 저장소 주소만 알려 주면 빌드부터 배포, HTTPS 주소까지 Deploy 가 알아서 해요. 서버나 쿠버네티스를 몰라도 괜찮아요. 이 가이드는 배포 로봇 디플이와 함께, 폼의 각 칸이 무슨 뜻이고 막히면 어디를 봐야 하는지 차근차근 알려 드려요.

push 한 번에 무슨 일이 일어나는지, 이 네 장면이 전부예요.

1코드를 건네요
Git 저장소 주소를 알려 주면 디플이가 소스를 받아 가요.

2컨테이너로 포장해요
어디서든 똑같이 돌아가게 앱을 이미지로 만들어요. 이게 ‘빌드’예요.

3서버에 올려요
새 버전이 준비된 걸 확인한 다음 옛 버전과 바꿔요. 이게 ‘배포’예요.

4주소가 열려요
HTTPS 주소가 바로 생겨요. 웹훅을 달아 두면 push 할 때마다 반복돼요.
가이드에 계속 나오는 열 단어예요. 이것만 알면 나머지는 술술 읽혀요.
01
시작하는 길은 세 가지예요. 어느 길로 들어와도 만들어진 뒤에는 똑같이 동작해요(주소·배포·로그·요금 전부).

Git 저장소에서 빌드추천
템플릿12개
이미지 직접 지정
템플릿은 vLLM, TGI, Ollama, Open WebUI, JupyterLab, Node, FastAPI, 정적 사이트, Postgres, Redis, whoami, code-server 가 있어요. 새 프로젝트 첫 화면이 바로 이 카탈로그예요.
https://<주소>.apps.core.today 가 열려요. 빌드 탭에서 진행 상황이 실시간으로 보여요.팁배포가 잘 되는지만 먼저 보고 싶다면
Claude Code·Cursor 같은 코딩 에이전트에게 “Deploy 에 올려 줘”라고 맡길 수 있어요. 에이전트가 읽을 안내문이 /llms.txt 에 있어서, 저장소를 배포할 수 있게 고치고, 프로젝트·데이터베이스를 만들고, 빌드가 실패하면 로그를 읽어 다시 고쳐요. 사람은 처음에 “승인” 한 번만 누르면 돼요(Vercel 의 vercel login 과 같아요).
deploy.core.today/device?code=… 모양이에요. 화면의 8자리 코드가 에이전트가 보여 준 코드와 같은지 보고, 배포할 조직과 유효 기간을 고른 뒤 “승인”을 눌러요.https://deploy.core.today/llms.txt 를 읽고 이 저장소를 Deploy 에 배포해 줘. 프로젝트 이름은 my-shop 으로 하고, 데이터베이스가 필요하면 PostgreSQL 로 만들어 줘.
kc_…)을 쓰세요. 그 프로젝트의 빌드·로그·작업만 열려요. 에이전트 로그인을 쓰지 않으려면, 에이전트에게 “생성 링크를 만들어 달라”고 하면 값이 채워진 새 프로젝트 화면 링크를 줘요.오픈소스 앱은 흔히 docker-compose.yml 한 장에 화면·API·워커·DB 가 함께 들어 있어요. Deploy 는 compose 파일을 그대로 받지 않아요. 프로젝트 하나가 컨테이너 하나라서, 서비스를 하나씩 아래처럼 옮기면 돼요.
| compose 에 있던 것 | Deploy 에서는 |
|---|---|
| 화면·API·워커 서비스 | 서비스마다 프로젝트를 하나씩 만들어요. 공개 이미지가 있으면 “이미지 직접 지정”, 없으면 Git 저장소에서 빌드해요 |
| postgres·redis·벡터 DB 같은 데이터 서비스 | 프로젝트가 아니라 관리형 데이터베이스로 만들어요. 프로젝트로 띄우면 재시작할 때 데이터가 사라져요. → 엔진 고르기 |
volumes (업로드 파일·데이터 폴더) | 프로젝트에는 디스크가 없어요. 앱의 파일 저장 방식을 S3 로 바꾸고(대부분 설정에 있어요) 오브젝트 스토리지나 외부 S3 버킷을 연결하세요 |
서비스 이름으로 부르기 (http://api:8080) | 상대 프로젝트 개요 화면의 “클러스터 내부” 주소로 바꿔요. 포트는 항상 80 이에요 |
depends_on (시작 순서) | 순서를 맞춰 주지 않아요. 데이터베이스 → 백엔드 → 화면 순으로 직접 만드세요 |
command · entrypoint 덮어쓰기 | 폼에는 없어요. FROM 이미지 + CMD […] 두 줄 Dockerfile 로 Git 빌드하세요 |
.env 파일 | 프로젝트 설정의 환경변수 칸에 그대로 붙여 넣고, 다른 서비스를 가리키는 값(호스트·비밀번호)만 고쳐요 |
주의프로젝트마다 공개 주소가 생겨요
<이름>.apps.core.today 주소가 열리고, 끌 수 없어요. 인증이 없는 서비스라면 앱의 토큰·비밀번호 설정을 꼭 켜세요. 가입 화면이 열려 있는 앱도 마찬가지예요. 첫 관리자 계정을 만든 뒤 가입을 막는 설정을 켜 두세요(이름은 앱마다 달라요).<내부 주소>:80 으로 불러요. gRPC 처럼 HTTP 가 아닌 서비스도 같고, 그런 프로젝트는 헬스체크 경로를 비워 두세요.참고템플릿은 ‘폼을 미리 채워 주는 것’일 뿐이에요
FROM 이미지 + CMD […] 두 줄짜리 Dockerfile 을 쓰면 돼요.02
새 프로젝트 폼에서 “이건 어디서 만들죠?” 싶은 것들을 모았어요. 필요한 것만 골라 보세요.

| 이런 상황이면 | 필요한 것 |
|---|---|
| 공개 저장소를 배포 | 없어요. 주소만 있으면 돼요 → 저장소 조건 확인 |
| 비공개 저장소를 배포 | 읽기 전용 토큰(열쇠) → GitHub · GitLab·Gitea |
| push 할 때마다 자동 배포 | 웹훅(초인종) → 웹훅 등록 |
| 빌드 결과·장애를 Slack 으로 | Slack Incoming Webhook → Slack 알림 |
| 내 도메인으로 열기 | 설정에서 도메인 추가 + DNS 에 CNAME 하나 → 커스텀 도메인 |
| docker-compose 로 돌리던 앱을 옮기기 | 서비스별 프로젝트 + 관리형 데이터베이스 → 서비스가 여러 개인 앱 올리기 |
| AI 코딩 에이전트에게 배포 맡기기 | 에이전트가 보여 주는 로그인 링크에서 “승인” → 에이전트에게 배포 맡기기 |
PORT 환경변수의 포트에서 HTTP 를 받아야 해요. 주소는 0.0.0.0 으로 열어 주세요. localhost 로만 열면 밖에서 안 보여요.https://github.com/org/repo (.git 은 빼도 돼요). SSH 주소([email protected]:…)는 지원하지 않아요.비유하면
deploy-core-today). Expiration 은 90일 이상이나 사용자 지정으로 정해요.github_pat_… 로 시작하는 값을 복사해 폼의 “접근 토큰” 칸에 붙여 넣어요. 이 화면을 닫으면 다시 볼 수 없어요.주의만료일을 달력에 적어 두세요
토큰은 비밀값으로 따로 저장되고, 빌드에서 소스를 가져오는 단계에서만 쓰여요.
예전 방식(ghp_…)도 되지만 repo 범위가 필요해서 권한이 넓어요. 가능하면 fine-grained 를 쓰세요. 조직이 SSO 를 쓰면 토큰에 Configure SSO → Authorize 를 해 줘야 해요.
비유하면
application/json, 이벤트는 Just the push event 로 정해요.{"ok":true,"pong":true} 가 돌아오면 연결된 거예요(Recent Deliveries 에서 확인).이제 설정한 브랜치에 push 할 때마다 빌드·배포돼요. GitLab·Gitea 화면과 수동 호출 방법은 빌드 → 웹훅 등록에 있어요.
주의URL 이 새어 나갔다면
https://hooks.slack.com/services/T…/B…/… URL 을 복사해 프로젝트 폼의 “Slack 알림” 칸에 붙여 넣어요.이제 빌드 성공·실패와 앱 장애·복구가 그 채널로 와요. 무엇이 언제 오는지는 알림에서 설명해요.
팁웹훅 하나를 여러 프로젝트에 써도 돼요
플랫폼 주소 <이름>.apps.core.today 는 아무 설정 없이 바로 열려요. 내 도메인을 붙이려면 DNS 레코드 하나만 추가하면 돼요. 관리자 요청은 필요 없어요.
NEXT_PUBLIC_SITE_URL)는 직접 바꿔야 해요.app.example.com)을 넣고 “도메인 추가”를 눌러요. 재시작 없이 바로 반영돼요.CNAME, 이름, 값 cname.core.today)가 나와요. 복사해서 도메인의 DNS 관리 화면에 추가해요. 같은 이름의 A·AAAA 레코드가 있으면 지워요.example.com)은 CNAME 을 못 거는 DNS 가 많아요. www 같은 하위 주소를 쓰거나, CNAME flattening(ALIAS)을 지원하는 DNS 를 쓰세요.*.core.today 주소는 관리자만 붙일 수 있어요. 플랫폼 도메인이라 그래요. 한 주소는 한 프로젝트만 쓸 수 있고, 내 도메인은 프로젝트당 10개까지예요.03
폼의 각 칸이 무엇을 만드는지, 어떤 규칙이 있는지 칸 순서대로 정리했어요.

저장하면 앱이 재시작되나요?
https://<slug>.apps.core.today 예요. 만든 뒤에도 설정의 “도메인”에서 바꾸거나 더할 수 있어요. → 도메인폼에 적는 “컨테이너 포트”는 앱이 요청을 기다리는 포트예요. 같은 값이 PORT 환경변수로도 들어가니, 코드에서 process.env.PORT 를 읽어 열면 포트를 바꿔도 코드를 고칠 필요가 없어요.
X-Forwarded-Proto: https 헤더가 함께 와요.큐를 읽어 처리하는 소비자, 메시지 봇, 주기적으로 도는 루프처럼 요청을 받지 않고 계속 떠 있어야 하는 프로그램은 새 프로젝트의 종류를 “작업자”로 골라요. 빌드·환경변수·크기·복제본·GPU·NAS·로그·롤백은 웹 서비스와 같아요.
PORT 환경변수도 들어가지 않아요.CMD 를 대신해요.비유하면
| 크기 | CPU · 메모리 | 어울리는 것 |
|---|---|---|
| XS | 0.25 vCPU · 256 MB | whoami·정적 사이트·Redis |
| S | 0.5 vCPU · 512 MB | Node·FastAPI 소형 API |
| M | 1 vCPU · 1 GB | 일반 웹 앱·code-server |
| L | 2 vCPU · 4 GB | Open WebUI·Jupyter·메모리를 쓰는 서비스 |
| XL | 4 vCPU · 8 GB | LLM 서빙(vLLM·TGI·Ollama) |
| 2XL | 8 vCPU · 16 GB | 대형 모델 로딩·배치 작업 |
프로젝트 하나가 쓸 수 있는 합계는 CPU 236 · 메모리 880Gi · GPU 4 · Pod 300 예요. 여기에 조직 한도도 함께 검사해요.
비유하면
| 고르는 것 | 어떻게 배치되나요 | 이럴 때 |
|---|---|---|
| 아무 GPU | 통째 GPU 중 빈 자리에 배치돼요. A100 과 V100 이 섞여 있어 어느 쪽이 걸릴지 몰라요 | 종류가 상관없을 때 |
| 통째 GPU (종류 지정) | 그 종류의 GPU 가 있는 서버에만 배치돼요 | 메모리가 큰 모델, 성능이 일정해야 할 때 |
| 조각 | A100 한 장을 나눈 것이에요. 메모리와 연산이 다른 조각과 격리돼요 | 작은 모델·추론·실험. 비용을 아끼고 싶을 때 |
--dtype half 같은 옵션을 함께 쓰세요.주의GPU 서버에 직접 접속해 띄우지 마세요
ollama serve 를 직접 실행하면 플랫폼이 모르는 채로 GPU 메모리를 차지해요. 그 GPU 를 배정받은 다른 프로젝트·작업이 메모리 부족으로 실패하고, 실행한 프로그램이 죽어도 모델이 GPU 에 남을 수 있어요. 아래처럼 프로젝트나 배치 작업으로 GPU 를 배정받아 쓰세요.| 서버에 직접 | 프로젝트·작업으로 | |
|---|---|---|
| GPU | 다른 사람에게 배정된 GPU 와 겹칠 수 있어요 | 배정받은 GPU 만 보여서 서로 부딪히지 않아요 |
| 주소 | 서버 주소와 포트를 직접 알려야 해요 | https://<slug>.apps.core.today 과 같은 조직용 내부 주소 |
| 서버가 멈추면 | 같이 멈춰요 | 통째 GPU 는 빈 GPU 가 있는 다른 서버에서 다시 떠요 |
| 로그·사용량 | 서버 안의 파일로만 남아요 | 로그 탭, GPU 사용률, GPU 시간 집계 |
/v1 에 OpenAI 와 같은 경로를 열어 둬요.# 모델 받기 (끝날 때까지 기다렸다가 {"status":"success"})
curl https://<slug>.apps.core.today/api/pull -d '{"model":"qwen3:8b","stream":false}'
# 받은 모델 · GPU 에 올라간 모델 (size_vram 이 size 와 같으면 전부 GPU 에 올라간 것)
curl https://<slug>.apps.core.today/api/tags
curl https://<slug>.apps.core.today/api/ps
# Python — OpenAI SDK 그대로
from openai import OpenAI
client = OpenAI(base_url="https://<slug>.apps.core.today/v1", api_key="ollama") # 키는 아무 값
stream = client.chat.completions.create(
model="qwen3:8b", stream=True,
messages=[{"role": "user", "content": "안녕하세요"}],
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="")| 모델 (4bit 기준 파일 크기) | 고를 GPU | 메모 |
|---|---|---|
| ~4B (3GB 안팎) | 조각 10GB | 실험·분류·짧은 답변 |
| 7~14B (5~9GB) | 조각 10~20GB | 일반 챗봇·요약 |
| 27~32B (17~20GB) | 조각 40GB, 또는 V100 32GB 통째 | 품질이 필요한 답변 |
| 70B (40GB 이상) | A100 80GB 통째 | 가장 큰 모델 |
GPU 메모리는 모델 파일 크기 + 2~4GB(대화 내용을 담는 캐시) 이상이 필요해요. 모자라면 일부가 CPU 로 넘어가 수십 배 느려지니, /api/ps 로 전부 GPU 에 올라갔는지 확인하세요. 컨텍스트를 길게 늘리면(num_ctx) 캐시도 커져요.
stream: true). 60초 동안 응답이 한 글자도 오지 않으면 연결이 끊기고 504 가 와요. 스트리밍은 글자가 계속 흘러서 몇 분짜리 답변도 괜찮아요.OLLAMA_KEEP_ALIVE(템플릿 기본 30분) 동안 쓰지 않아도 남아 있어요. 계속 올려 두려면 -1 로 바꾸세요.OLLAMA_NUM_PARALLEL(예 4)만큼 함께 처리해요. 늘릴수록 캐시가 그만큼 커져 GPU 메모리를 더 써요.VLLM_API_KEY)을 쓰거나 필요할 때만 켜 두세요(복제본 0 이면 요금이 나가지 않아요).| 방법 | 좋은 점 | 알아 둘 점 |
|---|---|---|
| 띄운 뒤 /api/pull (기본) | 설정이 필요 없어요 | Pod 가 새로 뜰 때마다 모델 크기만큼 다시 받아요 |
| 이미지에 모델 넣기 (Git + Dockerfile) | 재시작·서버 이동 뒤에도 바로 응답해요 | 이미지가 모델만큼 커지고, 빌드 제한 시간(30분) 안에 받아야 해요 |
| NAS 폴더 (NAS 폴더 붙이기) | 여러 프로젝트가 같은 모델 폴더를 써요 | 쓰기 권한이 있는 폴더가 필요해요. 환경변수 OLLAMA_MODELS 에 붙인 경로를 적어요. 첫 로딩은 NAS 속도만큼 걸려요 |
FROM ollama/ollama:0.40.8 RUN (ollama serve >/dev/null 2>&1 &) \ && until ollama list >/dev/null 2>&1; do sleep 1; done \ && ollama pull qwen3:8b # ENTRYPOINT·CMD 는 원본 이미지 것(ollama serve)을 그대로 써요
이 저장소를 Git 프로젝트로 만들고 포트 11434, 헬스체크 /, GPU 를 템플릿과 같이 고르면 돼요.
두 가지 길이 있어요. 모델을 한 번만 올리고 여러 곳에서 쓰려면 첫째, 작업이 끝나면 GPU 까지 반납하려면 둘째예요.
/v1 을 붙여 불러요. 작업 Pod 에는 GPU 가 필요 없어요. 동시 실행 수에 맞춰 Ollama 의 OLLAMA_NUM_PARALLEL 을 올리세요.python3)을 더한 이미지를 Dockerfile 로 만들고, 작업에 GPU 를 붙여 명령에서 Ollama 를 먼저 띄워요.ollama serve >/tmp/ollama.log 2>&1 & until ollama list >/dev/null 2>&1; do sleep 1; done ollama pull qwen3:8b && python3 /app/infer.py --shard "$JOB_COMPLETION_INDEX" --of "$JOB_COMPLETIONS"
Ollama 와 vLLM 중 고르기 — 여러 모델을 바꿔 가며 실험하거나 4bit 모델을 작은 GPU 에 올리려면 Ollama, 공개 API 로 많은 동시 요청을 받아야 하면 API 키와 높은 처리량을 갖춘 vLLM 이 맞아요. V100 에서 vLLM 을 쓸 땐 --dtype half 를 잊지 마세요.
비유하면
/api/health, /health한 줄에 하나씩 KEY=VALUE 로 적어요. 비밀값으로 따로 저장되고 콘솔 DB 에는 남지 않아요. 값을 바꾸면 자동으로 재배포돼요.
NEXT_PUBLIC_* 처럼 빌드 시점에 필요한 값 때문이에요.--build-arg 로 넘기고, Dockerfile 에 ARG KEY 를 선언한 것만 쓰여요.주의Dockerfile 에서 비밀값을 ARG 로 받지 마세요
DATABASE_URL 같은 비밀값은 실행할 때 환경변수로 들어오니 ARG 가 필요 없어요. ARG 는 공개돼도 되는 값에만 쓰세요.FROM node:24-alpine WORKDIR /app ARG NEXT_PUBLIC_API_URL # 공개돼도 되는 값만 COPY package.json package-lock.json ./ RUN npm ci COPY . . RUN npm run build ENV NODE_ENV=production CMD ["npm", "start"] # DATABASE_URL 등 비밀값은 런타임에 env 로 들어옴
| Slack | 이메일 | |
|---|---|---|
| 켜는 법 | Incoming Webhook URL 을 넣어요 (만드는 법) | “이메일 알림”을 켜요 |
| 빌드 성공 | 와요 | 안 와요 |
| 빌드 실패(테스트 실패 포함) | 와요 — 원인 한 줄과 결과 요약(테스트 FAIL 항목 등) | 와요 — 원인·요약·빌드 상세 링크 |
| 앱 장애·복구 | 와요 | 와요 |
| 배치 작업 완료 | 와요 | 안 와요 |
| 배치 작업 실패·제한 시간 초과 | 와요 — 원인 한 줄(메모리 초과면 그렇다고) | 와요 — 원인·작업 목록 링크 |
| 받는 곳 | 웹훅을 만든 채널 | 프로젝트 소유자와 조직 관리자(admin·owner). 발신 [email protected] |
CI·사내 봇처럼 사람이 아닌 시스템이 빌드 결과를 받아야 할 때 써요. 빌드가 끝나면(성공·실패·취소) 정해 둔 https:// 주소로 빌드 결과 JSON 을 보내 줘요. 상태 API 를 계속 물어볼 필요가 없어요.
ping 을 한 번 보내서 2xx 로 답해야 저장돼요.X-Kubecore-Signature 헤더로 우리가 보낸 요청인지 확인해요(HMAC-SHA256, 확인 코드는 설정 카드에 있어요).X-Kubecore-Delivery(빌드 ID)로 걸러 주세요.callbackUrl, API 문서 6.7).비유하면
프로젝트 설정 → “예약 호출”에 한 줄씩 분 시 일 월 요일 경로 를 적어요. 예약 시각이 되면 클러스터 안에서 앱의 그 경로를 GET 으로 불러요(공개 URL 을 거치지 않아요).
# vercel.json: {"crons":[{"path":"/api/cron/sync","schedule":"*/15 * * * *"}]}
*/15 * * * * /api/cron/sync
0 3 * * * /api/cron/cleanup
5,20,35,50 * * * * /api/cron/usage-sync주의시간대는 한국 시간(Asia/Seoul)이에요
CRON_SECRET 환경변수를 두면 Authorization: Bearer <CRON_SECRET> 헤더가 붙어요. Vercel 과 같은 규약이라 핸들러 코드를 바꿀 필요가 없어요. 없으면 헤더 없이 부르니, 내부 호출인지 핸들러가 직접 확인해야 해요.비유하면
프로젝트 설정 → “NAS 폴더” → “폴더 추가”에서 폴더를 고르고, 컨테이너 안 경로(기본 /data/폴더이름)를 정한 뒤 저장해요. 저장하면 롤링 재배포로 적용돼요. 고를 수 있는 폴더는 플랫폼 관리자가 조직에 허용한 범위 안이에요 — 필요한 폴더가 보이지 않으면 관리자에게 요청하세요.
# 앱 코드에서는 일반 폴더처럼 읽어요
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("/data/qwen")USER 로 비루트 사용자(예 uid 10001)가 됐다면 그 폴더가 그 uid 에게 쓰기 가능해야 해요. Dockerfile 의 chown 은 NAS 에 닿지 않으니 관리자에게 요청하세요.연결 절차는 커스텀 도메인 연결에 있어요. 기본 주소 *.apps.core.today 와 브랜치 미리보기 주소는 이미 열려 있어서 아무 설정도 필요 없어요.
04
빌드는 소스 코드를 이미지로 포장해서 배포까지 이어 주는 과정이에요. 세 단계가 순서대로 돌고, 단계마다 로그가 실시간으로 보여요.
1fetch
소스 가져오기
저장소에서 코드를 받아요. Dockerfile 이 없으면 여기서 언어를 감지해요.
2build
이미지 만들기
앱을 이미지로 포장해 프로젝트 전용 레지스트리에 올려요. 테스트도 여기서 돌아요.
3deploy
배포하기
새 이미지로 교체해요. 준비가 확인된 뒤에 옛 버전을 내려요.
실패하면 어느 단계에서 멈췄는지와 원인이 빌드 상세의 “실패 원인” 상자에 나와요. → 문제 해결
지정한 Dockerfile 이 있으면 그대로 써요. 없으면 Railpack(Railway 의 빌더)이 저장소를 보고 언어·프레임워크·패키지 매니저를 알아내 빌드해요. Vercel 의 프레임워크 프리셋과 같은 역할이에요.
| 감지 대상 | 무엇을 보고 | 기본 동작 |
|---|---|---|
| Node.js | package.json | lockfile 로 npm·pnpm·yarn·bun 을 고르고, engines/.nvmrc 로 버전을 정해요. Next·Nuxt·Vite·Astro·SvelteKit·Remix·Angular 인식. build → start 스크립트 실행 |
| Python | requirements.txt · pyproject.toml | uv·poetry·pdm·pip. Django·FastAPI·Flask 시작 명령을 추정해요 |
| Go · Rust | go.mod · Cargo.toml | 바이너리를 빌드해서 실행해요 |
| Ruby · PHP · Java · .NET · Elixir · Deno · Bun | 각 매니페스트 | Rails·Laravel·Gradle/Maven 등을 인식해요 |
| 정적 사이트 | index.html 또는 SPA 빌드 결과 | Caddy 로 서빙해요 |
railpack.json 을 둬도 돼요(형식).PORT 로 열어야 해요. 예: next start, uvicorn --port $PORT주의모노레포는 ‘루트 디렉터리’를 비워 두세요
pnpm --filter @org/web... build, 시작 pnpm --filter @org/web start비유하면
소스 설정의 “테스트 명령”에 pnpm test 처럼 적어 두면 빌드 중에 테스트가 먼저 돌아요. 실패하면 빌드가 “테스트 실패 (배포 중단)”으로 끝나고 배포되지 않아요. GitHub Actions 없이도 push 마다 검사가 돼요.
주의자동 감지 빌드에서 테스트가 전부 실패한다면
NODE_ENV=production, CI=true 라서 그래요. React Testing Library·vitest 처럼 개발 빌드가 필요한 테스트는 NODE_ENV=test pnpm test 처럼 앞에 붙이세요. 안 그러면 React.act is not a function 같은 오류로 전부 실패해요.| 빌드 방식 | 테스트가 도는 곳 | 알아 둘 점 |
|---|---|---|
| 자동 감지(Railpack) | 의존성 설치 뒤, 빌드 명령 앞 | devDependencies 가 포함돼요. 출력은 build 단계 로그에 그대로 보여요 |
| Dockerfile | 완성된 이미지 안에서 RUN <테스트 명령> | 멀티스테이지로 소스·테스트 의존성을 뺀 이미지에서는 못 돌려요. 그럴 땐 Dockerfile 의 빌드 스테이지에 RUN npm test 를 직접 두세요 |
프로젝트 설정 → 소스에서 웹훅 URL 을 복사해 저장소에 등록해요. 형식은 https://deploy.core.today/api/hooks/<slug>/<토큰> 이고, 토큰은 프로젝트마다 다르며 설정에서 재발급할 수 있어요.
Settings → Webhooks → Add webhook Payload URL : (복사한 웹훅 URL) Content type: application/json Secret : (비움 — 토큰이 URL 에 있음) Events : Just the push event
Settings → Webhooks → Add new webhook URL : (복사한 웹훅 URL) Trigger : Push events (브랜치 필터는 비워도 됨 — 콘솔이 거름) SSL verification: 켬
Settings → Webhooks → Add Webhook → Gitea Target URL : (복사한 웹훅 URL) HTTP Method : POST POST Content Type: application/json Trigger On : Push Events
{"ok":true,"pong":true} 로 답하니 “Recent Deliveries”에서 성공을 확인하세요.# 설정한 브랜치로 빌드
curl -X POST "https://deploy.core.today/api/hooks/<slug>/<토큰>"
# 브랜치·태그·커밋 SHA 지정
curl -X POST "https://deploy.core.today/api/hooks/<slug>/<토큰>?ref=v1.4.0"
# 다른 브랜치를 미리보기 환경으로 (미리보기를 켠 프로젝트만)
curl -X POST "https://deploy.core.today/api/hooks/<slug>/<토큰>?ref=feature/login&preview=1"
# 응답 예
{"ok":true,"build":"20261012-091530-a1b2","preview":false,"url":"https://deploy.core.today/projects/<slug>/builds/20261012-091530-a1b2"}콘솔 빌드 탭의 “새 빌드” 버튼도 같은 일을 해요. URL 이 노출됐다면 설정에서 토큰을 재발급하세요.
:buildcache 태그로 저장돼 다음 빌드에 재사용돼요. 의존성 설치(COPY package.json + RUN npm ci)를 소스 복사보다 앞에 두면 효과가 커요.05
마트의 시식 코너예요. 정식 출시 전에 다른 브랜치를 별도 주소에 띄워 맛보게 해 줘요. Vercel 의 Preview Deployment 와 같아요.

?ref=<branch>&preview=1 호출도 돼요.)<slug>--<branch>-<해시4>.apps.core.today 주소에 그 브랜치가 떠요. 예: feature/login → <slug>--feature-login-ab12.apps.core.today주의GPU 프로젝트는 미리보기도 GPU 를 하나 더 써요
06
웹 서버처럼 계속 떠 있는 게 아니라 일을 마치면 사라지는 실행이에요. 대량 추론·데이터 변환·색인 만들기처럼 한 번 돌리고 끝나는 일에 써요.
비유하면
이것만 기억하세요
python -m jobs.infer. 비우면 이미지의 기본 명령이 실행돼요.# 모든 Pod 에 이 환경변수가 들어와요 JOB_COMPLETION_INDEX # 내 조각 번호 (0부터) JOB_COMPLETIONS # 전체 조각 수 # 예: 파일 목록을 조각 수로 나눠 내 몫만 처리 python -m jobs.infer --shard "$JOB_COMPLETION_INDEX" --of "$JOB_COMPLETIONS"
명령이 0 으로 끝나면 그 조각은 완료예요. 실패한 조각은 정해 둔 횟수만큼 다시 돌려요(기본 2회).
주의비밀값은 프로젝트 환경변수에 두세요
“작업” 탭에서 API 토큰을 발급해 프로젝트 환경변수(예 KUBECORE_API_TOKEN)에 넣어 두고 쓰세요. 재발급하면 이전 토큰은 바로 막혀요. 같은 토큰으로 빌드 시작과 빌드·앱 상태·로그도 볼 수 있어요. → AI 코딩 에이전트에게 배포 맡기기
# 시작
curl -X POST https://deploy.core.today/api/v1/projects/<slug>/jobs \
-H "Authorization: Bearer $KUBECORE_API_TOKEN" -H "Content-Type: application/json" \
-d '{"command":"python -m jobs.infer","completions":100,"parallelism":4,"gpu":1,"env":{"RUN_ID":"r1"}}'
# 최근 작업 목록
curl -H "Authorization: Bearer $KUBECORE_API_TOKEN" https://deploy.core.today/api/v1/projects/<slug>/jobs
# 상태 (state · succeeded · failedIndexes), ?logs=1&index=N 으로 조각 로그
curl -H "Authorization: Bearer $KUBECORE_API_TOKEN" https://deploy.core.today/api/v1/projects/<slug>/jobs/<id>
# 취소
curl -X DELETE -H "Authorization: Bearer $KUBECORE_API_TOKEN" https://deploy.core.today/api/v1/projects/<slug>/jobs/<id>대화형 AI 서비스가 사용자 대신 코드를 돌리는 격리된 실행 환경이에요. “이 CSV 를 분석해 그래프를 그려 줘” 같은 요청을 모델이 코드로 풀 때 써요.
비유하면
이것만 기억하세요
kcs_…)을 발급해 서비스 백엔드의 환경변수(예 KUBECORE_SANDBOX_TOKEN)에 넣어요. 배치 작업 토큰과 따로라서 샌드박스만 열려요.X-Sandbox-Subject 헤더에 최종 사용자 ID 를 실어 보내요. 그 사용자가 만든 세션·실행은 같은 값으로만 보이고 쓸 수 있어요.주의토큰은 서버에만 두세요
curl -X POST https://deploy.core.today/api/v1/projects/<slug>/sandbox-runs \
-H "Authorization: Bearer $KUBECORE_SANDBOX_TOKEN" -H "X-Sandbox-Subject: user-123" \
-H "Content-Type: application/json" \
-d '{"language":"python","code":"import pandas as pd\nprint(pd.read_csv(\"in.csv\").describe())","files":[{"path":"in.csv","content":"a,b\n1,2\n"}]}'
# 200 {"run":{"state":"succeeded","result":{"exitCode":0,"stdout":"…","stderr":"","timedOut":false,"truncated":false}}}
# 85초 안에 안 끝나면 202 → GET https://deploy.core.today/api/v1/projects/<slug>/sandbox-runs/<id>?wait=60python(3.12, pandas·numpy·matplotlib·scikit-learn 등)·node(22)·bash. 둘 다 헤드리스 Chromium 을 쓸 수 있어요(아래 “헤드리스 브라우저”). 코드와 파일은 합쳐 512KB 까지예요.state: "timeout", 종료 코드 124 예요.AUTH="Authorization: Bearer $KUBECORE_SANDBOX_TOKEN"; SUBJECT="X-Sandbox-Subject: user-123"
# 만들기 (준비될 때까지 기다렸다 돌려줘요) → {"sandbox":{"id":"s-…","state":"ready",…}}
curl -X POST https://deploy.core.today/api/v1/projects/<slug>/sandboxes -H "$AUTH" -H "$SUBJECT" -H "Content-Type: application/json" \
-d '{"size":"s","network":"none","idleMinutes":30}'
# 명령 실행 (bash, /workspace 에서) → {"exitCode":0,"stdout":"…","stderr":"…","timedOut":false}
curl -X POST https://deploy.core.today/api/v1/projects/<slug>/sandboxes/s-…/exec -H "$AUTH" -H "$SUBJECT" -H "Content-Type: application/json" \
-d '{"command":"python3 analyze.py","timeoutSeconds":60}'
# 90초가 넘는 명령: 시작만 하고(202 {"execId":"x-…"}) 조회 — 도는 중이면 지금까지의 출력
curl -X POST https://deploy.core.today/api/v1/projects/<slug>/sandboxes/s-…/exec -H "$AUTH" -H "$SUBJECT" -H "Content-Type: application/json" \
-d '{"command":"soffice --headless --convert-to pdf report.docx","timeoutSeconds":600,"async":true}'
curl "https://deploy.core.today/api/v1/projects/<slug>/sandboxes/s-…/exec/x-…?wait=60" -H "$AUTH" -H "$SUBJECT"
# 디렉터리 통째로 넣고 받기 (tar.gz)
curl -X PUT "https://deploy.core.today/api/v1/projects/<slug>/sandboxes/s-…/files/archive?path=inputs" -H "$AUTH" -H "$SUBJECT" --data-binary @inputs.tar.gz
curl "https://deploy.core.today/api/v1/projects/<slug>/sandboxes/s-…/files/archive?path=outputs" -H "$AUTH" -H "$SUBJECT" -o outputs.tar.gz
# 파일 올리기·내려받기·목록·지우기 (/workspace 기준 경로, 100MB 까지)
curl -X PUT "https://deploy.core.today/api/v1/projects/<slug>/sandboxes/s-…/files?path=data/in.csv" -H "$AUTH" -H "$SUBJECT" --data-binary @in.csv
curl "https://deploy.core.today/api/v1/projects/<slug>/sandboxes/s-…/files?path=chart.png" -H "$AUTH" -H "$SUBJECT" -o chart.png
curl "https://deploy.core.today/api/v1/projects/<slug>/sandboxes/s-…/files?path=data&list=1" -H "$AUTH" -H "$SUBJECT"
curl -X DELETE "https://deploy.core.today/api/v1/projects/<slug>/sandboxes/s-…/files?path=tmp" -H "$AUTH" -H "$SUBJECT"
# 프로세스 모두 정리(파일은 남음) · 세션 지우기
curl -X POST https://deploy.core.today/api/v1/projects/<slug>/sandboxes/s-…/reset -H "$AUTH" -H "$SUBJECT"
curl -X DELETE https://deploy.core.today/api/v1/projects/<slug>/sandboxes/s-… -H "$AUTH" -H "$SUBJECT""async": true 로 시작하고 조회하세요 — 최대 60분, 도는 동안 세션이 지워지지 않아요. 넘기면 그 명령이 띄운 프로세스까지 모두 끝내고 종료 코드 124 를 돌려줘요."keepBackground": true 를 주세요.idleMinutes(기본 30분, 최대 24시간)가 지나거나, 만든 뒤 24시간이 지나면 지워져요. 파일을 남기려면 그 전에 내려받으세요.truncated: true 예요. 큰 결과는 파일로 쓰고 내려받으세요.| 크기 | CPU | 메모리 | 작업 공간(/workspace) |
|---|---|---|---|
| S | 500m | 512Mi | 1Gi |
| M | 1 | 2Gi | 2Gi |
| L | 2 | 4Gi | 5Gi |
| XL | 4 | 8Gi | 10Gi |
none(기본)은 바깥과 완전히 끊겨 있어요. allowlist 는 프로젝트에 등록한 도메인만(https) 열려요 — pip·npm·git 이 그대로 돼요. internet 은 프로젝트에서 허용했을 때만 고를 수 있고 공인 인터넷 전체가 열려요. 사내망·플랫폼 내부·이 조직의 다른 프로젝트와 데이터베이스에는 어느 것이든 닿지 않아요.puppeteer·playwright-core, Python playwright. 웹 페이지 스크린샷, HTML → PDF, 화면 테스트에 써요. 브라우저를 따로 내려받지 않아요 — puppeteer 는 launch() 그대로, playwright 는 executablePath 에 CHROME_PATH 를 넘겨요. 외부 페이지를 열려면 네트워크가 allowlist(그 도메인 등록)나 internet 이어야 하고, 크기는 M 이상을 권해요.convert)·tesseract(한국어·영어 OCR)·graphviz(dot)·mermaid(mmdc)·ripgrep·7z. 네트워크 없이 바로 써요. plotly 그림 파일은 write_image 가 안 되니 write_html 로 만든 뒤 브라우저로 찍어요./workspace·/tmp 만 되고, 그 밖은 읽기 전용이에요."labels": {"conversation": "c-123"} 를 붙이고 GET …/sandboxes?label=conversation=c-123&all=1 로 찾거나 DELETE 로 한꺼번에 지워요. 서비스가 다시 시작된 뒤 남은 세션을 정리할 때 써요.code 로 구분해요. budget_exhausted(402)는 조직 예산 소진이라 사용자가 아니라 운영자에게 알리고, limit_sessions·limit_subject_sessions·limit_runs(409)는 한도예요.GET …/sandbox-usage?range=24h 가 subject 별 CPU 초·메모리·세션·실행 수와 추정 금액을 돌려줘요(subject 는 sha256 앞 16자로 표시).모델에 run_code(단발 실행)나 sandbox_exec·sandbox_write_file·sandbox_read_file(세션) 같은 도구를 주고, 모델이 부르면 서비스 백엔드가 위 API 로 바꿔 보내요. 세션 ID 는 대화마다 서비스가 들고 있다가 쓰고, 대화가 끝나면 지워요. 도구 정의 예는 프로젝트의 “샌드박스” 탭에 있어요.
07
PostgreSQL·Redis 부터 벡터·검색·그래프 DB, AI 메모리 서버, 오브젝트 스토리지까지 20가지 엔진을 클러스터 안에 영구 디스크와 함께 만들어요. 프로젝트와는 별개의 리소스라 상단 메뉴 “데이터베이스”에서 따로 관리해요.

이것만 기억하세요
접속 정보를 직접 보고 싶으면 상세 화면의 “접속 정보”에 주소·포트·비밀번호(또는 API 키)·접속 URL 이 있어요. 항목은 엔진마다 다르고, PostgreSQL 계열의 사용자와 DB 이름은 app 이에요.
DATABASE_URL 이 아닌 다른 이름을 읽는다면 그 이름을 적으세요.DB_HOST, DB_PASSWORD 같은 식) “프로젝트에 연결” 대신 “접속 정보”의 값을 프로젝트 환경변수에 하나씩 적으세요. 오픈소스 앱은 이런 경우가 많아요. → 서비스가 여러 개인 앱 올리기참고내 PC 에서 붙어야 한다면
새 데이터베이스 폼에서 종류별로 묶여 나와요. 엔진은 만든 뒤에 바꿀 수 없으니, 앱이 무엇을 요구하는지 먼저 확인하세요.
| 엔진 | 이럴 때 | 연결하면 들어가는 환경변수 | 자동 백업 |
|---|---|---|---|
| PostgreSQL 18 (+pgvector)관계형 (PostgreSQL 계열) | 일반적인 관계형 DB. 벡터 검색(pgvector)도 돼요 | DATABASE_URL | 매일 |
| ParadeDB (PostgreSQL 17 + pg_search)관계형 (PostgreSQL 계열) | PostgreSQL 에 전문 검색(BM25)을 더한 것. 키워드 + 벡터 하이브리드 검색을 한 DB 로 하는 RAG 앱에 좋아요 | DATABASE_URL | 매일 |
| VectorChord (PostgreSQL 18 + vchord)관계형 (PostgreSQL 계열) | PostgreSQL 에 대규모 벡터용 인덱스를 더한 것. 벡터가 수백만 건을 넘어갈 때 좋아요 | DATABASE_URL | 매일 |
| PostGIS (PostgreSQL 18 + PostGIS 3.6)관계형 (PostgreSQL 계열) | PostgreSQL 에 공간 데이터(PostGIS)를 더한 것. 지도·위치·반경 검색에 좋아요 | DATABASE_URL | 매일 |
| PostgreSQL 17 (+pgvector)관계형 (PostgreSQL 계열) | 이전 버전(17). 앱이 17 을 요구할 때만 골라요 | DATABASE_URL | 매일 |
| MongoDB 8문서 · S 이상 | 스키마 없이 JSON 문서를 넣는 문서 DB. 트랜잭션과 변경 스트림도 돼요 | MONGODB_URI | 없어요 |
| Redis 8키-값 · 캐시 | 캐시·세션·큐 | REDIS_URL | 매일 |
| Valkey 8키-값 · 캐시 | Redis 와 프로토콜이 같은 오픈소스 포크. Redis 클라이언트를 그대로 써요 | REDIS_URL | 매일 |
| Qdrant벡터 | 벡터 검색 전용 엔진. REST 와 gRPC 를 함께 열어요 | QDRANT_URLQDRANT_API_KEY | 없어요 |
| Weaviate벡터 | 벡터 + 키워드 하이브리드 검색. 벡터는 앱이 만들어 넣어요 | WEAVIATE_URLWEAVIATE_API_KEY | 없어요 |
| LanceDB 저장소벡터 | LanceDB 는 앱 안에서 도는 라이브러리라 서버가 없어요. 테이블 파일을 둘 저장소와 버킷을 만들어 드려요 | LANCEDB_URIAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_REGIONAWS_ENDPOINT_URLAWS_ENDPOINTAWS_ALLOW_HTTP | 매일 |
| OpenSearch 3검색 · 분석 · L 이상 | 전문 검색·로그 분석·k-NN 벡터 | OPENSEARCH_URLOPENSEARCH_USERNAMEOPENSEARCH_PASSWORD | 없어요 |
| Meilisearch검색 · 분석 | 오타에 강한 즉시 검색. 검색창·자동완성에 좋아요 | MEILISEARCH_URLMEILI_MASTER_KEY | 없어요 |
| ClickHouse검색 · 분석 · M 이상 | 대량 집계·분석용 컬럼 DB | CLICKHOUSE_URLCLICKHOUSE_USERCLICKHOUSE_PASSWORDCLICKHOUSE_DB | 없어요 |
| Neo4j 5 (Community)그래프 · M 이상 | 그래프 DB(Cypher) | NEO4J_URINEO4J_USERNAMENEO4J_PASSWORD | 없어요 |
| FalkorDB그래프 | Redis 프로토콜 위의 그래프 DB(Cypher). GraphRAG·에이전트 메모리에 써요 | FALKORDB_URL | 매일 |
| Graphiti (시간 지식 그래프)AI 메모리 · M 이상 | 대화·문서에서 시점이 있는 지식 그래프를 만들어 주는 REST 서버 | GRAPHITI_URL | 없어요 |
| Mem0 (에이전트 메모리)AI 메모리 · S 이상 | 사용자·에이전트별 기억을 넣고 찾는 REST 서버 | MEM0_API_URLMEM0_API_KEY | 없어요 |
| Hindsight (에이전트 메모리)AI 메모리 · L 이상 | 기억을 쌓고 떠올리는 에이전트 메모리 서버. MCP 로도 붙어요 | HINDSIGHT_API_URL | 없어요 |
| 오브젝트 스토리지 (S3 호환)오브젝트 스토리지 | 배치 작업 결과·데이터셋·업로드 파일처럼 큰 파일. S3 API 로 읽고 써요 | AWS_ENDPOINT_URL_S3AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_REGION | 매일 |
주의자동 백업이 없는 엔진은 이렇게 쓰세요
PostgreSQL 계열 엔진은 접속 방법과 백업이 같고, 버전과 들어 있는 확장만 달라요. 앱이 요구하는 확장이 없는 엔진을 고르면 앱의 첫 실행(마이그레이션)이 extension "…" is not available 로 실패해요.
| 엔진 | 쓸 수 있는 확장 |
|---|---|
| PostgreSQL 18 (+pgvector) | vector(pgvector) · pg_trgm 같은 PostgreSQL 기본 확장 |
| ParadeDB (PostgreSQL 17 + pg_search) | 위의 것 + pg_search(BM25 전문 검색) |
| VectorChord (PostgreSQL 18 + vchord) | 위의 것 + vchord(CREATE EXTENSION vchord CASCADE 로 켜요) |
| PostGIS (PostgreSQL 18 + PostGIS 3.6) | postgis · postgis_topology(처음부터 켜져 있어요) · pg_trgm 같은 기본 확장 (vector 는 없어요) |
| PostgreSQL 17 (+pgvector) | PostgreSQL 18 과 같아요 (버전만 17) |
CREATE EXTENSION 을 찾아보세요. docker-compose 의 DB 이미지 이름(paradedb/…, pgvector/…, postgis/…)도 힌트예요.이 엔진들은 기억을 추출·요약할 때 스스로 LLM 을 불러요. 그래서 만들 때 LLM API 키(OpenAI 호환, 필수)와 주소·모델(선택)을 함께 받아요.
/v1 까지 적으세요. 비우면 OpenAI 로 가요.X-API-Key 헤더로 인증해요.http:// 예요. 클러스터 안에서만 통신하니 TLS 를 쓰지 않아요. 기본값이 https 인 클라이언트(OpenSearch 등)는 주소를 http:// 로 적고 SSL 옵션을 끄세요.aws s3 mb s3://results). JS SDK 는 forcePathStyle: true 가 필요하고(boto3·CLI 는 자동), 리전은 아무 값이나 돼요.lancedb.connect(os.environ["LANCEDB_URI"]) 한 줄이면 돼요. 여러 Pod 가 같은 테이블에 동시에 쓰면 충돌할 수 있으니 쓰는 쪽은 하나로 두세요.admin 과 비밀번호로 HTTP 기본 인증을 해요. 메모리를 많이 써서 L 이상만 만들 수 있어요.import { S3Client } from "@aws-sdk/client-s3";
// 연결하면 들어오는 환경변수만으로 충분해요. forcePathStyle 만 직접 켜세요.
const s3 = new S3Client({ endpoint: process.env.AWS_ENDPOINT_URL_S3, forcePathStyle: true });비유하면
| 복제 (기본) | 로컬 | |
|---|---|---|
| 데이터 위치 | 서버 2곳에 복제해요 (2벌) | 서버 한 대의 로컬 디스크 |
| 서버 한 대가 멈추면 | 다른 서버에서 다시 떠요 | 서버가 돌아올 때까지 DB 도 멈춰요 |
| 속도 | 쓰기 지연이 약간 커요 | 빨라요 |
| 어울리는 곳 | 운영 데이터 | 속도가 중요하고 다시 만들 수 있는 데이터(캐시 등) |
자동 백업이 되는 엔진(PostgreSQL 18 · ParadeDB · VectorChord · PostGIS · PostgreSQL 17 · Redis 8 · Valkey 8 · LanceDB 저장소 · FalkorDB · 오브젝트 스토리지)은 어느 쪽이든 백업이 따로 돌아요. 자동 백업이 없는 엔진은 복제를 고르세요. 스토리지는 만든 뒤에 바꿀 수 없고, 복제 스토리지가 생기기 전에 만든 데이터베이스는 로컬이에요.
백업과 복원은 PostgreSQL 계열·Redis 계열·오브젝트 스토리지(PostgreSQL 18 · ParadeDB · VectorChord · PostGIS · PostgreSQL 17 · Redis 8 · Valkey 8 · LanceDB 저장소 · FalkorDB · 오브젝트 스토리지)에만 있어요.
주의복원은 한가한 시간에 하세요
PostgreSQL 계열은 덤프(pg_dump 형식), Redis 계열은 RDB 스냅샷으로 클러스터 밖 전용 저장소에 올려요. 백업용 키에는 삭제 권한이 없어서, 키가 유출돼도 과거 백업을 지울 수 없어요. 오브젝트 스토리지는 모든 버킷의 파일을 하나로 묶어(파일 종류·메타데이터 포함) 백업할 때마다 통째로 올려요. 그래서 기본 일정이 주 1회예요 — 매일로 바꾸면 30일 동안 전체 복사본 30벌이 쌓여요. 파일을 차례로 읽기 때문에 백업 도중에 바뀐 파일은 섞일 수 있어요. PostgreSQL 계열 복원은 그 시점의 덤프로 덮어쓰고, Redis 계열 복원은 서버를 멈춘 뒤 스냅샷 파일을 교체해요.
08
앱의 건강 상태는 배지 하나로 보여 드려요. 배지가 초록이 아니면 아래 표에서 할 일을 찾으세요.

| 배지 | 무슨 뜻이고, 무엇을 하면 되나요 |
|---|---|
| 실행 중 | 할 일 없어요. 모두 준비 상태예요. |
| 배포 중 | 새 버전이 올라오는 중이에요. 진행바와 활동을 지켜보세요. 5분(GPU 프로젝트는 이미지가 커서 15분) 안에 끝나지 않으면 “실패”로 바뀌어요. |
| 첫 빌드 대기 | 빌드가 성공하면 자동으로 배포됩니다.Git 프로젝트의 첫 빌드가 아직 성공하지 않았어요. 빌드 탭에서 진행 상황을 확인하세요. |
| 일부 장애 | 일부 Pod 만 준비 상태입니다.일부만 준비됐어요. 준비되지 않은 Pod 의 이벤트·로그를 보세요. 서버 자원이 부족하면 크기·복제본을 줄여요. |
| 헬스체크 실패 | 컨테이너는 떠 있지만 헬스체크에 응답하지 않습니다.앱은 떠 있는데 헬스체크 경로가 200 을 돌려주지 않아요. 경로·포트를 확인하거나 헬스체크를 비우세요. |
| 반복 종료 | 컨테이너가 시작 직후 죽고 있습니다. 로그를 확인하세요.시작하자마자 종료돼요. 로그 탭에서 첫 오류를 확인하세요. 보통 PORT 를 안 쓰거나 필수 환경변수가 빠진 경우예요. |
| 이미지 오류 | 이미지를 받을 수 없습니다. 주소·태그·권한을 확인하세요.이미지를 받을 수 없어요. 주소·태그 오타, 비공개 이미지, ARM 전용 이미지를 의심하세요. |
| 메모리 초과 | 메모리 한도를 넘어 종료됐습니다. 크기를 올리세요.메모리 한도를 넘었어요. 크기를 한 단계 올리거나 워커 수를 줄이세요. |
| 실패 | 배포가 제한 시간 안에 끝나지 않았습니다.배포가 제한 시간(5분) 안에 끝나지 않았어요. Pod 상태를 보고, 이전 리비전으로 롤백할 수 있어요. |
| 정지 | 복제본이 0 이에요. 복제본을 올리면 다시 시작해요. 요금은 나가지 않아요. |
| 카드 | 무엇을 보여 주나요 |
|---|---|
| 활동 | 빌드·배포·경고·설정 변경을 시간순으로 합친 타임라인이에요. 각 항목의 “당시 로그”를 누르면 그 시간대 로그로 바로 가요. |
| Pod 미터 | Pod 별 CPU·메모리 실제 사용량과 한도예요. 85% 를 넘으면 경고색으로 바뀌어요. |
| 사용량 추이 | 24시간/7일 동안의 CPU·메모리 합계와 한도, 재시작 횟수예요. GPU 프로젝트는 GPU 사용률도 보여요. |
대시보드(홈)에서는 서버별 CPU·메모리·GPU 사용률과 디스크, 경고 이벤트를 볼 수 있어요.
| 실시간 | 보관 기록 | |
|---|---|---|
| 언제 쓰나요 | 지금 무슨 일이 일어나는지 볼 때 | 어제·지난주에 무슨 일이 있었는지 찾을 때 |
| 범위 | 지금 떠 있는 모든 Pod 를 한 화면에. 크래시한 직전 컨테이너의 로그도 봐요 | 모든 Pod 의 출력을 30일 동안 보관해요 |
| 기능 | 검색 · “에러만” · 타임스탬프 · 다운로드 | 시간 범위(프리셋·날짜 지정) · 검색(대소문자 무시) · Pod 필터 · 500줄씩 더 보기 · 다운로드 |
주의로그는 stdout/stderr 로만 내보내세요
보관 기록 저장소가 멈춰도 실시간 로그와 배포에는 영향이 없어요. 보관 탭만 “사용 불가”로 보여요.
빌드 결과와 앱 장애·복구는 Slack 과 이메일로 받을 수 있어요. → 알림 설정 · Slack 웹훅 만들기
클러스터 차원의 알림(서버 다운, 디스크 소진 예측 등)은 운영 채널로 따로 가고, 프로젝트 설정과는 무관해요.
09
조직은 팀이 함께 쓰는 공간이에요. 프로젝트·데이터베이스·예산이 사람이 아니라 조직에 속해요.

이것만 기억하세요
고객사 조직은 플랫폼 관리자가 한도와 함께 직접 등록해 주기도 해요.
| 할 수 있는 일 | 구성원 | 관리자 | 소유자 | 개인 공간 본인 |
|---|---|---|---|---|
| 조직 프로젝트 보기·로그·활동 | O | O | O | 본인 것만 |
| 새 프로젝트·데이터베이스 만들기 | O | O | O | — (조직에서만) |
| 빌드 시작·이미지 배포·복제본 조절·재시작·롤백 | O | O | O | O |
| 설정 변경(이미지·env·크기·GPU·도메인·소스·알림·예약), 작업 API 토큰 | 내가 만든 것만 | O | O | O |
| 프로젝트·데이터베이스 삭제, 데이터베이스 복원·백업 일정 | 내가 만든 것만 | O | O | O |
| 구성원 초대·역할 변경·제거, 조직 이름 변경 | — | O | O | — |
| 소유자 지정·소유자 제거, 조직 삭제 | — | — | O | — |
| 조직에서 나가기 | O | O | O (마지막 소유자 제외) | — |
로그인은 core.today 계정으로 하고, 조직·소속·역할은 콘솔이 관리해요. 프로젝트의 소유 단위(테넌트)는 활성 조직 또는 개인이에요.
10
선불 교통카드를 떠올리면 돼요. 관리자가 충전해 준 예산에서 실제로 쓴 만큼만 차감돼요.

이것만 기억하세요
| 항목 | 기본 단가 | 참고 시세 |
|---|---|---|
| vCPU 1개 · 1시간 | ₩40 | Railway $20/vCPU·월 ≈ ₩39/h · AWS 서울 ≈ ₩87/h |
| 메모리 1 GB · 1시간 | ₩15 | Railway $10/GB·월 ≈ ₩19/h · Fly ≈ ₩10/h |
| DB 디스크 1 GB · 1시간 (할당 용량) | ₩0.2 | AWS gp3 ≈ ₩0.15/h · 복제 2벌+백업 포함 → ₩0.2/h (≈ ₩146/GB·월) |
| GPU A100 80GB · 1시간 | ₩2,000 | 온디맨드 $1.09~2.79/h (RunPod·Lambda·Vast) |
| GPU A100 40GB · 1시간 | ₩1,500 | Lambda $1.99/h · 마켓 ≈ $1.1/h |
| GPU V100S 32GB · 1시간 | ₩800 | 커뮤니티 클라우드 ≈ $0.5~0.8/h |
| GPU V100 16GB · 1시간 | ₩500 | 커뮤니티 클라우드 ≈ $0.3~0.5/h |
| GPU (기타 모델) · 1시간 | ₩1,000 | 모델을 식별하지 못한 GPU |
| 조각 | 기본 단가 | 계산 |
|---|---|---|
| A100 80GB 한 장 (MIG 7g.80gb) · 1시간 | ₩2,000 | A100 80GB 와 같음 |
| A100 조각 40GB (4g.40gb) · 1시간 | ₩1,150 | A100 80GB × 4/7 |
| A100 조각 40GB (3g.40gb) · 1시간 | ₩860 | A100 80GB × 3/7 |
| A100 조각 20GB (2g.20gb) · 1시간 | ₩580 | A100 80GB × 2/7 |
| A100 조각 20GB (1g.20gb) · 1시간 | ₩430 | A100 80GB × 1/7 + 메모리 2칸 |
| A100 조각 10GB (1g.10gb) · 1시간 | ₩290 | A100 80GB × 1/7 |
| A100 40GB 한 장 (MIG 7g.40gb) · 1시간 | ₩1,500 | A100 40GB 와 같음 |
| A100 조각 20GB (4g.20gb) · 1시간 | ₩860 | A100 40GB × 4/7 |
| A100 조각 20GB (3g.20gb) · 1시간 | ₩650 | A100 40GB × 3/7 |
| A100 조각 10GB (2g.10gb) · 1시간 | ₩430 | A100 40GB × 2/7 |
| A100 조각 5GB (1g.5gb) · 1시간 | ₩220 | A100 40GB × 1/7 |
비유하면한 달이면 얼마쯤?
관리자가 단가를 바꾸면 “단가” 화면의 현재 값이 우선이에요.
| 상황 | 막히는 것 | 되는 것 |
|---|---|---|
| 예산을 한 번도 받지 않은 조직 | 새 프로젝트·데이터베이스·빌드(웹훅 포함)·증설 | 구성원 초대, 화면 보기 |
| 잔액이 0 이하가 된 조직 | 새 프로젝트·빌드 시작·증설(복제본·크기·GPU·이미지 변경). 진행 중인 배치 작업은 일시 중지 | 실행 중인 서비스는 계속 돌아요 |
| 개인 공간(예전에 만든 프로젝트) | — | 예산 없이 그대로 운영돼요 |
크레딧이 넉넉해도 조직이 동시에 쓸 수 있는 자원에는 상한이 있어요. 기본값은 프로젝트 100개 · vCPU 236 · 메모리 880 GiB · GPU 4 이고(크기 × 복제본을 합쳐 계산), 관리자가 조직별로 조정해요. 더 필요하면 /org 에서 사용량/한도를 확인하고 관리자에게 요청하세요.
/org 에서 이번 달·지난 달의 프로젝트별 vCPU 시간·메모리 GB·시간·GPU 시간·금액을 보고 CSV 로 내려받을 수 있어요. 플랫폼 관리자는 관리 화면에서 조직별 합계와 월별 CSV 를 받아요. 집계가 늦는 것 같으면 관리자 화면의 “지금 집계”로 최근 2일을 다시 집계할 수 있어요.
11
당황하지 마세요. 대부분은 상태 설명과 로그의 첫 오류 줄에 답이 있어요. 증상을 눌러 펼쳐 보세요.

흔한 원인
이렇게 해 보세요
linux/amd64 매니페스트가 있는 태그를 써요. Docker Hub 의 Tags 화면에서 태그마다 OS/ARCH 를 볼 수 있어요.흔한 원인
이렇게 해 보세요
흔한 원인
이렇게 해 보세요
NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE=1, gunicorn/uvicorn 은 WEB_CONCURRENCY 로 워커 수를 고정해요.흔한 원인
이렇게 해 보세요
https://<slug>.apps.core.today<경로> 가 200 인지 확인해요.흔한 원인
이렇게 해 보세요
흔한 원인
이렇게 해 보세요
흔한 원인
이렇게 해 보세요
ARG 로 선언해요.흔한 원인
이렇게 해 보세요
NODE_ENV=test 를 붙여요. → 테스트 명령흔한 원인
이렇게 해 보세요
흔한 원인
이렇게 해 보세요
흔한 원인
이렇게 해 보세요
<주소>.apps.core.today 로 먼저 확인해요. 열리면 앱은 정상이고 도메인 연결 문제예요.흔한 원인
이렇게 해 보세요
:80 으로 적어요(gRPC 도 같아요).흔한 원인
이렇게 해 보세요
흔한 원인
이렇게 해 보세요
흔한 원인
이렇게 해 보세요
흔한 원인
이렇게 해 보세요
흔한 원인
이렇게 해 보세요
흔한 원인
이렇게 해 보세요
USER 줄보다 앞에서 그 폴더만 소유자를 바꿔요: RUN mkdir -p /app/cache && chown app /app/cache/tmp 에 써요. 컨테이너 안에 쓴 파일은 어차피 재시작 때 사라져요.흔한 원인
이렇게 해 보세요
흔한 원인
이렇게 해 보세요
흔한 원인
이렇게 해 보세요
aws s3 mb s3://resultsforcePathStyle: true 를 켜요. 앱 설정이라면 “path style” 옵션을 켜고 SSL 옵션을 꺼요.12
아직 안 되는 것을 숨기지 않고 적어 둬요. 왜 안 되는지와, 그 대신 쓸 수 있는 방법을 함께 정리했어요.

| 안 되는 것 | 왜 | 대신 이렇게 |
|---|---|---|
| 프로젝트의 영구 볼륨 | 프로젝트에 디스크를 붙이는 기능이 아직 없어요. 컨테이너 안에 쓴 파일은 재시작과 함께 사라져요 | 데이터는 관리형 데이터베이스에, 파일은 오브젝트 스토리지에 두세요. Postgres·Redis 템플릿은 실험용이에요 |
| 레지스트리로 직접 push | 바깥에서 들어오는 요청 크기에 제한이 있어 큰 이미지 레이어를 올릴 수 없어요 | Git 저장소에서 빌드하거나 공개 레지스트리(Docker Hub·GHCR) 이미지를 쓰세요 |
| 컨테이너 인자(args)·커맨드 변경 | 콘솔 폼이 image·env·port 만 다뤄요 | FROM 이미지 + CMD […] 두 줄 Dockerfile 로 Git 빌드 |
| docker-compose 파일 그대로 배포 | 프로젝트 하나가 컨테이너 하나예요 | 서비스마다 프로젝트를 만들고 DB 는 관리형 데이터베이스로 옮기세요. → 서비스가 여러 개인 앱 올리기 |
| 공개 주소가 없는 내부 전용 프로젝트 | 모든 프로젝트에 공개 주소가 붙고, 끌 수 없어요 | 앱 자체의 인증(토큰·비밀번호)을 켜세요. 같은 조직의 프로젝트끼리는 내부 주소로 부르면 돼요 |
| 100 MB 가 넘는 요청(파일 업로드) | 바깥에서 들어오는 요청 한 번의 크기에 상한이 있어요 | 큰 파일은 나눠 올리거나, 브라우저가 외부 S3 로 직접 올리게 하세요 |
| TCP/UDP 포트 공개 | 바깥에서 들어오는 길이 HTTP(S) 뿐이에요 | HTTP API 로 감싸세요. DB 는 클러스터 안에서만 접속돼요 |
| ARM 이미지 | 서버가 전부 amd64 예요 | linux/amd64 태그를 쓰세요. ARM 용만 있는 이미지는 소스를 Git 빌드로 올리면 돼요 |
| 벡터·검색·그래프·AI 메모리 엔진의 자동 백업 | 자동 백업은 PostgreSQL 계열·Redis 계열·오브젝트 스토리지에만 있어요 | 복제 스토리지를 쓰고, 다시 만들 수 있는 데이터에 쓰세요. → 엔진 표 |
| 브랜치마다 다른 환경변수 | 미리보기 전용 환경변수는 모든 미리보기에 똑같이 적용돼요 | 프로덕션과만 분리하면 될 땐 미리보기 전용 환경변수를 쓰세요. 브랜치별로 달라야 하면 앱이 호스트명으로 분기하거나 별도 프로젝트를 만드세요 |
| 초대 없이 조직 가입 · 초대 메일 발송 | 가입은 초대 수락으로만 이뤄지고, 콘솔은 초대 메일을 보내지 않아요 | 소유자·관리자에게 초대를 요청하고, 초대한 쪽이 직접 알려 주세요 |
| 개인 프로젝트를 조직으로 옮기기 | 소유 단위가 격리 설정에 묶여 있어서 만든 뒤에는 바꿀 수 없어요 | 조직으로 전환한 뒤 새로 만드세요 |
| 빌드 30분 · 이력 20개 · 복제본 50 · GPU 4 | 한 프로젝트가 클러스터를 독차지하지 못하게 둔 상한이에요 | 더 필요하면 관리자와 상의하세요 |
위 표의 나머지는 플랫폼 구조에서 오는 한계라 단기 계획이 없어요. 필요한 것이 있으면 관리자에게 사례와 함께 알려 주세요.