초기 Rails 앱 디플로이 SaaS 비교
Fly.io, Render, DigitalOcean, Fargate, Railway, Heroku 등 실전 비교
Rails 앱을 처음 배포할 때 가장 고민되는 것이 "어디에 올릴까?"입니다. Heroku가 무료 티어를 폐지한 이후, Fly.io, Render, Railway 등 대안이 급부상했습니다.
비교 기준: "1인/소규모 팀의 Rails 8 + SQLite 프로젝트를 가장 빨리, 가장 싸게, 가장 안정적으로 배포할 수 있는 곳은 어디인가?"
| 서비스 | 최소 비용 | Docker | SQLite 지원 | 볼륨(영구 스토리지) | Rails 8 친화도 | 적합한 단계 |
|---|---|---|---|---|---|---|
| Fly.io | ~$5/월 | O | O (볼륨 마운트) | O | 높음 (공식 지원) | MVP → 프로덕션 |
| Render | 무료 티어 있음 | O | 제한적 (영구 디스크 없음) | X (무료) / O (유료) | 중간 | MVP |
| DigitalOcean (Droplet) | $4~6/월 | O (직접 설정) | O (로컬 디스크) | O (로컬 SSD) | 높음 (자유도 최고) | 프로덕션 |
| Railway | 사용량 과금 | O | 제한적 | 제한적 | 중간 | 빠른 프로토타이핑 |
| Heroku | $7/월~ | O | X (ephemeral filesystem) | X | 낮음 (PostgreSQL 강제) | 레거시 |
| AWS Fargate | $10+/월 | O | 복잡 (EFS 필요) | O (EFS) | 낮음 (복잡) | 대규모 |
Fly.io vs DigitalOcean — 실전 운영 비교
이 프로젝트는 Fly.io와 DigitalOcean Droplet을 동시 운영하고 있습니다. Rails 8 + SQLite + Solid Queue 구성에서 체감한 차이:
Fly.io 장점: 배포가 간단 (fly deploy 한 줄), 글로벌 엣지 네트워크, SSL 자동, Kamal 대비 설정이 적음
Fly.io 단점: 볼륨 I/O 레이턴시가 DigitalOcean보다 높음 → SQLite database locked 에러 빈발 (아래 상세 설명)
DigitalOcean 장점: 로컬 SSD로 I/O 성능이 안정적, 가격 대비 성능 우수, 완전한 제어권
DigitalOcean 단점: 서버 설정을 직접 해야 함 (Kamal 또는 Docker Compose), SSL은 직접 설정 또는 Cloudflare
⚠️ Fly.io + SQLite + Solid Queue = "database is locked" 문제
Rails 8의 SQLite + Solid Queue 구성을 Fly.io에서 운영하면, "SQLite3::BusyException: database is locked" 에러가 빈번하게 발생할 수 있습니다. DigitalOcean에서는 같은 구성에서 거의 발생하지 않는 에러입니다.
원인 분석:
SQLite는 동시에 1개의 writer만 허용합니다(WAL 모드에서도 write lock은 1개). Solid Queue는 지속적으로 DB에 write하는데, 웹 프로세스도 동시에 write하면 lock 충돌이 발생합니다.
Fly.io에서 특히 심한 이유:
- Fly.io 볼륨 I/O 성능: DigitalOcean Droplet은 로컬 SSD, Fly.io는 네트워크 연결 볼륨(Network-attached volume). 같은 busy_timeout 설정이라도 lock 잡는 시간이 다르다:
DigitalOcean: lock 잡는 시간 ms 단위 → timeout 안에 lock 해제됨 → 에러 없음
Fly.io: lock 잡는 시간 수십~수백ms → timeout 초과 → BusyException 발생
둘 다 WAL 모드 + 동일한 timeout 설정인데, I/O latency 차이 하나로 결과가 완전히 달라진다
2. Solid Queue + Web이 같은 DB 파일 공유: 두 프로세스가 동시에 write하면 BusyException 발생. DigitalOcean의 로컬 SSD는 latency가 낮아서 lock이 빨리 풀리지만, Fly.io 볼륨은 네트워크 스토리지라 lock 보유 시간이 김
3. WAL 모드 미설정 또는 busy_timeout 부족: Rails 8은 기본으로 WAL 모드를 설정하지만, busy_timeout(lock 대기 시간)이 짧으면 에러 발생. 기본값 5000ms로는 부족할 수 있음
해결 방법:
# config/database.yml
production:
adapter: sqlite3
database: /data/production.sqlite3
pool: 5
timeout: 10000 # busy_timeout 10초로 증가
pragmas:
journal_mode: wal
synchronous: normal
busy_timeout: 10000
busy_timeout을 10000ms(10초)로 증가Solid Queue용 DB를 별도 파일로 분리하는 것도 효과적 (queue.sqlite3, cache.sqlite3, cable.sqlite3)
그래도 문제가 계속되면 DigitalOcean Droplet + Kamal 조합이 SQLite 운영에는 더 안정적
구조 다이어그램
핵심 포인트
Dockerfile 작성 (Rails 7.1+는 자동 생성)
환경변수 설정 (DATABASE_URL, SECRET_KEY_BASE, RAILS_MASTER_KEY)
PostgreSQL/SQLite 데이터베이스 설정
에셋 프리컴파일 + 이미지 빌드
PaaS에 배포 (git push 또는 Docker push)
커스텀 도메인 + SSL 설정
모니터링 + 로그 확인