🚀 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 |
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 |
[!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| -
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 |
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 |
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.