만든 앱을 올리면 공개 주소 https://{이름}.onpod.ai 까지 같이 가져와요. 사람이 정할 건 딱 하나 — 어떤 형태로 배포할지 뿐이에요. 나머지는 코딩 에이전트(Claude Code · Codex)가 자동으로 처리해요.
4가지 모양 중 하나 (에이전트가 먼저 물어봐줘요)
- 한 대로 충분한 앱 — 1대 고정. 비용 적고, 로그인·글·업로드 파일·DB 데이터를 보존할 수 있어요. 트래픽이 급증하면 처리가 어려워요. 취미·MVP는 무료(절전형)로 올릴 수 있어요 — 30분 동안 방문이 없으면 절전 상태로 전환되고, 접속하면 몇 초 안에 다시 실행돼요. 잔액 충전이나 카드 등록 없이도 배포돼요(서버 빌드 포함).
- 자동으로 늘었다 줄었다 하는 앱 — 트래픽 따라 1~N대 자동 조절. 데이터는 바깥 DB(또는 onpod 관리형 DB)에 둬요.
- 봇·상시 워커 — 웹서버(HTTP)·공개 주소가 없는 프로그램 (Discord·Slack·텔레그램 봇, 폴링 워커 등). 포트 없이 절전 없이 항상 실행되고, 중단되면 자동으로 재시작돼요. 단순 봇은 hobby 요금제 (0.5vCPU·512MB, 월 9,450원)면 충분해요. 셸 접속은
onpod ssh <이름>, 단발 명령은onpod exec <이름> -- <명령>. 봇은 절전 상태가 되면 메시지를 놓치니 무료(절전형)로는 못 올려요. 단, 웹훅을 받는 봇(Slack Events API·텔레그램 webhook 등)은 「웹훅 보관함」을 켜면 무료로도 안전해요 — 절전 상태인 동안 온 웹훅을 대신 받아뒀다가 다시 실행되면 전달해줘요. - 학습 한 번 — GPU 한 대 + 체크포인트 자동 백업. 공개 주소는 없어요.
“내 앱 띄워줘” 라고만 해도 에이전트가 넷 중 어떤 모양인지 + 데이터 보존이 필요한지 + 공개 주소가 필요한지 먼저 물어봐주고, 답에 맞춰 올려요.
“이 앱 onpod에 올려줘. 로그인이랑 글 저장이 있어서 데이터는 유지돼야 해”
“이건 트래픽이 들쭉날쭉하니까 자동으로 늘었다 줄었다 하게 올려줘”
“이 디스코드 봇 올려줘 — 웹서버 없어, 그냥 계속 돌기만 하면 돼”
에이전트가 형태를 정해 배포하고, 접속 주소를 돌려줘요.
멈추기 — 「일시정지」와 「절전」은 달라요
- 일시정지 — 내가 직접 멈추는 거예요. 앱 화면의 「일시정지」를 누르면 컴퓨트 요금이 끊기고 디스크·환경변수·데이터는 그대로 남아요. 다시 켤 때까지는 누가 주소로 접속해도 켜지지 않아요 — 방문자에겐 「일시정지 상태」 안내가 보여요. 다시 열려면 「재시작」을 누르세요.
- 절전 — 무료(절전형) 앱이 30분 동안 접속이 없을 때 자동으로 절전 상태로 전환되는 거예요. 접속하면 몇 초 안에 자동으로 다시 실행돼요. 내가 뭘 누른 게 아니라서 「이슈가 난 건가?」 하고 놀랄 필요 없어요.
- 완전히 지우려면 「삭제」 — 디스크 보관료까지 끝나요.
“myapp 잠시 내려줘 — 다시 켤 때까지 아무도 못 들어오게”
“myapp 다시 켜줘”
코드를 고쳤어요 — 데이터 유실 없이 업데이트
새 코드로 다시 올려도, 한 대짜리 앱이면 컨테이너만 바뀌고 그 안의 DB·업로드 파일은 그대로 남아요. 잘못 올렸으면 즉시 이전 버전으로 되돌릴 수 있어요 — 이미지뿐 아니라 직전의 환경변수(설정값) 세트까지 함께 돌아와요. 환경변수를 실수로 지우거나 덮어썼을 때도 같은 방법으로 복구돼요.
“새 코드로 다시 올려줘 (데이터는 그대로 둬)”
“방금 올린 거 이상해, 이전 버전으로 되돌려줘”
“환경변수를 잘못 덮어쓴 것 같아, 직전 상태로 되돌려줘”
그냥 껐다 켜고 싶어요
코드도 설정도 그대로 두고 앱만 다시 시작하고 싶을 때가 있어요 — 밖에서 설정값을 바꿔 넣었거나, 앱이 응답하지 않을 때요. 켜져 있는 앱도 바로 다시 시작돼요. 이미지·환경변수·데이터는 전부 그대로 남고, 여러 대짜리 앱은 한 대씩 차례로 바꿔서 서비스가 안 끊겨요.
설정값을 바꿀 땐 다시 시작을 따로 요청하지 않아도 돼요 — 값을 바꾸면 앱이 새 값으로 다시 시작되는 것까지 자동으로 처리해요.
“앱 다시 시작해줘”
“이 설정값 바꾸고 반영될 때까지 확인해줘”
소스 코드는 공개하지 않고 올리고 싶어요
소스를 공개하지 않고도 배포할 수 있어요. 컴파일된 결과물만 담은 이미지를 비공개 저장소에 올려 배포하면, 소스·비밀 키는 이미지에 안 들어가고 저장소도 비공개 그대로 유지돼요. 에이전트한테 “비공개 이미지로 올려줘” 라고 하거나, 콘솔의 팟 만들기 → 이미지 → 직접 입력 에서 「비공개 이미지인가요?」 를 펼쳐 사용자명·토큰을 넣으면 돼요. 자격증명은 서버에만 저장되고 응답으로 절대 돌아오지 않아요.
AWS ECR·비공개 저장소를 한 번만 연결해두기
매번 계정·토큰을 넣는 대신, 비공개 이미지 저장소를 한 번만 등록해두면 그 저장소의 이미지를 올릴 때 자동으로 자격이 붙어요. 특히 AWS ECR 은 접속 토큰이 12시간마다 바뀌는데,onpod registry add-ecr 로 AWS 키만 등록해두면 onpod이 받아올 때마다 새 토큰을 자동으로 발급해요. GHCR·Docker Hub 같은 곳은onpod registry add 로 사용자명·토큰을 등록하면 돼요. 등록 목록은onpod registry ls, 삭제는 onpod registry rm 이에요.
“내 AWS ECR 연결해줘 — 지역이랑 키 줄게. 그리고 거기 이미지로 배포해줘”
Dockerfile만 있으면 서버에서 빌드해서 올려요
내 컴퓨터에 도커(Docker)가 설치돼 있지 않아도 괜찮아요. 폴더에 Dockerfile 만 있으면 onpod이 서버에서 대신 빌드해서 이미지를 만들고 배포까지 해줘요. onpod build <폴더> 하면 서버에서 빌드해 이미지를 만들어주고, onpod app deploy --build <폴더> 하면 빌드부터 배포까지 한 번에 끝나요.
비밀 파일은 올라가지 않아요. 폴더 안의 .env, 인증서·키 파일(*.pem·*.key·id_rsa), .aws 폴더 같은 것은 빌드에 자동으로 빼고, 무엇을 뺐는지 화면에 알려줘요. 비밀값은 --env(환경변수)로 넣어주세요. 파일째 꼭 넣어야 하면 --include-secrets 를 붙이면 되지만, 그 파일은 이미지 안에 남아 앱을 다른 사람에게 넘기면 함께 보여요.
빌드는 내 터미널과 상관없이 서버에서 계속 진행돼요. 기다리는 중에 인터넷이 잠깐 끊겨서 「실패한 것처럼」 보여도, 대개 이미 다 만들어져 있어요. 이럴 때 다시 빌드하지 마세요 — 화면에 찍힌 빌드 번호로 onpod build status <번호> 를 보면 상태와 만들어진 이미지 주소가 나와요. 번호를 잃었으면 onpod build ls 로 찾을 수 있어요. 같은 이름으로 다시 빌드하면 그사이 바뀐 코드가 올라가서 방금 고친 게 되돌아갈 수 있어요.
앱을 여러 개 운영한다면 빌드할 때 이미지 이름을 앱 이름으로 직접 지정하세요(onpod build . --tag 앱이름:v1). 지정하지 않으면 폴더 이름이 이미지 이름이 되는데, 서로 다른 앱의 폴더 이름이 같으면(예: 둘 다 api 폴더) 나중에 빌드한 쪽이 먼저 빌드한 앱의 이미지를 덮어써서, 다른 앱이 다시 시작될 때 엉뚱한 코드로 켜질 수 있어요.
같은 소스로 앱을 여러 개 올린다면(관리자용·강사용처럼) 빌드는 한 번만 하세요. 첫 앱을 빌드해서 올리면 이미지 주소가 나오는데, 나머지 앱은 그 주소로 바로 업데이트하면 돼요(onpod app update 다른앱 --image 그주소). 앱마다 다시 빌드하면 한 번에 1~2분씩 앱 수만큼 더 기다리게 돼요. 빌드가 끝날 때 에이전트에게도 같은 안내가 나가요.
“내 앱 빌드해서 onpod에 올려줘. 도커는 안 깔려 있어.”
에이전트가 서버에서 빌드하고, 열리는 주소를 돌려줘요.
잘 안 될 때
- 올린 직후 1~3분간
warming up— 준비 중이에요. 큰 앱이면 더 오래(5분~). - “같은 이름의 앱이 이미 있어요” — 이름을 다르게 해달라고 하면 돼요.
- 업데이트 후 시작되지 않으면 에이전트한테 “이전 버전으로 되돌려줘” — 즉시 복구돼요.
- “빌드 상태를 더 못 읽었어요” — 빌드는 서버에서 계속 실행 중이에요. 다시 빌드하지 말고 에이전트한테 “빌드 상태 확인해줘”라고 하면 돼요(
onpod build status <번호>). 성공이라고 나오면 그 이미지로 배포만 이어가면 끝이에요. - “빌드가 취소됐어요” — 압축이나 업로드 단계에서 멈춘 거예요. 왜 멈췄는지(읽을 수 없는 파일·업로드 실패 등)가 빌드 기록에 남아 있으니 에이전트한테 “빌드 왜 취소됐는지 봐줘”라고 하면 원인을 읽고 고쳐서 다시 올려요. 무작정 다시 시도하면 같은 자리에서 또 멈춰요.
- 주소를 열면 502·503만 나와요 — 앱이 아직 시작되지 않은 거예요. 에이전트한테 “왜 안 뜨는지 봐줘” 하면 종료 직전 로그를 읽고 원인을 알려줘요. 자주 나오는 원인은 세 가지예요 — DB 인증서 설정(Node.js 앱), 낡은 DB 비밀번호, 맥에서 만든 이미지의 아키텍처 불일치. 셋 다 로그에 나오면 고치는 방법까지 같이 안내돼요.
- 앱이 시작하자마자 꺼지면 목록에
failed로 남아요(사라진 게 아니에요). 에이전트한테 “로그 보고 고쳐서 다시 올려줘” 하면 저장해 둔 설정·데이터 그대로 그 자리에서 복구돼요. 같은 이름으로 처음부터 다시 배포하면 넣어 둔 설정이 지워지니, 고쳐서 업데이트하는 쪽이 안전해요. - 예기치 않게 꺼진 앱은 자동으로 다시 켜져요. 따로 설정 안 해도 돼요. 다만 켜자마자 계속 꺼지기를 반복하면(대개 설정이나 코드 문제) 무한 재시작을 막으려고 자동 복구를 잠시 멈추고 이메일로 알려드려요. 그 다음은 앱 모양에 따라 달라요 — 자동 확장 앱은 이미 실행 중인 것은 그대로 실행되고, 대수를 다시 늘릴 일이 생기면(트래픽이 다시 몰릴 때 등) 15분에 1대씩만 켜 봐요. 새로 켠 것이 안정되면 자동으로 재개하니 일시적 문제(트래픽 급증 등)였다면 손댈 게 없어요. 문제를 고쳐서 앱을 업데이트하면 15분을 기다리지 않고 바로 다시 켜 봐요. 이 상태인지는 앱 목록의 상태가 알려줘요 —
parked(켜기를 잠시 멈춤)·down(실행 중인 것이 0대)으로 나와요. 한 대짜리 앱·봇은 자동으로 해제되지 않아요 — 에이전트한테 “로그 보고 고쳐서 다시 올려줘” 해서 앱이 실제로 다시 시작되면 자동 복구도 다시 활성화돼요. 여기에도 예외가 하나 있어요: 멈춘 원인이 연결해 둔 DB의 장애였다면, DB가 복구되고 몇 분 안정된 뒤 앱도 자동으로 한 번 다시 켜드려요. - 앱이 5분 넘게 멈춰 있으면 가입한 이메일로 알려드려요. 자동으로 다시 켜졌으면 메일은 안 가고, 여러 번 재시작해 봤는데도 안 될 때만 보내요 — 왜 멈췄는지(메모리 부족·앱 파일을 못 가져옴·시작하다 꺼짐)와 다음에 할 일을 한 줄로 담아서요. 같은 앱은 6시간에 한 통까지만 오고, 활동 기록에도 함께 남아요.
- 메모리가 부족해서 멈췄다면 — 더 큰 요금제로 올리는 게 해결책이에요. 무료 앱은 메모리가 512MB라서, 데이터를 많이 올리는 앱은 여기서 자주 멈춰요. 에이전트한테 “이 앱 standard로 올려줘” 하면(
onpod app upgrade {이름} standard) 멈춘 상태 그대로도 메모리 2GB 짜리로 옮기면서 다시 시작해드려요 — 주소·데이터는 그대로예요. 참고로 무료 → hobby는 상시 가동이 되는 것이지 메모리는 똑같아서, 메모리 문제엔 standard 이상을 골라야 해요. 요금제를 정하기 전에 “올릴 수 있는 요금제 보여줘” 라고 하면 가격부터 확인할 수 있어요. - 자동 확장 앱을 업데이트했는데 옛 화면이 보이면 — 절전 상태였던 서버가 아직 새 버전을 못 받은 것일 수 있어요. 몇 분 안에 자동으로 맞춰지고, 에이전트한테 “서버별 상태 보여줘” 하면 어느 서버가 어떤 버전인지 바로 확인해줘요.
- 자동 확장 앱도 이제 절전할 수 있어요. 에이전트한테 “한가하면 꺼지게 해줘” 하면(
onpod app scale {이름} --min 0) 밤처럼 아무도 안 쓸 때 0대까지 줄어 그동안 컴퓨트 요금이 0이에요. 접속이 오면 자동으로 다시 켜지고, 켜지는 1~2분 동안 방문자는 「앱을 준비하고 있어요」 화면을 봐요. 마지막 1대는 최근 요청이 있거나 대기 화면을 보는 방문자가 있으면 끄지 않아요 — 다만 실시간 스트리밍(SSE)·웹소켓처럼 오래 열어 두는 연결은 끝나기 전까지 셈에 안 잡혀서, 유휴 시간이 길어지면 끊길 수 있어요. 이런 앱은 --min 1 이상을 권해요. 공개 앱은 검색봇 접속도 서버를 다시 켤 수 있으니 월 지출 한도(spend-cap)를 같이 걸어두면 안심이에요. - 머신 크기도 자동으로 조절돼요. 앱이 메모리 부족으로 반복해서 멈추면, 다운을 막기 위해 머신을 한 단계 자동으로 키우고 바로 재시작해요(하루 1회까지·월 지출 한도는 절대 안 넘어요). 그만큼 요금이 올라가니 이메일과 활동 기록으로 바로 알려드려요 — 원치 않으면 알림 속 명령 한 줄로 되돌릴 수 있어요. 반대로 7일 내내 유휴 상태면 「한 단계 줄이면 월 얼마 절약」 알림만 보내드리고, 자동으로 줄이지는 않아요. 크기를 직접 바꾸고 싶으면 에이전트한테 “머신 한 단계 키워줘” 하면 되고(
onpod app scale {이름} --plan pro), 바꾸는 동안에도 새 머신을 먼저 켠 뒤 옛 머신을 꺼서 앱은 중단 없이 계속 실행돼요. - 요금 폭탄 방지 상한이 기본으로 켜져 있어요. 한도를 따로 안 걸어도 앱 하나당 월 20만원, 계정 전체 월 100만원의 안전 상한이 적용돼요. 상한에 닿으면 요금이 더 늘어나는 자동 동작(서버 늘리기·머신 키우기)만 그 달 동안 멈추고, 실행 중인 서비스는 그대로예요 — 이메일로 바로 알려드려요. 실제 사용이 늘어난 것이면 한 줄로 올리면 돼요:
onpod spend-cap-account 3000000. 「해제」는 기본 상한으로 돌아가는 것이지 무제한이 아니에요. - 막지 말고 알림만 받고 싶다면. 사람이 몰릴 때 상한에 걸려 느려지는 게 더 싫다면, 앱 한도를 알림 기준선으로 바꿀 수 있어요:
onpod spend-cap-default 300000 --warn-only. 그러면 서버는 기준을 넘겨서라도 계속 늘어나고, 넘긴 순간 이메일로 알려드려요. 다시 상한으로 되돌리려면--block.이 모드에서도 계정 전체 상한(onpod spend-cap-account)은 그대로 유지돼서, 진짜 요금 상한이 필요하면 그쪽을 쓰시면 돼요. - 다른 사이트에 iframe으로 끼워 넣었더니 「이 콘텐츠는 차단되어 있습니다」가 떠요 — onpod은 앱 응답에
X-Frame-Options나Content-Security-Policy헤더를 붙이지 않아요. 즉 iframe 임베드를 앞단에서 막지 않아요. 이 메시지는 대부분 임베드하는 쪽 페이지가 보내는 CSP 의frame-src규칙이 내 앱 주소를 허용하지 않아서예요 — 이 차단은 브라우저 안에서 일어나서 내 앱 서버에는 요청이 아예 안 와요 (그래서 서버 로그에도 안 남아요). 임베드하는 쪽 페이지의 CSP 에frame-src로 내 앱 주소를 추가해 주세요. 예를 들어 Directus 같은 CMS의 Live Preview라면 CMS 앱의 환경변수CONTENT_SECURITY_POLICY_DIRECTIVES__FRAME_SRC="'self' https://{이름}.onpod.ai"를 넣으면 돼요. 브라우저 개발자 도구 콘솔에 「Refused to frame … frame-src」 오류가 보이면 이 경우가 맞아요. - 「응답 대기 초과(gateway timeout)」가 뜨면 — 앱이 고장 난 게 아니라, 그 요청 하나가 응답을 시작하기까지 너무 오래 걸린 거예요. 기본 주소 (
{이름}.onpod.ai)는 95초, 직접 연결한 도메인은 300초까지 기다려줘요. 직결 주소({이름}.direct.onpod.ai)는 기본 300초고,onpod pod response-timeout으로 최대 600초까지 늘릴 수 있어요. AI 답변처럼 오래 걸리는 응답은 스트리밍으로 내보내면 시간 제한이 없어요 — 에이전트한테 “응답을 스트리밍으로 바꿔줘” 하면 돼요. - 「실시간 연결(웹소켓)이 유휴 상태면 끊겨요」 — 채팅·실시간 화면처럼 계속 열어 두는 웹소켓 연결은, 위의 스트리밍 응답과 규칙이 달라요. 아무것도 주고받지 않는 상태가 약 100초 이어지면 기본 주소 (
{이름}.onpod.ai)에서 자동으로 끊겨요. 두 가지 중 하나로 해결해요. ① 앱이 20~30초마다 짧은 신호(하트비트·ping)를 보내게 하기 — 에이전트한테 “웹소켓에 하트비트 넣어줘” 하면 돼요(대부분의 실시간 라이브러리에 기본 기능이 있어요). ② 직결 주소 ({이름}.direct.onpod.ai)나 직접 연결한 도메인으로 쓰기 — 이 경로는 그 제한이 없어요. - 「요청 본문 수신 초과(request timeout)」가 뜨면 — 큰 파일을 올리는데 회선이 느려서 업로드가 제한 시간 안에 다 도착하지 못한 거예요. 기본 주소(
{이름}.onpod.ai)는 업로드 본문을 50초까지, 직결 주소({이름}.direct.onpod.ai)는 300초까지 받아줘요. 큰 파일을 받는 앱이라면 에이전트한테 “업로드는 직결 주소로 받게 해줘” 하면 돼요.
더 알면 좋은 것
로그인·글·주문 같은 데이터를 저장하려면 관리형 Postgres 도움말을, 트래픽 따라 자동으로 대수를 조절하는 원리는 자동으로 늘리고 줄이기 도움말을 보세요. 세밀한 통제(GPU 직접 빌리기 등)가 필요하면 서버 도움말을 참고하세요.