🚀 Production Deployment Guide — ChienLe Labs¶
Hướng dẫn từng bước triển khai Full-Stack App lên môi trường Production sử dụng 100% Free-tier.
Tech Stack & Resources¶
| Thành phần | Công nghệ | Nền tảng Cloud | Domain |
|---|---|---|---|
| Frontend | Next.js (React) | Cloudflare Pages (Free) | chienle.dev |
| Backend | FastAPI (Python) | Northflank (Free) | api.chienle.dev |
| Database | PostgreSQL | Supabase (Free) | Managed by Supabase |
| File Storage | S3-compatible | Cloudflare R2 (Free) | CDN URL |
| DNS & SSL | — | Cloudflare (Free) | chienle.dev |
| Source Code | Git | GitHub | chienktv90/chienle-labs |
💡 Tối ưu hóa cho Tầng Miễn phí (Free Tier Optimization)¶
Do sử dụng các gói miễn phí, hệ thống cần được cấu hình tối ưu để đảm bảo độ ổn định cao nhất:
1. Northflank (Backend)¶
- RAM hữu hạn (128MB - 512MB): Dockerfile được cấu hình
-w 2(2 workers) thay vì 4 để tránh lỗi Out of Memory (OOM). - Cold Start: Thêm
--timeout 120trong lệnh chạy Gunicorn để chấp nhận việc khởi động chậm của các instance miễn phí sau một thời gian không hoạt động. - Build Context: Phải đặt đúng
/backendđể giảm dung lượng file gửi lên builder.
2. Supabase (Database)¶
- Connection Pooling (Port 6543): Bắt buộc sử dụng cổng 6543 (Transaction Mode) vì gói Free có giới hạn số lượng kết nối trực tiếp (Direct Connections) rất thấp.
- SSL: Luôn thêm
?sslmode=requiređể đảm bảo an toàn dữ liệu trên tầng miễn phí.
3. Cloudflare R2 (Storage)¶
- Hạn mức: Gói Free cho phép 10GB lưu trữ. Hãy nén ảnh/video trước khi upload để tiết kiệm dung lượng.
- CORS: Hệ thống tự động cấu hình CORS trên startup để tránh lỗi khi upload trực tiếp từ trình duyệt trên tầng miễn phí.
Kiến trúc tổng quan¶
┌─────────────────┐ HTTPS ┌──────────────────┐ SQL ┌─────────────┐
│ Cloudflare │ ──────────────▶│ Northflank │ ───────────▶│ Supabase │
│ Pages │ │ (FastAPI) │ │ (PostgreSQL)│
│ (Next.js) │ │ │ └─────────────┘
│ │ │ │ S3 API ┌─────────────┐
│ chienle.dev │ │ api.chienle.dev │ ───────────▶│ Cloudflare │
└─────────────────┘ └──────────────────┘ │ R2 │
└─────────────┘
Bước 1: Tạo Database trên Supabase¶
- Đăng nhập supabase.com → New Project.
- Đặt tên project, chọn region gần nhất, tạo Database Password (lưu lại cẩn thận).
- Sau khi project sẵn sàng, vào Project Settings → Database.
- Cuộn xuống phần Connection string → URI, copy chuỗi kết nối:
[!IMPORTANT] - Chọn Transaction Mode (Port 6543) cho SQLAlchemy/FastAPI. - Thay thế
[YOUR-PASSWORD]bằng mật khẩu đã lưu ở bước 2. - Thêm?sslmode=requireở cuối chuỗi.
Bước 2: Deploy Backend lên Northflank¶
2.1 Chuẩn bị Dockerfile¶
Đảm bảo file backend/Dockerfile có nội dung:
FROM python:3.11-slim
WORKDIR /app
RUN apt-get update \
&& apt-get install -y --no-install-recommends gcc libpq-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
2.2 Tạo Service trên Northflank¶
- Đăng nhập northflank.com → New Service → Combined Service.
- Repository: Kết nối GitHub, chọn repo
chienle-labs. - Branch: Chọn nhánh deploy (ví dụ:
init-run-full-stack-on-localhoặcmain). - Build Settings:
- Build type: Dockerfile
- Build context:
/backend⚠️ Rất quan trọng — nếu để/sẽ lỗi - Dockerfile location:
Dockerfile - Resources: Chọn gói Free (~512 MB RAM).
2.3 Cấu hình Networking¶
- Vào tab Networking → Ports:
- Port name:
http - Port number:
8000 - Protocol:
HTTP - Bật Public (sẽ tạo link
.code.run).
2.4 Cấu hình Environment Variables¶
Vào tab Environment → Runtime variables, thêm tất cả các biến sau:
| Variable | Value | Ghi chú |
|---|---|---|
DATABASE_URL | postgresql://postgres.[ref]:[pass]@...pooler.supabase.com:6543/postgres?sslmode=require | Chuỗi từ Bước 1 |
CORS_ORIGINS | https://chienle.dev,https://www.chienle.dev | Domain frontend (phẩy ngăn cách) |
R2_ENDPOINT | https://[account-id].r2.cloudflarestorage.com | Từ Cloudflare R2 Dashboard |
R2_KEY | [Access Key ID] | Tạo API token trên R2 |
R2_SECRET | [Secret Access Key] | Tạo API token trên R2 |
R2_BUCKET | [bucket-name] | Tên bucket đã tạo trên R2 |
CDN_BASE | https://[cdn-domain] | Public URL của R2 bucket |
API_KEY | [random-secure-string] | Khóa bí mật API bảo vệ write operations |
[!CAUTION] Thiếu bất kỳ biến nào ở trên, FastAPI sẽ crash ngay khi khởi động (do Pydantic validation) → Container bị lỗi "no healthy upstream".
- Nhấn Create Service và đợi build hoàn tất.
Bước 3: Chạy Database Migration (Alembic)¶
[!IMPORTANT] Phải checkout đúng nhánh đang deploy trên prod để đảm bảo file migration khớp.
Chạy từ máy Local (Đơn giản nhất)¶
# 1. Checkout đúng branch
git checkout init-run-full-stack-on-local
git pull
# 2. Vào thư mục backend
cd backend
# 3. Kích hoạt môi trường
venv\Scripts\activate # Windows
# source venv/bin/activate # macOS/Linux
# 4. Tạm thời đổi DATABASE_URL trong .env sang Supabase
# DATABASE_URL=postgresql://...supabase...?sslmode=require
# 5. Chạy migration
alembic upgrade head
Nếu thành công, bạn sẽ thấy:
[!WARNING] Nhớ đổi lại
DATABASE_URLvề giá trị local sau khi chạy xong.
Chạy trên Northflank (Thay thế)¶
Vào Northflank → Service → tab Shell → gõ alembic upgrade head.
Bước 4: Deploy Frontend lên Cloudflare Pages¶
4.1 Chuẩn bị file cấu hình¶
frontend/package.json — Đảm bảo có đủ scripts:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
}
}
frontend/next.config.js — Bỏ qua lỗi TS/ESLint khi build:
/** @type {import('next').NextConfig} */
const nextConfig = {
typescript: { ignoreBuildErrors: true },
eslint: { ignoreDuringBuilds: true },
};
module.exports = nextConfig;
4.2 Tạo Project trên Cloudflare Pages¶
- Đăng nhập Cloudflare Dashboard → Workers & Pages → Create application → Pages → Connect to Git.
- Chọn repo
chienle-labs, chọn nhánh deploy. - Build settings:
- Framework preset:
Next.js - Root directory (advanced):
frontend⚠️ Bắt buộc vì là monorepo - Build command: Giữ mặc định (
npx @cloudflare/next-on-pages@1) -
Build output directory: Giữ mặc định
-
Environment variables: | Variable | Value | | :--- | :--- | |
NEXT_PUBLIC_API|https://api.chienle.dev| |NEXT_PUBLIC_API_KEY|Mã giống API_KEY ở Backend| -
Nhấn Save and Deploy.
4.3 Cấu hình Compatibility Flag¶
Sau khi build thành công lần đầu:
- Vào project → tab Settings → Functions → Compatibility flags.
- Thêm flag
nodejs_compatcho cả Production và Preview. - Quay lại tab Deployments → nhấn
...→ Retry deployment.
[!IMPORTANT] Nếu không thêm flag
nodejs_compat, trang web sẽ hiện lỗi "Node.JS Compatibility Error".
Bước 5: Cấu hình Custom Domain¶
5A. Backend → api.chienle.dev¶
- Northflank: Service Backend → Networking → Custom domains → Add
api.chienle.dev. - Cloudflare DNS: Thêm bản ghi: | Type | Name | Target | Proxy | | :--- | :--- | :--- | :--- | |
CNAME|api|api.chienle.dev.chie-c9p8.dns.northflank.app| DNS Only (☁️ xám) | - Quay lại Northflank nhấn Verify.
[!WARNING] Bắt buộc để DNS Only (không proxy) cho Northflank backend, nếu bật Proxy (☁️ cam) sẽ gây lỗi SSL.
5B. Frontend → chienle.dev¶
- Cloudflare Pages: Project → tab Custom domains → Set up a custom domain → nhập
chienle.dev. - Cloudflare tự động cập nhật DNS → nhấn Activate domain.
- Đợi trạng thái chuyển sang Active (🟢).
Bước 6: Xác minh hệ thống¶
| Kiểm tra | URL | Kết quả mong đợi |
|---|---|---|
| Backend API | https://api.chienle.dev | {"message": "Welcome to Course API"} |
| Frontend | https://chienle.dev | Trang web hiển thị đúng |
| CORS | F12 → Console trên chienle.dev | Không có lỗi CORS |
| Database | Supabase → Table Editor | Thấy các bảng đã tạo |
Phụ lục: So sánh Môi trường Dev vs Prod¶
Biến môi trường Backend (backend/.env)¶
| Biến | Local Dev | Production (Northflank) |
|---|---|---|
DATABASE_URL | postgresql://postgres:123456@localhost:5432/course_dev | postgresql://...supabase...?sslmode=require |
CORS_ORIGINS | http://localhost:3000 | https://chienle.dev,https://www.chienle.dev |
R2_ENDPOINT | Giá trị R2 thật | Giá trị R2 thật |
R2_KEY | Giá trị R2 thật | Giá trị R2 thật |
R2_SECRET | Giá trị R2 thật | Giá trị R2 thật |
R2_BUCKET | Giá trị R2 thật | Giá trị R2 thật |
CDN_BASE | Giá trị CDN thật | Giá trị CDN thật |
API_KEY | local_dev_key | chuoi_random_bao_mat_tren_northflank |
Biến môi trường Frontend (frontend/.env.local)¶
| Biến | Local Dev | Production (Cloudflare Pages) |
|---|---|---|
NEXT_PUBLIC_API | http://localhost:8000 | https://api.chienle.dev |
NEXT_PUBLIC_API_KEY | local_dev_key | chuoi_random_bao_mat_tren_northflank |
Chạy Local (Dev)¶
# Terminal 1: Backend
cd backend
venv\Scripts\activate
uvicorn main:app --reload
# Terminal 2: Frontend
cd frontend
npm run dev
Frontend → http://localhost:3000 | Backend → http://localhost:8000
Lưu ý quan trọng¶
[!CAUTION] - Không commit file
.envlên GitHub — đã có trong.gitignore. - Không dùngCORS_ORIGINS=*trên Production — chỉ cho phép domain tin cậy. - Luôn test trên branch dev trước khi merge vàomain.[!TIP] - Cloudflare Pages tự động deploy mỗi khi bạn push code lên nhánh đã kết nối. - Northflank cũng tự động rebuild khi có push mới (nếu bật auto-deploy). - Để rollback, vào Northflank/Cloudflare chọn bản deploy cũ và nhấn Redeploy.
Troubleshooting (Các lỗi thường gặp)¶
❌ Backend: "no healthy upstream" / ERR_CONNECTION_RESET¶
Nguyên nhân: Thiếu biến môi trường → Pydantic validation fail → Container crash. Fix: Kiểm tra tab Logs trên Northflank. Nếu thấy ValidationError → Thêm đủ 7 biến vào Environment → Rollout Restart.
❌ Frontend Build: "Failed to type check" — tailwind.config.ts¶
Nguyên nhân: Tailwind v4 không export type Config. Fix: Xóa import type { Config } và type annotation trong tailwind.config.ts.
❌ Frontend Build: Build command fails¶
Nguyên nhân: Thiếu build script trong package.json. Fix: Thêm "build": "next build" vào scripts.
❌ Frontend Runtime: "Node.JS Compatibility Error"¶
Nguyên nhân: Cloudflare Workers chưa bật hỗ trợ Node.js API. Fix: Settings → Functions → Compatibility flags → Thêm nodejs_compat → Retry deployment.
❌ Frontend: "Failed to create course" / CORS Error¶
Nguyên nhân: Thiếu Content-Type: application/json header trong fetch request HOẶC CORS_ORIGINS trên backend chưa bao gồm domain frontend. Fix: Kiểm tra CORS_ORIGINS trên Northflank có chứa https://chienle.dev và kiểm tra tất cả fetch POST có header Content-Type.
❌ Cloudflare DNS: Backend SSL Error¶
Nguyên nhân: Bản ghi CNAME của api đang bật Proxy (☁️ cam). Fix: Chuyển sang DNS Only (☁️ xám) để Northflank tự quản lý SSL.