Danh sách bài viết

Bài 37: Deploy lên Railway và Fly.io

Hai platform PaaS phổ biến cạnh Render: Railway tập trung vào UX đẹp và tích hợp database nhanh, Fly.io nổi bật ở edge compute và multi-region. Bài này trình bày cách deploy FastAPI app lên cả hai, cấu hình railway.json và fly.toml, CLI workflow, persistent volume, database tích hợp, và bảng so sánh 3 platform (Railway / Fly.io / Render) theo các tiêu chí thực tế.

27/05/2026
0 lượt xem
1

Mục tiêu bài học

Sau bài này bạn sẽ:

  • ✅ Hiểu Railway hoạt động thế nào và khi nào phù hợp
  • ✅ Deploy FastAPI app lên Railway qua Git + CLI
  • ✅ Cấu hình railway.json đúng chuẩn
  • ✅ Hiểu Fly.io dùng Firecracker microVM, không phải Docker container thuần
  • ✅ Deploy và scale multi-region với fly.toml
  • ✅ Chọn được platform phù hợp dựa trên bảng so sánh 3 PaaS
2

Railway là gì

Railway là PaaS deploy từ Git repo. Điểm nổi bật là dashboard UX rõ ràng, tích hợp database 1-click, và auto-detect build tool từ Dockerfile hoặc Nixpacks (build Python app mà không cần Dockerfile).

Service types

  • Web Service — serve HTTP, có URL public *.up.railway.app
  • Cron Job — chạy định kỳ theo schedule
  • Database — Postgres, MySQL, MongoDB, Redis (managed, cùng project)
  • Volume — persistent storage gắn vào service

Pricing (2024-2025)

  • Trial: $5 credit một lần (không cần card), sau đó cần upgrade
  • Pro: $5/tháng minimum + usage-based
  • RAM: $0.000463/GB-giờ
  • vCPU: $0.000231/vCPU-giờ
  • Ví dụ: 512 MB RAM, 0.5 vCPU, chạy 24/7 ≈ $5-7/tháng

Khi nào phù hợp

  • Prototype đến small production, cần DB và API trong cùng project
  • Ưu tiên UX dashboard hơn flexibility cấu hình
  • Team nhỏ, không muốn quản lý infra
3

Deploy lên Railway — từng bước

1. Tạo tài khoản và project

  1. Truy cập https://railway.app → đăng ký bằng GitHub
  2. New Project → Deploy from GitHub Repo
  3. Chọn repo, Railway tự detect:
  • Nếu có Dockerfile → build Docker image
  • Nếu không → Nixpacks tự phát hiện Python và build

2. Cấu trúc project mẫu

my-ai-api/
├── Dockerfile
├── railway.json
├── requirements.txt
├── main.py
└── .env.example

3. Cấu hình env vars

Sau khi Railway detect repo, vào tab Variables của service:

  • Add từng biến qua UI: OPENAI_API_KEY, MODEL_PATH, ...
  • Hoặc import từ file .env (Railway UI hỗ trợ paste bulk)
  • Railway tự inject biến PORT — dùng trong start command

4. Auto-deploy

Mỗi lần git push lên branch được config → Railway tự trigger build và deploy. Không cần CI/CD riêng cho workflow đơn giản.

4

railway.json — cấu hình build và deploy

File railway.json (hoặc railway.toml) ở root repo cho phép override cấu hình mặc định.

{
  "$schema": "https://railway.com/railway.schema.json",
  "build": {
    "builder": "DOCKERFILE",
    "dockerfilePath": "Dockerfile"
  },
  "deploy": {
    "startCommand": "uvicorn main:app --host 0.0.0.0 --port $PORT",
    "healthcheckPath": "/health",
    "restartPolicyType": "ON_FAILURE",
    "restartPolicyMaxRetries": 3
  }
}

Giải thích các field quan trọng

  • builder: "DOCKERFILE" — dùng Dockerfile, không dùng Nixpacks
  • builder: "NIXPACKS" — để Railway tự build Python (không cần Dockerfile)
  • startCommand — override CMD trong Dockerfile. Railway inject $PORT tự động
  • healthcheckPath — Railway ping path này để xác nhận service healthy trước khi route traffic
  • restartPolicyType: "ON_FAILURE" — tự restart khi crash, tối đa 3 lần

