Bỏ qua

🚀 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

  1. Đăng nhập supabase.comNew Project.
  2. Đặt tên project, chọn region gần nhất, tạo Database Password (lưu lại cẩn thận).
  3. Sau khi project sẵn sàng, vào Project Settings → Database.
  4. Cuộn xuống phần Connection string → URI, copy chuỗi kết nối:
    postgresql://postgres.[project-ref]:[PASSWORD]@aws-0-[region].pooler.supabase.com:6543/postgres
    

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

  1. Đăng nhập northflank.comNew ServiceCombined Service.
  2. Repository: Kết nối GitHub, chọn repo chienle-labs.
  3. Branch: Chọn nhánh deploy (ví dụ: init-run-full-stack-on-local hoặc main).
  4. Build Settings:
  5. Build type: Dockerfile
  6. Build context: /backend ⚠️ Rất quan trọng — nếu để / sẽ lỗi
  7. Dockerfile location: Dockerfile
  8. Resources: Chọn gói Free (~512 MB RAM).

2.3 Cấu hình Networking

  1. Vào tab NetworkingPorts:
  2. Port name: http
  3. Port number: 8000
  4. Protocol: HTTP
  5. Bật Public (sẽ tạo link .code.run).

2.4 Cấu hình Environment Variables

Vào tab EnvironmentRuntime 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".

  1. 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:

INFO  [alembic.runtime.migration] Running upgrade  -> b0859fcb5b70, Initial

[!WARNING] Nhớ đổi lại DATABASE_URL về 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

  1. Đăng nhập Cloudflare DashboardWorkers & PagesCreate applicationPagesConnect to Git.
  2. Chọn repo chienle-labs, chọn nhánh deploy.
  3. Build settings:
  4. Framework preset: Next.js
  5. Root directory (advanced): frontend ⚠️ Bắt buộc vì là monorepo
  6. Build command: Giữ mặc định (npx @cloudflare/next-on-pages@1)
  7. Build output directory: Giữ mặc định

  8. Environment variables: | Variable | Value | | :--- | :--- | | NEXT_PUBLIC_API | https://api.chienle.dev |

  9. Nhấn Save and Deploy.

4.3 Cấu hình Compatibility Flag

Sau khi build thành công lần đầu:

  1. Vào project → tab SettingsFunctionsCompatibility flags.
  2. Thêm flag nodejs_compat cho cả ProductionPreview.
  3. 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

  1. Northflank: Service Backend → NetworkingCustom domains → Add api.chienle.dev.
  2. 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) |
  3. 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

  1. Cloudflare Pages: Project → tab Custom domainsSet up a custom domain → nhập chienle.dev.
  2. Cloudflare tự động cập nhật DNS → nhấn Activate domain.
  3. Đợ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 .env lên GitHub — đã có trong .gitignore. - Không dùng CORS_ORIGINS=* trên Production — chỉ cho phép domain tin cậy. - Luôn test trên branch dev trước khi merge vào main.

[!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 EnvironmentRollout 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_compatRetry 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.


Tài liệu liên quan