Autoscaling สำหรับ Thai SaaS — เมื่อไหร่ scale, scale เท่าไหร่, ระวังอะไร
"เว็บล่ม" = สูญเสีย customer trust + revenue. แต่ scale เผื่อตลอด = burn money
When to scale
Signals จาก NirvaDeploy metrics
ดูที่ /dashboard/[projectId]/[serviceId] — ถ้าเห็นข้อใดข้อหนึ่งบ่อยๆ → ต้อง scale:
| Signal | Threshold | สาเหตุ |
|---|---|---|
| 💻 CPU peak | > 80% sustained 5 min | CPU-bound (parsing, ML, compression) |
| 🧠 RAM peak | > 85% sustained | Memory leak หรือ workload โต |
| 🌐 Network RX | > 50 MB/s | Traffic spike (sale, viral content) |
| ⏱ p95 latency | > 1s | Service slow under load |
| 💥 Crashes/hour | ≥ 1 | OOM (out of memory) kills |
ใช้ Claude สั่งดู:
"CPU + RAM ของ service web ตอนนี้เท่าไหร่"
How to scale บน NirvaDeploy
Phase 1-3: Manual scaling (ทำเอง)
ใช้ MCP tool หรือ web dashboard:
"Scale service web เป็น 3 replicas"nirvadeploy_scale_service(service_id="svc-001", replicas=3)Plan limits:
- Free / Hobby: replicas = 1 เท่านั้น
- Pro: 1-10 replicas
- Business: 1-10 replicas (Phase 2.x: unlimited)
- Enterprise: custom
Phase 4: Auto-scaling (Phase 4 own infra)
# .nirvadeploy.yaml (Phase 4+)
service: web
autoscale:
min_replicas: 2
max_replicas: 10
target_cpu: 70 # scale up when avg CPU > 70%
target_memory: 80 # scale up when avg RAM > 80%
scale_up_cooldown: 60s
scale_down_cooldown: 300s # ระวัง flap — รอนานก่อน scale downPhase 4 plan: ใช้ Kubernetes HPA หรือ Dokploy native scaler (TBD)
Capacity planning
Calculate baseline
Step 1: Measure single replica capacity
# Load test ด้วย vegeta (ลง 1 replica ก่อน)
echo "GET https://your-service.nirvadeploy.com/api/products" \
| vegeta attack -duration=60s -rate=50 \
| vegeta reportResult might be:
- p50 latency: 80ms
- p95 latency: 250ms
- p99 latency: 800ms
- Success rate: 99.5%
- Throughput sustained: ~45 req/s before degradation
Step 2: Plan for peak
- Average traffic: 1,000 req/min = 17 req/s → 1 replica พอ
- Peak traffic (lunch 11am-1pm): 5,000 req/min = 83 req/s → ต้อง 2-3 replicas
- Sale event (11.11 noon): 20,000 req/min = 333 req/s → ต้อง 8-10 replicas
Rule of thumb:
replicas = ceil(peak_qps / single_replica_capacity_qps × 1.5)
^^^^
50% headroom for safetyDatabase bottleneck (ระวัง!)
Scale app ไม่ scale DB — ถ้า Postgres กลายเป็น bottleneck:
- 10 replicas × 20 connections = 200 conns → exceed default Postgres limit (100)
- Solution 1: use connection pooler (PgBouncer) — recommended
- Solution 2: tune
max_connectionsใน Postgres config - Solution 3: read replicas (Phase 4)
# Use SQLAlchemy pool settings
engine = create_async_engine(
DATABASE_URL,
pool_size=5, # per replica
max_overflow=10,
pool_recycle=3600, # recycle conn every 1 hr
pool_pre_ping=True, # ping before use (avoid stale)
)Common pitfalls
1. "Scale up = ลดราคา"
❌ Wrong. Scale up = ราคาเพิ่ม (โดยตรงตาม replicas × tier price).
✅ Right strategy:
- Optimize code first (profile slow queries)
- Add caching (Redis) before scaling out
- Scale only when optimization ไม่พอ
2. State in memory (broken at scale)
ถ้า service เก็บ session ใน memory → user request 1 ไป replica A, request 2 ไป replica B → session lost.
❌ Bad:
sessions = {} # in-memory dict
@app.post("/login")
async def login(...):
sessions[user_id] = token # only replica A เห็น✅ Good:
import redis
r = redis.from_url(REDIS_URL)
@app.post("/login")
async def login(...):
await r.setex(f"session:{user_id}", 3600, token) # ทุก replica เห็น3. ลืม graceful shutdown
ตอน scale down → service ถูก SIGTERM. ต้อง:
- หยุดรับ request ใหม่
- รอ in-flight requests จบ (ปกติ 30s grace)
- Flush logs / close DB connections
@app.on_event("shutdown")
async def shutdown():
await db.disconnect() # close PG pool
await redis.close()
await job_queue.shutdown(wait=True)4. Cold start spikes
Scale up จาก 1→5 → 4 new replicas need to:
- Pull container image (5-15s)
- Boot Python interpreter (1-2s)
- Load deps + warm caches (5-30s ขึ้นกับ app)
Total cold start: 11-47s — ระหว่างนี้ traffic ตกที่ replicas เก่าทั้งหมด → อาจ overload
Mitigation:
- Pre-warm before peak (scale up 30 min ก่อน)
- Health check endpoint ที่ smart (return 200 only หลัง warm-up)
warmed_up = False
@app.on_event("startup")
async def warmup():
global warmed_up
# Preload models, warm caches
await preload_db_connection_pool()
await load_ml_model()
warmed_up = True
@app.get("/health")
async def health():
if not warmed_up:
return JSONResponse({"status": "warming"}, status_code=503)
return {"status": "ok"}NirvaDeploy load balancer respect 503 → ไม่ส่ง traffic จนกว่า /health = 200
5. Scale-down flapping
หาก traffic ขึ้นๆ ลงๆ → scaler ขยับ replicas ขึ้นลงตลอด → unstable
Mitigation:
scale_down_cooldownยาวกว่าscale_up_cooldown(5 min vs 1 min)- Min replicas ≥ 2 (สำหรับ HA แล้วยัง — single point of failure)
Cost optimization
Idle hour scaling
Thai SaaS pattern: ปกติ traffic peak 11am-2pm + 6pm-10pm. Night idle.
Strategy: Scale down เหลือ 1 replica ตอน 2am-7am.
# Phase 4+ scheduled scaling
autoscale:
schedule:
- cron: "0 7 * * *" # 7am bangkok
min_replicas: 3
- cron: "0 2 * * *" # 2am bangkok
min_replicas: 1Saves 30-40% on infra cost without affecting users
Database read replicas
Heavy read workload → main DB CPU 90%? — add read replica:
# Route reads to replica, writes to primary
write_engine = create_engine(WRITE_DB_URL)
read_engine = create_engine(READ_DB_URL)
async def get_products():
async with read_engine.connect() as conn:
return await conn.execute("SELECT * FROM products") # ↓ load on primaryPhase 4: NirvaDeploy will offer managed read replicas for Postgres add-on
CDN for static assets
Don't serve static from your service:
// Next.js: assets auto-uploaded to Cloudflare R2 + served via CDN
// (Phase 2.x — NirvaDeploy will integrate)Saves bandwidth + reduces service load drastically
Monitoring checklist
ก่อน scale up — เช็คก่อน:
- [ ] Slow queries ใน database (use
EXPLAIN ANALYZE) - [ ] N+1 queries (ORM lazy loading without eager)
- [ ] Synchronous LLM calls blocking event loop (use async)
- [ ] Large response payload (return only needed fields)
- [ ] Missing index (Postgres
pg_stat_user_indexes) - [ ] Logging too verbose (writes log = slow)
- [ ] Inefficient JSON serialization (use
orjsoninstead of stdlib)
Often 1 optimization > 5 replicas
Summary
- Measure first — single-replica capacity via load test
- Optimize before scaling — 1 good index > 3 extra replicas
- Watch DB bottleneck — connection pool sizing
- Stateless services — sessions in Redis, not memory
- Graceful shutdown — handle SIGTERM properly
- Schedule for traffic patterns — Thai business hours
- CDN for static — don't waste replica CPU on assets
NirvaDeploy MCP makes manual scaling trivial:
"Scale service api เป็น 5 replicas, ขยายตามดู metrics"
Phase 4 จะมี auto-scaling — Dokploy + Hetzner native HPA
คำถาม performance? Email [email protected] — เรา reply within 12h
ชอบบทความนี้?
ลอง NirvaDeploy ฟรี
Free tier ตลอดชีพ · ไม่ต้องใส่บัตร · Sign in ด้วย Line/Google
เริ่มเลย →