Ví dụ dùng Nixpacks (không có Dockerfile)

{
  "$schema": "https://railway.com/railway.schema.json",
  "build": {
    "builder": "NIXPACKS"
  },
  "deploy": {
    "startCommand": "uvicorn main:app --host 0.0.0.0 --port $PORT",
    "healthcheckPath": "/health",
    "restartPolicyType": "ON_FAILURE",
    "restartPolicyMaxRetries": 3
  }
}

Lưu ý: Nixpacks đọc requirements.txt hoặc pyproject.toml. Nếu cần CUDA hoặc system lib đặc biệt thì vẫn cần Dockerfile.

5

Railway CLI

# Cài đặt
npm i -g @railway/cli

# Đăng nhập (mở browser)
railway login

# Link thư mục hiện tại với Railway project
railway link

# Deploy thư mục hiện tại lên project đã link
railway up

# Xem log realtime
railway logs

# Chạy script local nhưng dùng env vars của project (useful cho migration)
railway run python migrate.py

# Xem danh sách env vars
railway variables

# Set env var
railway variables --set "MODEL_NAME=gpt-4o-mini"

Workflow thực tế

Với AI app có model weights nặng, thường dùng railway run để chạy script tải model hoặc migration DB từ local nhưng với đúng env vars của production — tránh phải SSH vào container.

# Chạy database migration với production credentials
railway run python -m alembic upgrade head

# Debug: in env vars của project
railway run env | grep DATABASE_URL
6

Database, Redis và Volume trên Railway

Database 1-click

  1. Trong project dashboard → New Service → chọn Postgres (hoặc MySQL / MongoDB / Redis)
  2. Railway tạo service DB trong cùng project
  3. Connection string (DATABASE_URL) tự động inject vào các service khác cùng project qua Reference Variable
# Reference Variable trong dashboard — Railway tự resolve
DATABASE_URL = ${{Postgres.DATABASE_URL}}
REDIS_URL    = ${{Redis.REDIS_URL}}

Không cần copy-paste URL thủ công. Nếu DB service được recreate (URL đổi), reference variable tự cập nhật.

Persistent Volume

Dùng khi cần lưu model weights hoặc data file giữa các lần deploy:

  1. Service settings → Volumes → Add Volume
  2. Chỉ định mount path, ví dụ /data
  3. Volume gắn với service, không share giữa các service khác nhau

Lưu ý quan trọng: Volume trên Railway gắn với 1 region. Khi migrate region, volume không tự di chuyển theo — cần export/import data thủ công.

7

Railway specifics — sleep, networking, environments

Service sleep

  • Trial / Hobby: service sleep sau thời gian idle (không có request). Cold start khi có request mới (~1-5s tùy image size)
  • Pro: không sleep, service chạy liên tục

Với AI inference API, sleep không chấp nhận được trong production vì model load time thêm vào cold start. Dùng Pro tier hoặc ping định kỳ.

Private networking

Các service trong cùng project communicate với nhau qua private DNS (không ra Internet):

# Service "api" gọi service "postgres" trong cùng project
# URL private: postgres.railway.internal:5432
# Tự động inject khi dùng Reference Variable

Multiple environments

Railway hỗ trợ nhiều environment (Production, Staging, PR Preview) trong 1 project. Mỗi environment có bộ env vars riêng và có thể có service riêng. Hữu ích khi cần test model version mới trước khi deploy production.

8

Fly.io là gì

Fly.io là PaaS với khả năng deploy nhiều region đồng thời. App chạy trên Firecracker microVM — lightweight virtual machine do Amazon phát triển cho AWS Lambda, cho boot time nhanh hơn VM thông thường nhưng isolation tốt hơn container thuần.

