목차
오라클 클라우드 무료 서버(A1 Flex) 확보기 — 5주간의 크론과 PAYG 전환
개인 서버가 필요했다. 조건은 하나, 기간 제한 없는 진짜 무료.
AWS·Azure·GCP는 대부분 12개월 프리 트라이얼이라 1년 뒤엔 결국 돈을 내야 한다. 영구 무료 티어를 제공하는 건 사실상 Oracle Cloud Infrastructure(OCI) 하나뿐이다. 그래서 OCI로 갔고, 그 뒤로 5주를 헤맸다.
이 글은 그 5주의 기록이다. 결론부터 말하면 PAYG(유료 계정) 전환이 답이었다.
1. 왜 OCI Always Free인가
OCI Always Free가 주는 것:
- ARM 인스턴스(VM.Standard.A1.Flex) — 2 OCPU / 12GB RAM
- x86 Micro 인스턴스(E2.1.Micro) 2대
- 블록 스토리지 총 200GB
- 로드밸런서 1개(10Mbps), Object Storage 20GB
12GB RAM을 영구 무료로 주는 곳은 여기밖에 없다. 다만 함정이 두 개 있다.
함정 1: 2026년 6월 스펙 축소
2026년 6월 15일부로 Always Free A1 한도가 4 OCPU/24GB → 2 OCPU/12GB로 반토막났다. 별도 공지도 없었다.
예전 글들을 보고 4/24를 기대하면 안 된다. (기존 사용자는 그대로 유지된다는 얘기가 있는데, 이 부분은 뒤에서 다시 다룬다.)
함정 2: 홈 리전 고정
Always Free 리소스는 홈 리전에서만 무료다. 계정 만들 때 고른 리전이 평생 간다.
나는 오사카(ap-osaka-1)를 골랐는데, 이게 나중에 발목을 잡는다.
2. 계정 가입 — 카드 검증에서 막히다
가입 마지막 단계에서 카드 검증(CyberSource)이 계속 실패했다. 원인이 명확히 안 나오는 종류의 에러다.
여기서 하나 배웠다. 가입 단계에서 무한 재시도하면 안 된다. 카드나 IP 단위로 블록이 걸리면 가입 자체가 막힌다. 실패하면 브라우저를 바꾸거나 카드를 바꾸거나, 하루 이틀 뒤에 다시 시도하는 게 낫다.
(참고로 인스턴스 생성 단계의 재시도는 이것과 다르다. 계정이 이미 정상이면 Out of capacity를 아무리 받아도 계정이 정지되진 않는다.)
3. Out of capacity — 진짜 벽
계정을 만들고 콘솔에서 인스턴스 생성을 눌렀다.
Out of capacity for shape VM.Standard.A1.Flex in availability domain AD-1.
Create the instance in a different availability domain or try again later.
설정 문제가 아니다. 그 순간 그 AD에 빈 하드웨어가 없다는 뜻이다. A1은 인기가 너무 많아서 무료 계정으로는 거의 상시 만석이다.
에러 메시지는 "다른 AD를 쓰라"고 하는데, 여기서 알아야 할 게 있다.
OCI 초기 4개 리전(London, Ashburn, Phoenix, Frankfurt)만 AD가 3개다. 그 이후에 생긴 리전은 전부 AD가 1개다. 오사카도 마찬가지. AD를 바꿀 선택지 자체가 없다.
AD 아래 FD(Fault Domain) 1/2/3은 있지만, 용량 부족은 보통 AD 단위 하드웨어 풀에서 나는 거라 FD를 돌린다고 극적으로 뚫리진 않는다.
그래서 남은 방법은 하나. 될 때까지 재시도. 손으로 할 순 없으니 자동화했다.
4. 자동 재시도 파이프라인 구축
집에 놀고 있던 구형 노트북에 Ubuntu 24.04를 올려서 상시 서버로 쓰고 있었다. 여기에 OCI CLI를 깔고 크론을 걸었다.
4.1 OCI CLI 설치 + API 키 등록
bash -c "$(curl -L https://raw.githubusercontent.com/oracle/oci-cli/master/scripts/install/install.sh)"
oci setup config
oci setup config가 API 키 쌍을 만들어준다. 생성된 공개키를 OCI 콘솔 > 사용자 설정 > API Keys에 등록하면 CLI가 인증된다. 이때 콘솔이 알려주는 fingerprint가 ~/.oci/config의 값과 같아야 한다.
💡 삽질 1:
UnicodeDecodeErroroci setup config도중에 한글 IME가 켜져 있으면 입력이 깨지면서 UnicodeDecodeError가 난다. 영문 입력 상태로 진행할 것.
4.2 재시도 스크립트
#!/bin/bash
export PATH="$HOME/bin:$PATH"
LOG="$HOME/create_instance.log"
WEBHOOK="여기에_디스코드_웹훅_URL"
RESULT=$(oci compute instance launch \
--availability-domain "AD_이름" \
--compartment-id "ocid1.tenancy.oc1..xxxx" \
--shape "VM.Standard.A1.Flex" \
--shape-config '{"ocpus":2,"memoryInGBs":12}' \
--image-id "ocid1.image.oc1..xxxx" \
--subnet-id "ocid1.subnet.oc1..xxxx" \
--boot-volume-size-in-gbs 100 \
--ssh-authorized-keys-file "$HOME/.ssh/id_ed25519.pub" \
--wait-for-state RUNNING 2>&1)
if echo "$RESULT" | grep -q "Out of host capacity\|Out of capacity"; then
echo "$(date '+%F %T') 용량부족, 다음에 재시도" >> "$LOG"
exit 1
elif echo "$RESULT" | grep -q '"lifecycleState": "RUNNING"'; then
echo "$(date '+%F %T') !!! 성공 !!!" >> "$LOG"
curl -s -H "Content-Type: application/json" \
-d '{"content": "🎉 A1 인스턴스 생성 성공!"}' "$WEBHOOK"
# 성공하면 크론에서 자기 자신 제거
crontab -l | grep -v 'create_instance.sh' | crontab -
exit 0
else
echo "$(date '+%F %T') 알수없는 결과:" >> "$LOG"
echo "$RESULT" >> "$LOG"
exit 2
fi
포인트 몇 가지:
--wait-for-state RUNNING— 성공하면 부팅 완료까지 기다린다. 몇 분간 멈춘 것처럼 보이는데 이게 오히려 좋은 신호다. 용량부족이면 몇 초 만에 바로 떨어진다.- 성공 시 크론 자기 제거 — 이거 없으면 성공한 뒤에도 계속 돌아서 인스턴스를 두 개, 세 개 만들려 든다. 무료 한도 초과 사고의 지름길.
- 알림은 디스코드 웹훅 — 처음엔 이메일을 생각했는데, 개인 서버에서 보내는 메일은 스팸 처리되기 일쑤다. 웹훅은 채널 설정 > 연동 > 웹훅에서 URL 하나 복사하면 끝이고
curl한 줄이면 폰으로 푸시가 온다.
크론 등록 전에 반드시 수동으로 한 번 돌려보고, bash -n script.sh로 문법도 체크할 것.
*/5 * * * * /home/user/create_instance.sh
4.3 상태 리포트 스크립트
로그만 쌓이면 뭐가 어떻게 되고 있는지 모른다. 그래서 로그를 집계해서 시간마다 디스코드로 보내는 daily_report.sh를 따로 만들었다. 결과를 네 가지로 분류했다.
- 용량 부족 (정상)
- 레이트 리밋 (429 / TooManyRequests)
- 네트워크 타임아웃
- 기타
이 분류가 나중에 결정적인 역할을 한다.
5. 크론을 돌리며 만난 삽질들
삽질 2: 크론에서 PATH를 못 찾음
수동 실행은 되는데 크론에서만 실패했다. 크론은 로그인 셸의 PATH를 상속받지 않는다. OCI CLI가 ~/bin에 깔려 있으면 크론은 그걸 못 찾는다.
해결: 스크립트 맨 위에 명시.
export PATH="$HOME/bin:$PATH"
삽질 3: DNS 해석 실패
네트워크 환경 문제로 DNS가 간헐적으로 죽었다. 임시로 /etc/resolv.conf를 고쳐봐야 재부팅하면 날아간다.
해결: systemd-resolved에 영구 설정.
sudo mkdir -p /etc/systemd/resolved.conf.d
sudo tee /etc/systemd/resolved.conf.d/dns.conf <<'EOF'
[Resolve]
DNS=8.8.8.8 1.1.1.1
EOF
sudo systemctl restart systemd-resolved
삽질 4: 리포트에 시도 횟수가 0으로 찍힘
분명 크론은 도는데 일일 리포트는 0건이라고 했다. 원인은 UTC/KST 불일치. 로그는 서버 로컬 시간(KST)으로 찍히는데 리포트 스크립트의 date는 다른 기준을 보고 있었다. 날짜 필터가 아무것도 못 잡은 것.
로그 타임스탬프와 집계 스크립트의 시간 기준은 반드시 맞춰야 한다.
삽질 5: 크론 주기를 짧게 할수록 손해
"5분마다는 너무 느린가?" 싶어서 3분으로 줄였다. 리포트를 보니 결과는 이랬다.
- 총 시도 횟수는 늘어남
- 그런데 그중 약 24%가 429(TooManyRequests)로 거절됨
- 유효 시도 수는 오히려 감소
OCI API에는 레이트 리밋이 있다. 무작정 자주 때리면 요청이 버려질 뿐이다. 5분으로 되돌렸다.
6. 그리고 5주 뒤 — PAYG 전환
이 모든 걸 갖춰놓고 4~5주를 돌렸다. 한 번도 안 잡혔다.
무료 계정은 A1 용량 배분에서 우선순위가 가장 낮다. 아무리 정교하게 재시도해도 줄 맨 뒤에 서 있으면 소용이 없다.
그래서 PAYG(Pay As You Go)로 전환했다. 핵심은 이거다.
PAYG로 전환해도 Always Free 리소스는 그대로 무료다. 없어지는 건 "무료 범위를 넘는 리소스를 못 만들게 막아주는 가드레일"이다. 무료 한도 안에서만 쓰면 청구액은 $0이다.
전환 순서
- 크론부터 정지 (
crontab -e에서 주석 처리) - 콘솔 상단 Upgrade → Billing > Upgrade and Manage Payment
- 카드 등록 (검증용 소액 승인이 잡혔다 취소됨) — 나는 Individual 계정 / Oracle Singapore 계약 주체로 진행
- 계정 상태가 Pay As You Go로 바뀐 것 확인
- 가드레일(Quota Policy) 먼저 설정
- 그다음 인스턴스 생성
결과
전환 후 스크립트를 수동으로 한 번 실행했다. 바로 잡혔다.
5주 vs 1회. 허무했다.
7. 가드레일 — 청구서 $0을 강제하기
PAYG의 유일한 리스크는 "실수로 유료 리소스를 만드는 것"이다. 방어선을 두 겹 쳤다.
7.1 Quota Policy — 이게 유일한 하드 차단
거버넌스 및 관리 > 할당량(Quotas)에서 정책을 만든다. 나는 always-free-guard라는 이름으로 만들었고, 구문은 이 5줄이다.
zero compute-core quotas in tenancy
set compute-core quota standard-a1-core-count to 4 in tenancy
set compute-core quota standard-a1-core-regional-count to 4 in tenancy
set compute-core quota standard-e2-core-count to 2 in tenancy
set block-storage quota total-storage-gb to 200 in tenancy
읽는 순서가 곧 동작 순서다. 첫 줄에서 모든 compute shape을 0으로 막고, 그다음 줄들에서 A1과 E2 Micro만 다시 열어준다. 그래서 첫 줄이 반드시 맨 위에 있어야 한다. 이 구조 덕분에 실수로 유료 shape을 고르면 생성 자체가 거부된다.
⚠️ 가장 중요한 함정:
standard-a1-core-regional-count
3번째 줄이 없으면, 즉standard-a1-core-count만 설정하면 인스턴스 생성이QuotaExceeded로 실패한다. 실제로 적용되는 건 regional-count 쪽이다. 나도 여기서 한 번 막혔다. 두 개 다 설정해야 한다.
7.2 Budget 알림 — 차단은 안 됨
비용 관리 > 예산에서 free-tier-alert를 만들고 $1/월, Actual Spend 100% 로 걸었다.
단, OCI에는 하드 스펜딩 캡이 없다. 예산은 알림만 준다. 진짜 차단은 Quota Policy뿐이다. 예산은 백업 경보 정도로 생각해야 한다.
7.3 조용히 돈이 새는 지점들
- 볼륨 백업 정책 — 인스턴스 생성 시 자동으로 켜지는 경우가 있는데 백업은 무료 대상이 아니다. 제일 흔한 케이스.
- 부트/블록 볼륨 합계 200GB 초과
- Boot volume VPU — 무료는 VPU 10(Balanced)까지. 이걸 20, 30으로 올리면 과금된다.
- 예약 공인 IP 2개 초과, 로드밸런서 추가
- Object Storage 20GB 초과, 커스텀 메트릭/로그
7.4 A1 코어를 4로 열어둘 것인가
Quota를 4코어로 잡아두면 나중에 확장 여지가 생기지만, 리스크가 있다. A1 무료 한도는 사실 "월 1,500 OCPU-시간" 이다.
- 2 OCPU × 24시간 × 30일 ≈ 1,460시간 → 무료 안
- 4 OCPU × 24시간 × 30일 ≈ 2,920시간 → 초과
"PAYG는 4/24 무료 유지"라는 얘기가 있지만 공식 확정이 아니다. $0을 확실히 강제하려면 quota를 2코어로 낮추는 게 맞다. 나는 인스턴스가 2코어라 당장 문제는 없지만, 이 부분은 열려 있는 구석으로 남겨뒀다.
8. 최종 구성과 인스턴스 초기 세팅
| 항목 | 값 |
| Shape | VM.Standard.A1.Flex |
| 스펙 | 2 OCPU / 12GB RAM |
| Boot Volume | 100GB (VPU 10) |
| OS | Ubuntu 24.04 aarch64 |
| 리전 | ap-osaka-1 / AD-1 |
| 접속 | ssh ubuntu@<공인IP> (ed25519 키) |
2 OCPU / 12GB로 간 건 사실 소극적인 선택이었다.
2026년 6월 15일부로 Always Free A1 한도가 4 OCPU/24GB에서 2 OCPU/12GB로 축소됐다.
그런데 이게 애매하다. 공식 문서는 "모든 테넌시(all tenancies)"에 적용된다고 써놨는데, 정작 오라클 지원팀 답변에서는 축소가 무료 티어에만 해당되고 기존 사용자나 PAYG 계정은 4/24를 그대로 쓸 수 있다고 한 사례들이 있다.
하지만 확정된 정보가 아니었고, 만약 아니었다면 A1 무료 한도인 월 1,500 OCPU-시간을 넘겨 과금이 시작되는 상황이었다.
솔직히 아쉽다. 몇 달만 일찍 계정을 만들었으면 축소 전 사양을 그대로 확보했을 거고, 지금도 사양은 절반이다.
영구 무료 티어라는 게 "지금 조건이 영원히 유지된다"는 뜻은 아니라는 걸 이번에 배웠다. 쓸 계획이 있으면 일단 확보해두는 게 맞다.
(나중에 4/24가 확실해지면 인스턴스를 다시 만들 수는 있다.)
방화벽은 두 겹이다
이게 처음 하는 사람이 제일 많이 막히는 지점이다. OCI는 방화벽이 두 겹이라 한쪽만 열면 접속이 안 된다.
(1) OCI Security List — 콘솔의 VCN > Security List에서 Ingress Rule 추가 (0.0.0.0/0, TCP 80/443)
(2) 인스턴스 내부 iptables — Ubuntu 이미지에 이미 REJECT 룰이 들어있다.
sudo iptables -L INPUT -n --line-numbers
sudo iptables -I INPUT <REJECT보다_위_번호> -p tcp --dport 80 -j ACCEPT
sudo iptables -I INPUT <REJECT보다_위_번호> -p tcp --dport 443 -j ACCEPT
sudo netfilter-persistent save # ← 이거 빼먹으면 재부팅 시 소멸
netfilter-persistent save를 빼먹으면 재부팅 후 원상복구된다. 확인은 간단히:
python3 -m http.server 80
그 외 초기 작업
sudo apt update && sudo apt upgrade -y # 첫 부팅 시 보안 업데이트 100개 이상 대기 중이었음
# swap 생성 — 기본 이미지는 swap 0B
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
12GB면 평소엔 swap이 없어도 무관하지만, 빌드나 DB를 돌리다 OOM이 한 번 나면 인스턴스가 통째로 뻗는다. 싸게 드는 보험이다.
마지막 검증
인스턴스 생성 2~3일 뒤 비용 관리 > 비용 분석에서 $0인지 확인. 청구 반영에 하루 이상 지연이 있어서 당일 $0은 아무 의미가 없다. 이게 진짜 검증 단계다.
9. 이 서버로 뭘 할 것인가
2 OCPU / 12GB. 개인용으로는 과할 만큼 넉넉하다. 대략 이렇게 나눠 쓸 생각이다.
| 용도 | 메모리 | 비고 |
| DB (MySQL) | 3~4GB | 버퍼 풀에 전체의 1/3 정도면 개인용엔 충분 |
| 게임 서버 | 2GB | 소규모 인원이면 이 정도로 돌아감 |
| 사이드 프로젝트 | 2GB | 자동화 워크플로우, 모니터링 등 |
| OS + 여유분 | 4GB | 시스템 구동 및 트래픽 스파이크 쿠션 |
여유분 4GB를 따로 잡아둔 게 핵심이다. 12GB를 꽉 채워 배분하면 순간적으로 트래픽이 튀거나 빌드가 돌 때 OOM으로 인스턴스가 통째로 뻗는다. 앞서 만든 swap 4GB는 이 쿠션의 마지막 방어선이다.
한 대에 여러 개를 올린다. Docker + 리버스 프록시(Caddy 또는 nginx)를 깔고, 서브도메인으로 앱을 나눠 붙이는 구조로 갈 생각이다. 앱 하나마다 서버를 따로 두는 건 무료 티어에서 애초에 불가능하고, 12GB면 컨테이너 몇 개는 충분히 감당한다.
멈춰 있던 개인 프로젝트를 올릴 자리로 쓴다. 지금까지 로컬에서만 돌리다 만 것들이 몇 개 있다. 배포할 곳이 없다는 게 손을 놓게 되는 이유 중 하나였다. 상시 켜져 있는 주소가 생기면 그 핑계는 없어진다.
미리 알아둘 제약
- aarch64(ARM) — x86 전용 이미지는 안 돈다. Docker 이미지의 arm64 지원 여부를 매번 확인해야 한다. 요즘은 대부분 멀티아키를 지원하지만 예외가 있다.
- 스토리지 총 200GB — 부트 볼륨에 이미 100GB를 썼으니 남은 여유는 100GB다.
- 볼륨 백업은 유료 — 백업이 필요하면 오브젝트 스토리지(20GB 무료)나 외부로 빼는 방법을 따로 생각해야 한다.
- 로드밸런서는 10Mbps 1개 — 개인 서비스엔 충분하지만 이걸 넘기면 과금이다.
구체적인 구성 — 프록시 선택, 도메인 연결, HTTPS, 컨테이너 배치, 배포 자동화 — 은 다음에 더 자세히 이야기 해보겠다.
정리 — 다시 한다면
- 처음부터 PAYG로 시작한다. 무료 계정으로 A1을 잡는 건 복불복이고, 나는 5주를 썼다. 무료 한도 안에서 쓰면 어차피 $0이다.
- PAYG 전환 직후, 인스턴스 만들기 전에 Quota Policy부터 건다.
standard-a1-core-regional-count를 반드시 포함한다. 없으면QuotaExceeded.- 크론 재시도 주기는 3분보다 5분이 낫다. 짧게 하면 429로 버려진다.
- Budget은 알림일 뿐 차단이 아니다. 하드 차단은 Quota뿐이다.
- 방화벽은 Security List + iptables 두 겹.
netfilter-persistent save잊지 말 것.
5주간의 크론이 헛수고였냐 하면, 그건 아니다. OCI CLI, 크론 환경의 PATH, systemd-resolved, API 레이트 리밋, 로그 집계까지 — 결국 서버를 운영하면서 계속 쓰게 될 것들을 그 과정에서 다 만졌다. 다만 다음 사람은 5주를 안 썼으면 해서 이 글을 남긴다.