Railway deploy guide — P0.E5

Step-by-step deploy XTalk server lên Railway staging. ~30 phút setup lần đầu.

Cost estimate

  • Railway: $5 free credit/tháng (đủ test 1 service + Postgres + Redis ~500h/tháng) → sau hết: $5-15/tháng
  • Sentry: free tier 5k events/tháng
  • OpenRouter: pay-per-use (~$0.001/intent với Haiku)
  • Domain xtalk.app (optional): $15/năm

Tổng: $0/tháng đầu, scale lên $20-30 khi vượt free tier.

Bước 1 — Tạo Railway account + CLI (5 phút)

  1. Đăng ký https://railway.app — sign in qua GitHub (recommend, dễ link repo)
  2. Cài CLI:
    brew install railway   # Mac
    # hoặc: npm i -g @railway/cli
    
  3. Login:
    railway login
    

    Browser sẽ mở → authorize.

Bước 2 — Tạo project + services (5 phút)

cd /Users/xuyenngo/Projects/AICode/XTalk
railway init
# → Choose "Empty Project", đặt tên "xtalk-staging"

Sau đó add 2 services qua web UI hoặc CLI:

Web UI (dễ hơn):

  1. Mở project trên railway.app dashboard
  2. Click “+ New” → “Database” → “PostgreSQL” → tự deploy
  3. Click “+ New” → “Database” → “Redis” → tự deploy
  4. Click “+ New” → “Empty Service” → đặt tên “api”
  5. Vào service “api” → Settings → Source → Connect repo xuyenthanhngo/XTalk → branch main
  6. Settings → Root Directory = / (monorepo root — nixpacks tự detect)

Bước 3 — Set environment variables (5 phút)

Vào service api → tab Variables → add các biến:

# Database — Railway tự gen khi link Postgres service.
# Click "Add Reference" → chọn Postgres service → DATABASE_URL
DATABASE_URL=$
REDIS_URL=$

# Required secrets (anh tự gen — DÙNG 3 chuỗi KHÁC NHAU):
#   openssl rand -base64 48  ← chạy 3 lần
JWT_SECRET=<chuỗi 1 — ≥ 32 ký tự>
JWT_REFRESH_SECRET=<chuỗi 2 — ≥ 32 ký tự>
DEVICE_SECRET_KMS_KEY=<chuỗi 3 — 32 byte base64>

# LLM (em đã có key)
OPENROUTER_API_KEY=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
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
LLM_MODEL_DEFAULT=anthropic/claude-haiku-4-5
LLM_MODEL_FALLBACK=anthropic/claude-sonnet-4-6

# Server
PORT=3000
NODE_ENV=production

# Optional — Sentry (em hướng dẫn dưới)
# SENTRY_DSN=

Tip: dùng railway variables set KEY=value từ CLI cũng được.

Bước 4 — Deploy lần đầu (~3 phút)

Railway sẽ tự deploy khi anh connect repo + push commit. Lần đầu sẽ:

  1. Detect nixpacks.toml ở root
  2. Run pnpm install --frozen-lockfile
  3. Build shared + server
  4. Start: migrate DB → pnpm --filter @xtalk/server start

Theo dõi log: Service api → tab Deployments → click latest → View Logs.

Expected output cuối:

INFO  Running migrations...
INFO  Migrations completed.
INFO  XTalk server v0.1.0 listening on :3000

Bước 5 — Tạo public domain (1 phút)

Service api → Settings → Networking → “Generate Domain” → Railway sinh URL kiểu xtalk-api-production.up.railway.app.

Test:

curl https://xtalk-api-production.up.railway.app/health
# {"status":"ok","timestamp":"2026-05-14T..."}

Bước 6 — Setup Sentry (10 phút, optional)

  1. Đăng ký https://sentry.io (free tier)
  2. Create project: platform = Node.js, name = xtalk-server
  3. Copy DSN từ Settings → Client Keys: https://abc@xyz.ingest.sentry.io/123
  4. Railway → api service → Variables → add SENTRY_DSN=... → redeploy.
  5. Test: vào /auth/guest với body sai → trigger 400 → check Sentry dashboard.

Cho Flutter client:

  1. Sentry → Create project, platform = Flutter, name = xtalk-app
  2. Copy DSN → build app với:
    flutter run --dart-define=SENTRY_DSN=https://...@xyz.ingest.sentry.io/456
    

Bước 7 — Update Flutter client API_BASE

Build APK/AAB với production URL:

cd app
flutter build appbundle --release \
  --dart-define=API_BASE=https://xtalk-api-production.up.railway.app \
  --dart-define=SENTRY_DSN=https://...@sentry.io/456

AAB này upload lên Play Console internal track thay cho file Cloudflare tunnel.

Bước 8 — GitHub Actions auto-deploy (optional, 5 phút)

Railway tự deploy khi push lên branch đã connect (main). Nếu muốn manual control:

  1. Railway → Project Settings → Tokens → Create Token → copy
  2. Repo GitHub → Settings → Secrets → add RAILWAY_TOKEN
  3. Em đã có CI workflow ở .github/workflows/ci.yml. Add deploy job:
    deploy:
      needs: ci-pass
      if: github.ref == 'refs/heads/main'
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v4
        - run: npm i -g @railway/cli
        - run: railway up --service api
          env:
            RAILWAY_TOKEN: $
    

Verify everything end-to-end

  1. Đóng Cloudflare tunnel (Mac terminal): Ctrl+C
  2. Mở app trên simulator/phone với API_BASE = Railway URL
  3. Chat tab → gõ “bật to lên” → server (Railway) → LLM → response
  4. Check Railway logs cho POST /v1/intent log entries
  5. Check Sentry nếu có lỗi nào trigger

Rollback

Railway giữ 5 deployments cuối → vào Deployments → click old → Redeploy.

Cost monitoring

Railway → Project → Usage tab xem dollars/giờ.

Nếu vượt $5 free credit → setup billing limit ở Account → Billing → Usage Limits.


TLDR cho anh:

  1. brew install railway && railway login
  2. railway init → tạo project
  3. Web UI: add Postgres + Redis + Empty service “api”
  4. Connect repo branch main + set env vars (em đã liệt kê ở trên)
  5. Đợi 3 phút → /health ok
  6. Gen public domain → update API_BASE Flutter build
  7. (Optional) Sentry account → DSN → redeploy

Stuck ở step nào báo em.