Điểm khác biệt so với Railway

  • Multi-region native: 1 lệnh scale app ra nhiều region (Singapore, Tokyo, Frankfurt, ...)
  • Anycast routing: request tự route đến region gần user nhất
  • Firecracker microVM: không phải Docker container — mỗi Machine là 1 VM nhỏ
  • CLI-first: workflow chủ yếu qua terminal, dashboard ít tính năng hơn Railway
  • GPU support: A10, A100 (đắt nhưng khả dụng)

Pricing (2024-2025)

  • Shared vCPU: $0.0000022/vCPU-giây
  • RAM: theo block (256MB increments)
  • Free allowance: 3 shared-cpu-1x VMs 256MB RAM, 3GB volume
  • Sau free tier: usage-based, không có monthly minimum

Khi nào phù hợp

  • App cần latency thấp toàn cầu (inference gần user)
  • Cần edge compute hoặc multi-region stateless API
  • Dùng GPU inference mà không muốn setup Kubernetes
  • Quen CLI workflow, không cần dashboard phức tạp
9

Deploy lên Fly.io — từng bước

1. Cài flyctl (Fly CLI)

# macOS / Linux
curl -L https://fly.io/install.sh | sh

# macOS (Homebrew)
brew install flyctl

# Windows (PowerShell)
pwsh -Command "iwr https://fly.io/install.ps1 -useb | iex"

2. Đăng ký và đăng nhập

# Tạo tài khoản mới
fly auth signup

# Hoặc đăng nhập nếu đã có tài khoản
fly auth login

3. Khởi tạo app

# Trong thư mục project
fly launch

fly launch hỏi interactive:

  • App name (hoặc auto-generate)
  • Region (primary region)
  • Có cần Postgres/Redis không

Kết quả: tạo file fly.toml và register app trên Fly.

4. Deploy

# Build image và deploy
fly deploy

# Theo dõi logs trong lúc deploy
fly logs

# Mở app trên browser
fly open

# Kiểm tra status machines
fly status

5. Xem thông tin app

# Danh sách machines đang chạy
fly machines list

# SSH vào machine đang chạy
fly ssh console

# Xem logs theo thời gian thực
fly logs --tail
10

fly.toml — cấu hình chi tiết

app = "my-ai-api"
primary_region = "sin"  # Singapore

[build]
  dockerfile = "Dockerfile"

[http_service]
  internal_port = 8000
  force_https = true
  auto_stop_machines = "stop"   # dừng machine khi idle
  auto_start_machines = true    # khởi động lại khi có request
  min_machines_running = 0      # cho staging; đặt 1 cho prod

[env]
  LOG_LEVEL = "info"
  # Không đặt secret ở đây — dùng fly secrets set

[[vm]]
  cpu_kind = "shared"
  cpus = 1
  memory_mb = 1024

Giải thích các section

  • primary_region — region mặc định khi deploy. Mã region: sin (Singapore), hkg (Hong Kong), nrt (Tokyo), fra (Frankfurt), iad (Virginia)
  • auto_stop_machines = "stop" — machine dừng khi không có request. Options: "off" (không auto-stop), "stop", "suspend" (nhanh hơn stop, chỉ supported trên một số region)
  • auto_start_machines = true — tự khởi động machine khi có request vào
  • min_machines_running = 0 — số machine tối thiểu luôn chạy. Đặt 1 để tránh cold start trong production
  • cpu_kind = "shared" — shared CPU (rẻ hơn). Dùng "performance" cho CPU-intensive inference

Health check

[[http_service.checks]]
  grace_period = "10s"
  interval = "30s"
  method = "GET"
  path = "/health"
  timeout = "5s"

Fly dùng health check để xác định machine healthy trước khi route traffic. Nếu app khởi động lâu (load model weights), tăng grace_period lên 30s-60s.

11

Fly Machines — scale và multi-region

Trên Fly.io, mỗi instance app là 1 Machine. Machine = Firecracker microVM chạy image của bạn. Fly quản lý lifecycle: start, stop, route request.

Scale số lượng và region

# Scale lên 3 machine, mỗi region 1 machine
fly scale count 3 --region sin,hkg,nrt

# Scale chỉ trong 1 region
fly scale count 2 --region sin

# Xem resource hiện tại
fly scale show

# Thay đổi RAM và CPU
fly scale vm shared-cpu-1x --memory 2048

Anycast routing

Fly dùng anycast IP — cùng 1 IP nhưng request được route đến region gần nhất (về mặt network). Người dùng ở Singapore sẽ hit machine sin, người dùng ở Nhật hit nrt.

Với AI inference, nếu model nhỏ có thể load vào memory tất cả region thì multi-region giúp giảm latency đáng kể. Nếu model lớn (>4GB), cần cân nhắc cost.

Cold start

Khi auto_stop_machines = "stop"min_machines_running = 0, machine dừng khi idle và khởi động lại khi có request đầu tiên. Cold start bao gồm:

  • Firecracker boot: ~300ms
  • App startup + model load: phụ thuộc vào code

Tổng cold start thường 1-5s. Cho production với yêu cầu latency thấp, đặt min_machines_running = 1.

12

Secrets và Volume trên Fly.io

Secrets

Không đặt credentials trong fly.toml hay Dockerfile. Dùng fly secrets:

# Set secret (inject vào app như env var)
fly secrets set OPENAI_API_KEY=sk-proj-...
fly secrets set DATABASE_URL=postgresql://...

# Set nhiều cùng lúc
fly secrets set OPENAI_API_KEY=sk-... MODEL_NAME=gpt-4o-mini

# Xem danh sách secrets (chỉ tên, không thấy value)
fly secrets list

# Xóa secret
fly secrets unset OPENAI_API_KEY

Sau khi set secret, Fly tự restart app để inject giá trị mới. Nếu muốn set mà không restart ngay: fly secrets set --stage rồi deploy sau.

Persistent Volume

Volume là block storage gắn với 1 region. Dùng để lưu model weights hoặc data:

# Tạo volume 10GB ở Singapore
fly volumes create model_data --region sin --size 10

# Liệt kê volumes
fly volumes list

Mount volume vào app qua fly.toml:

[[mounts]]
  source = "model_data"
  destination = "/data"

App sau khi deploy có thể đọc/ghi vào /data. Thường dùng entrypoint script để download model weights vào volume lần đầu:

#!/bin/sh
# entrypoint.sh — chạy trước khi start uvicorn
if [ ! -f /data/model.bin ]; then
  echo "Downloading model..."
  python download_model.py --output /data/model.bin
fi
exec uvicorn main:app --host 0.0.0.0 --port 8080

Quan trọng: Volume chỉ accessible trong 1 region. Nếu scale sang region khác (nrt, hkg), mỗi region cần volume riêng — volume không share giữa các region.

13

Database và GPU trên Fly.io

Fly Postgres

# Tạo Postgres cluster
fly postgres create --name my-ai-db --region sin

# Attach vào app (tự inject DATABASE_URL)
fly postgres attach my-ai-db

# Kết nối trực tiếp để debug
fly postgres connect -a my-ai-db

Fly Postgres là app Postgres chạy trên Fly Machines, không phải managed service như AWS RDS. Bạn chịu trách nhiệm backup và patching. Phù hợp cho prototype và staging. Production nghiêm túc nên dùng managed Postgres (Neon, Supabase, hoặc cloud provider).

Upstash Redis

# Tạo Redis (tích hợp Upstash)
fly redis create

# Upstash Redis là managed service, tự inject REDIS_URL

GPU (A10 / A100)

Fly có GPU support nhưng không phải tất cả region đều có. GPU machines đắt hơn đáng kể:

# Deploy với GPU A10
fly deploy --vm-gpu-kind a10

Hoặc trong fly.toml:

[[vm]]
  gpu_kind = "a10"
  cpus = 8
  memory_mb = 32768

GPU region: chủ yếu ord (Chicago) và iad (Virginia) tính đến 2025. Kiểm tra availability tại docs.fly.io trước khi plan.

14

So sánh Railway vs Fly.io vs Render

Tiêu chí Railway Fly.io Render
Pricing model $5/tháng + usage-based (RAM/CPU) Usage-based, không monthly minimum Fixed tier ($7/$25/...) hoặc usage-based
UX / Dashboard Tốt — visual graph service dependencies Đơn giản hơn — CLI-first Tốt — quen thuộc, dễ dùng
CLI experience Tốt (railway CLI) Rất tốt (flyctl), nhiều subcommand Có CLI nhưng ít dùng hơn
Multi-region Không native (1 region / service) Có — anycast, scale per region Không native
GPU support Không Có (A10, A100) — hạn chế region Có (A100 40GB) — từ 2024
Persistent volume Có (per service) Có (per region) Có (per service)
Managed database Postgres, MySQL, MongoDB, Redis (1-click) Fly Postgres (self-managed trên Fly), Upstash Redis Postgres, Redis managed
Free tier $5 credit một lần (trial) 3 VMs 256MB + 3GB volume Free tier (sleep sau idle)

Khi nào chọn cái nào

  • Railway: prototype nhanh cần API + DB trong cùng project, team không muốn config nhiều, pricing usage-based ổn định
  • Fly.io: cần multi-region hoặc edge inference, dùng GPU, quen CLI workflow, muốn kiểm soát networking chi tiết
  • Render: muốn dashboard đơn giản, Postgres managed chắc chắn, hoặc cần GPU A100 ngay
15

Common pitfalls

Railway

  • Free trial credit hết tháng → service tắt đột ngột. Đặt spending limit rõ ràng trong billing settings. Không nên dùng trial cho demo người dùng thực.
  • Region change gây downtime nhỏ. Khi thay đổi region của service, Railway migrate service sang region mới — có downtime ngắn. Lên kế hoạch ngoài giờ cao điểm.
  • Secret set nhưng service không nhận. Sau khi thêm env var hoặc secret, cần trigger redeploy thủ công nếu auto-deploy không kích hoạt. Railway không tự restart service running.
  • Nixpacks build thành công nhưng startup lỗi. Nixpacks đoán đúng Python version nhưng có thể bỏ sót system library (ví dụ libgomp cho PyTorch). Khi gặp lỗi runtime, chuyển sang Dockerfile cho kiểm soát tốt hơn.

Fly.io

  • Auto-stop machines → cold start bất ngờ trong production. Mặc định min_machines_running = 0. Đặt min_machines_running = 1 cho production để luôn có ít nhất 1 machine warm.
  • Volume chỉ accessible trong 1 region. Nếu scale sang region mới, machine ở region đó không thể đọc volume của region khác. Cần tạo volume riêng cho mỗi region và cơ chế sync data nếu cần.
  • Health check timeout khi load model nặng. App load model 2GB mất 30-60s. Nếu grace_period quá ngắn, Fly đánh dấu machine unhealthy và kill trước khi app sẵn sàng. Tăng grace_period hoặc lazy-load model sau startup.
  • Secret set mà không deploy lại → app không nhận giá trị mới. fly secrets set mặc định trigger restart. Nhưng nếu dùng --stage, cần fly deploy sau để apply.
  • GPU availability không phải lúc nào cũng có. GPU machines có quota giới hạn. Nếu deploy GPU app lần đầu, mở support ticket để request quota trước.
16

Tóm tắt

✅ Railway: PaaS đơn giản, UX tốt, DB tích hợp 1-click, phù hợp prototype → small production
railway.json cấu hình builder (Dockerfile / Nixpacks), start command, healthcheck
✅ Railway CLI: railway up deploy, railway run chạy script với production env
✅ Fly.io: edge compute, multi-region, Firecracker microVM, CLI-first
fly.toml: auto_stop_machines, min_machines_running, [[vm]] resources
fly secrets set cho credentials, không đặt trong fly.toml
✅ Volume trên Fly gắn region — mỗi region cần volume riêng
✅ Production: đặt min_machines_running = 1grace_period đủ cho model load

17

Bài tiếp theo

Bài 38: Deploy lên AWS / GCP (overview) — EC2, App Engine, Cloud Run, ECS Fargate: so sánh khi nào rời PaaS sang cloud provider thực sự.