Bản đồ công trìnhTờ này lắp: Hosting & tên miền, Database
Bài nàyĐã họcChưa tớiĐã thay thế
Bạn sẽ học được gì
Tiêu chí nghiệm thu
Ba môi trường: bếp thử, buổi nếm thử, tiệm đang bán
Khi anh Tuấn nghĩ ra công thức bánh mousse chanh dây mới, anh không mang mẻ đầu tiên ra tủ kính. Quy trình thường là:
- Làm thử trong bếp vài lần, sai thì đổ đi, không ai biết.
- Mời chị Lan và vài nhân viên nếm thử, trong điều kiện giống hệt khi bán: cùng hộp đựng, cùng nhiệt độ tủ, cùng thời gian từ lúc làm tới lúc ăn.
- Đưa vào menu khi mọi người đồng ý.
Phần mềm đi đúng ba bước như vậy, mỗi bước là một môi trường (environment): một bản cài đặt riêng của ứng dụng, với dữ liệu và cấu hình riêng.
| Môi trường | Ẩn dụ | Ai dùng | Dữ liệu | Địa chỉ ví dụ | Hỏng thì sao |
|---|---|---|---|---|---|
| Local (development) | Bếp thử ở nhà | Bạn, agent, lập trình viên | Dữ liệu mẫu, giả | http://localhost:3000 |
Không ai biết; sửa rồi chạy lại |
| Staging | Buổi nếm thử nội bộ | Chị Lan, Hà, anh Tuấn kiểm tra trước khi phát hành | Dữ liệu thử nghiệm, giống thật nhưng không phải thật | https://staging.oms.banhmoc.vn |
Phát hiện lỗi trước khách; chậm phát hành một chút |
| Production | Tiệm đang bán | Nhân viên 3 chi nhánh, khách hàng | Dữ liệu thật: đơn, khách, tiền | https://oms.banhmoc.vn |
Đơn sai, khách giận, mất tiền, mất uy tín |
Ngoài ba môi trường chính, các nền tảng hosting hiện đại còn tạo preview (bản xem trước) tự động cho từng thay đổi: mỗi khi agent đề xuất một thay đổi qua pull request, có một địa chỉ riêng để bạn bấm thử trước khi gộp vào. Preview, staging và cách bật tính năng mới cho một nhóm nhỏ được hướng dẫn kỹ ở bài Môi trường preview, staging và feature flag10-06.
Đường đi của một thay đổi
- LOCAL (máy chị Lan)
- STAGING / PREVIEW
- PRODUCTION
- DB local
- DB staging
- DB production
- đẩy code lên (git push)
- duyệt xong, phát hành
Xem sơ đồ gốc (Hình 1)
Vuốt ngang để xem hết sơ đồ
Vuốt ngang để xem hết sơ đồ
Đọc sơ đồ dạng chữ
- LOCAL (máy chị Lan)
- Agent viết code chạy test → DB local dữ liệu mẫu
- STAGING / PREVIEW
- Bản thử nghiệm Hà và anh Tuấn bấm thử → DB staging dữ liệu thử
- PRODUCTION
- oms.banhmoc.vn 3 chi nhánh dùng thật → DB production dữ liệu thật
- Agent viết code chạy test → Bản thử nghiệm Hà và anh Tuấn bấm thử (đẩy code lên (git push))
- Bản thử nghiệm Hà và anh Tuấn bấm thử → oms.banhmoc.vn 3 chi nhánh dùng thật (duyệt xong, phát hành)
Hãy để ý hai điều trong sơ đồ:
- Code đi một chiều: local → staging → production. Không ai sửa code trực tiếp trên production.
- Mỗi môi trường có database riêng. Không có mũi tên nào nối local tới database production. Đó là điều cố ý, và là điều quan trọng nhất trong bài.
Vì sao không dùng chung một database cho tiện?
Hãy tưởng tượng chị Lan đang nhờ agent viết tính năng “xóa đơn thử nghiệm”, và agent chạy một đoạn script dọn dẹp. Nếu máy local đang trỏ vào database production, đoạn script đó sẽ dọn dẹp đơn thật của khách. Agent không có ác ý; nó chỉ làm đúng điều được viết, trên đúng database được cấu hình.
Tách database theo môi trường là hàng rào vật lý: dù agent hay bạn có nhầm lẫn thế nào ở local, dữ liệu khách hàng vẫn an toàn vì local không hề có chìa khóa vào kho thật.
Biến môi trường: cùng công thức, khác nguyên liệu
Nếu ba môi trường dùng ba database khác nhau, làm sao cùng một bộ code biết phải kết nối tới database nào?
Ẩn dụ: Bánh Mộc có ba chi nhánh dùng cùng một cuốn công thức. Nhưng mật khẩu Wi-Fi, số điện thoại nhà cung cấp sữa, mã kho của mỗi chi nhánh khác nhau. Không ai in những thông tin đó vào cuốn công thức; chúng được ghi trên tấm bảng dán ở cửa từng chi nhánh. Công thức chỉ ghi “gọi nhà cung cấp sữa (xem bảng ở cửa)”.
Biến môi trường (environment variable) chính là tấm bảng ở cửa. Đó là các cặp TÊN=giá trị được đặt bên ngoài code, trong môi trường nơi ứng dụng chạy. Code chỉ đọc theo tên:
// Code chỉ biết TÊN biến, không biết giá trị. Mỗi môi trường cung cấp giá trị riêng.
const databaseUrl = process.env.DATABASE_URL;
- Trên máy chị Lan,
DATABASE_URLtrỏ tới database local. - Trên staging, nó trỏ tới database staging.
- Trên production, nó trỏ tới database thật.
Code giống hệt nhau ở ba nơi. Chỉ có tấm bảng ở cửa là khác.
Những gì nên là biến môi trường
| Loại | Ví dụ trong Mộc OMS | Bí mật? |
|---|---|---|
| Địa chỉ và mật khẩu kết nối database | DATABASE_URL |
✅ Bí mật |
| Khóa API dịch vụ bên ngoài | RESEND_API_KEY (email), khóa mô hình AI |
✅ Bí mật |
| Khóa quản trị của Supabase (bỏ qua RLS) | SUPABASE_SERVICE_ROLE_KEY |
✅ Rất bí mật |
| Bí mật xác minh webhook | PAYMENT_WEBHOOK_SECRET |
✅ Bí mật |
| Địa chỉ và khóa công khai của Supabase | NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY |
❌ Công khai (an toàn nhờ RLS) |
| Tên môi trường | APP_ENV=staging |
❌ |
| Bật/tắt tính năng theo môi trường | ENABLE_ZALO_NOTIFY=false ở local |
❌ |
Những gì không nên là biến môi trường: quy tắc nghiệp vụ (phí ship, ngưỡng miễn phí ship) thay đổi theo quyết định kinh doanh, nên nằm trong database hoặc code có test; danh sách sản phẩm; nội dung giao diện.
Nguyên tắc 12-factor: tách cấu hình khỏi code
The Twelve-Factor App (12factor.net) là bộ 12 nguyên tắc nổi tiếng để xây ứng dụng web dễ triển khai và vận hành. Bạn chưa cần đọc cả 12; ở mức nhập môn, hãy nắm nguyên tắc số III, Config (cấu hình):
- Cấu hình là mọi thứ thay đổi giữa các môi trường triển khai: địa chỉ database, khóa dịch vụ bên ngoài, tên miền.
- Cấu hình phải được tách hẳn khỏi code và lưu trong biến môi trường.
- Phép thử đơn giản mà 12-factor đưa ra: nếu ngay lúc này bạn công khai toàn bộ mã nguồn lên mạng, có lộ khóa bí mật nào không? Nếu có, cấu hình chưa được tách đúng.
Phép thử này rất đáng dùng với chủ doanh nghiệp. Bạn có thể hỏi agent, hỏi freelancer, hỏi agency: “Nếu tôi công khai repo này ngay bây giờ, có lộ gì không?” Câu trả lời ngập ngừng là dấu hiệu cần kiểm tra.
Một nguyên tắc khác của 12-factor cũng liên quan trực tiếp: giữ các môi trường càng giống nhau càng tốt (dev/prod parity). Staging nên dùng cùng loại database, cùng phiên bản, cùng cách triển khai với production. Staging “gần giống” production thì lỗi chỉ xuất hiện trên production vẫn lọt qua.
File .env và .env.example
Gõ biến môi trường bằng tay mỗi lần chạy ứng dụng thì quá phiền. Trên máy local, người ta ghi chúng vào một file tên là .env (hoặc biến thể như .env.local). Ứng dụng tự đọc file này khi khởi động. Next.js hỗ trợ sẵn việc này.
Cú pháp file .env
# Dòng bắt đầu bằng # là chú thích
APP_ENV=local
DATABASE_URL=postgresql://postgres:mat-khau-local@localhost:54322/postgres
NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOi...khoa-cong-khai-local
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOi...khoa-bi-mat-local
ENABLE_ZALO_NOTIFY=false
Quy tắc: mỗi dòng một biến, dạng TÊN=giá_trị, không có dấu cách quanh dấu =, tên viết HOA với dấu gạch dưới. Giá trị có dấu cách thì đặt trong ngoặc kép.
Các file .env trong Next.js
Next.js đọc một số file theo quy ước (tại thời điểm viết, 2026; xem tài liệu chính thức để biết chi tiết thứ tự ưu tiên):
| File | Dùng khi | Đưa vào Git? |
|---|---|---|
.env |
Giá trị mặc định cho mọi môi trường | Chỉ khi không chứa bí mật; nhiều đội chọn không dùng |
.env.local |
Giá trị thật trên máy bạn, ghi đè các file khác | ❌ Không bao giờ |
.env.development, .env.production |
Giá trị mặc định riêng cho chế độ chạy dev / build | Chỉ khi không chứa bí mật |
.env.example |
Mẫu: danh sách tên biến, không có giá trị thật | ✅ Luôn luôn |
Trên staging và production, bạn không dùng file .env. Bạn nhập biến môi trường vào trang quản trị của nền tảng hosting (xem phần sau).
.env.example: danh sách tên trên tấm bảng, để trống giá trị
Nếu .env.local không vào Git, làm sao một người mới (hoặc agent ở một phiên làm việc mới) biết dự án cần những biến nào? Câu trả lời là .env.example: một bản sao chỉ có tên biến và chú thích, giá trị để trống hoặc là giá trị giả rõ ràng.
# .env.example — MẪU biến môi trường cho Mộc OMS. File này ĐƯỢC đưa vào Git.
# Cách dùng: sao chép thành .env.local rồi điền giá trị thật. KHÔNG điền giá trị thật vào file này.
# Môi trường: local | staging | production
APP_ENV=local
# Kết nối database PostgreSQL (Supabase). BÍ MẬT, chỉ dùng ở server.
DATABASE_URL=
# Supabase: địa chỉ và khóa công khai. Được phép xuất hiện trên trình duyệt (an toàn nhờ RLS).
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
# Supabase service role: BÍ MẬT, bỏ qua RLS. Chỉ dùng ở server. Tuyệt đối không thêm tiền tố NEXT_PUBLIC_.
SUPABASE_SERVICE_ROLE_KEY=
# Gửi email giao dịch (giai đoạn 2). BÍ MẬT.
RESEND_API_KEY=
# Bí mật xác minh webhook thanh toán (giai đoạn 2). BÍ MẬT.
PAYMENT_WEBHOOK_SECRET=
# Bật gửi thông báo Zalo. Để false ở local và staging để không nhắn nhầm cho khách thật.
ENABLE_ZALO_NOTIFY=false
Và file .gitignore phải có những dòng chặn file bí mật (nhiều bộ khởi tạo dự án Next.js đã thêm sẵn, nhưng hãy tự kiểm tra):
# .gitignore (trích)
.env*.local
.env
!.env.example
Dòng !.env.example nghĩa là “ngoại trừ file này”, để chắc chắn file mẫu vẫn được đưa vào Git.
Kiểm tra biến môi trường ngay khi khởi động
Một lỗi khó chịu: quên khai báo một biến trên staging, ứng dụng vẫn chạy, chỉ hỏng khi ai đó bấm đúng nút gửi email. Cách phòng: kiểm tra mọi biến cần thiết ngay lúc ứng dụng khởi động, thiếu là dừng và báo rõ. Agent có thể viết đoạn này bằng Zod (thư viện kiểm tra dữ liệu bạn đã gặp):
// src/lib/env.ts — chỉ dùng ở phía server.
// Kiểm tra biến môi trường khi khởi động; thiếu hoặc sai thì dừng ngay với thông báo rõ ràng.
import { z } from "zod";
const EnvSchema = z.object({
APP_ENV: z.enum(["local", "staging", "production"]),
DATABASE_URL: z.string().min(1, "Thiếu DATABASE_URL"),
NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
SUPABASE_SERVICE_ROLE_KEY: z.string().min(1, "Thiếu SUPABASE_SERVICE_ROLE_KEY"),
ENABLE_ZALO_NOTIFY: z.enum(["true", "false"]).default("false"),
});
export const env = EnvSchema.parse(process.env);
Khi thiếu biến, ứng dụng dừng với thông báo chỉ đích danh biến nào. Lỗi lộ ra ngay khi triển khai, không phải giữa giờ cao điểm ngày 20/10.
Dán .env.example (chỉ tên biến, chạy hoàn toàn trên trình duyệt) → cảnh báo biến bí mật mang tiền tố NEXT_PUBLIC_ (KEY, SECRET, TOKEN, SERVICE_ROLE), dòng có giá trị, .gitignore thiếu ‘!.env.example’; kèm bài phân loại 7 mục cấu hình
Bàn thử cần bật JavaScript.
Kết quả chỉ để tham khảo, không thay cho việc bạn tự duyệt.
Biến môi trường trên hosting
Trên staging và production, biến môi trường được nhập vào trang quản trị của nền tảng hosting. Với Vercel (trong stack Bánh Mộc), tại thời điểm viết, bạn vào phần cài đặt dự án, mục biến môi trường, và chọn mỗi biến áp dụng cho môi trường nào: Production, Preview, hay Development. Các nền tảng khác có cơ chế tương tự. Giao diện có thể thay đổi; hãy xem tài liệu chính thức của nền tảng bạn dùng.
Ba điều cần biết:
- Đổi biến môi trường thường cần triển khai lại thì ứng dụng mới nhận giá trị mới. “Đã đổi khóa mà vẫn lỗi” thường là do quên bước này.
- Mỗi môi trường một bộ khóa riêng: staging dùng project Supabase staging với khóa staging; production dùng project và khóa riêng. Lộ khóa staging không ảnh hưởng dữ liệu thật.
- Giới hạn người được xem khóa production: chỉ chị Lan (owner) và người thật sự cần. Bài Quản lý secrets09-03 đi sâu vào phân quyền và xoay vòng khóa.
Câu chuyện chị Lan lộ API key
Đây là câu chuyện chị Lan vẫn kể lại mỗi khi có ai hỏi “sao phải học bài bản thế”.
Hồi còn thử “vibe” app quản lý đơn bằng một công cụ tạo app tự động, chị muốn thêm tính năng “AI tự tóm tắt ghi chú đơn hàng”. Công cụ gợi ý dán khóa API của một dịch vụ mô hình AI vào. Chị tạo khóa, dán vào ô được chỉ. Mọi thứ chạy ngay, chị rất vui.
Chị không biết ba điều:
- Khóa được đặt trong một biến có tiền tố
NEXT_PUBLIC_, nghĩa là nằm trong code gửi xuống trình duyệt của bất kỳ ai mở app. - Chị đã bấm “đẩy code lên GitHub” để “lưu cho an toàn”, với repo để chế độ công khai (mặc định chị không để ý). Trong một file cấu hình, khóa còn được ghi thẳng vào code.
- Trên Internet có những chương trình tự động liên tục quét các repo công khai để tìm khóa bị lộ. Khóa của chị có thể bị phát hiện trong thời gian rất ngắn sau khi đẩy lên.
Khoảng một tuần sau, chị nhận email từ nhà cung cấp dịch vụ AI cảnh báo mức sử dụng bất thường. Hóa đơn tháng đó cao gấp nhiều lần mức chị dự tính cho cả năm. Ai đó đã dùng khóa của chị để chạy việc của họ, và chị trả tiền.
Phản xạ đầu tiên của chị là xóa file chứa khóa và đẩy lên lại. Không đủ. Một người bạn làm kỹ sư giải thích:
- Git lưu toàn bộ lịch sử; khóa vẫn nằm trong các phiên bản cũ của repo. Ai đã tải repo về thì vẫn có.
- Khóa còn nằm trong bản app đang chạy trên mạng, vì nó được nhúng vào code trình duyệt.
- Khóa đã lộ phải được coi là đã bị đánh cắp. Cách duy nhất là vô hiệu hóa nó.
Quy trình đúng mà chị làm theo, và giờ là quy trình chuẩn của Bánh Mộc khi nghi lộ khóa:
Vuốt ngang để xem hết sơ đồ
Đọc sơ đồ dạng chữ
- Phát hiện hoặc nghi ngờ lộ khóa → 1. Thu hồi / vô hiệu hóa khóa NGAY trên trang quản trị nhà cung cấp
- 1. Thu hồi / vô hiệu hóa khóa NGAY trên trang quản trị nhà cung cấp → 2. Tạo khóa mới, đặt vào biến môi trường trên hosting, triển khai lại
- 2. Tạo khóa mới, đặt vào biến môi trường trên hosting, triển khai lại → 3. Kiểm tra thiệt hại: log sử dụng, hóa đơn, dữ liệu bị truy cập
- 3. Kiểm tra thiệt hại: log sử dụng, hóa đơn, dữ liệu bị truy cập → 4. Liên hệ nhà cung cấp nếu có chi phí bất thường
- 4. Liên hệ nhà cung cấp nếu có chi phí bất thường → 5. Sửa tận gốc: chuyển khóa về backend, bỏ khỏi code, kiểm tra .gitignore
- 5. Sửa tận gốc: chuyển khóa về backend, bỏ khỏi code, kiểm tra .gitignore → 6. Đặt giới hạn chi tiêu, cảnh báo sử dụng bất thường nếu dịch vụ hỗ trợ
Ba bài học chị Lan rút ra, và cũng là ba quy tắc trong file AGENTS.md của dự án Mộc OMS:
- Khóa bí mật chỉ sống trong biến môi trường ở backend; không bao giờ trong code, không bao giờ có tiền tố
NEXT_PUBLIC_. - Repo mặc định để riêng tư;
.gitignorechặn mọi file.envtrừ.env.example. - Mọi dịch vụ tính tiền theo lượt dùng đều đặt giới hạn chi tiêu và cảnh báo, nếu nhà cung cấp hỗ trợ.
Biến môi trường và coding agent
Làm việc với agent đặt thêm vài lưu ý riêng:
- Agent có thể đọc file trong dự án, kể cả
.env.local. Nội dung đó đi vào ngữ cảnh của agent. Nhiều công cụ cho phép cấu hình chặn agent đọc file nhất định; hãy dùng tính năng này cho các file.env*(trừ.env.example), và xem hướng dẫn ở bài Bảo mật khi dùng agent09-07. - Agent đôi khi viết thẳng khóa vào code “để chạy thử cho nhanh”, hoặc điền giá trị thật vào
.env.example. Khi review thay đổi, hãy tìm các chuỗi dài ngẫu nhiên hoặc các chữ nhưkey,secret,token,password. - Agent có thể in biến môi trường ra log khi gỡ lỗi (ví dụ in toàn bộ
process.env). Log có thể bị lưu lại ở nơi nhiều người xem được. - Khi agent cần một biến mới, yêu cầu nó: thêm tên vào
.env.examplekèm chú thích, thêm vào đoạn kiểm tra khởi động, và báo bạn để bạn tự điền giá trị thật.
Áp dụng vào dự án Bánh Mộc
Nhật ký công trình · Bánh Mộc
BM-261012-014
12.10.2026
- Chị LanChủ chuỗi

Trước khi viết tính năng đầu tiên cho Mộc OMS, chị Lan dành một buổi để “dựng hàng rào” cho ba môi trường:
- Hai project Supabase riêng:
banhmoc-stagingvàbanhmoc-production. Trên máy, chị dùng Supabase chạy local với dữ liệu mẫu. - File
.env.examplenhư mẫu ở trên, đưa vào Git..env.localtrên máy chị chỉ chứa khóa local. - Biến môi trường trên Vercel: bộ khóa staging cho Preview, bộ khóa production cho Production. Chỉ chị Lan có quyền xem khóa production.
- Kiểm tra biến môi trường khi khởi động bằng
src/lib/env.ts. - Dải băng cảnh báo trên staging: chị yêu cầu agent hiển thị một dải màu cam “MÔI TRƯỜNG THỬ NGHIỆM, KHÔNG NHẬP ĐƠN THẬT” khi
APP_ENVkhácproduction. Lý do rất thực tế: Hà từng suýt nhập đơn thật của khách vào bản thử vì hai giao diện giống hệt nhau. - Tắt thông báo Zalo ở local và staging (
ENABLE_ZALO_NOTIFY=false), để khi thử nghiệm không có khách hàng thật nào nhận tin “Đơn của bạn đã bị hủy”.
Cuối buổi, chị làm phép thử 12-factor: “Nếu công khai repo này ngay bây giờ, có lộ gì không?” Chị nhờ agent quét toàn bộ repo và lịch sử commit tìm chuỗi giống khóa bí mật. Kết quả sạch. Lần này chị biết vì sao nó sạch.
Prompt mẫu
Điền ô trống rồi sao chép
Thiết lập quản lý biến môi trường cho dự án Next.js này theo nguyên tắc 12-factor:
1. Liệt kê mọi giá trị cấu hình hiện đang viết thẳng trong code (URL, khóa, mật khẩu, tên miền).
2. Chuyển chúng thành biến môi trường; tạo .env.example chỉ có tên biến và chú thích tiếng Việt,
ghi rõ biến nào bí mật, biến nào công khai.
3. Tạo src/lib/env.ts kiểm tra biến bằng Zod khi khởi động, thông báo lỗi chỉ rõ biến thiếu.
4. Kiểm tra .gitignore chặn .env và .env*.local nhưng giữ .env.example.
5. Kiểm tra không có biến bí mật nào mang tiền tố NEXT_PUBLIC_.
KHÔNG đọc hoặc in nội dung các file .env thật. Khi cần giá trị thật, hỏi tôi tên biến, tôi sẽ tự điền.
Điền ô trống rồi sao chép
Giả sử tôi công khai repo này ngay bây giờ. Hãy kiểm tra xem có lộ bí mật nào không:
quét toàn bộ file đang theo dõi bởi Git và lịch sử commit, tìm chuỗi giống API key, token,
mật khẩu, chuỗi kết nối database, hoặc file .env bị commit. Báo cáo vị trí (file, commit),
KHÔNG in ra giá trị đầy đủ của bất kỳ bí mật nào (chỉ hiện vài ký tự đầu).
Nếu tìm thấy, đề xuất các bước xử lý theo thứ tự ưu tiên.
Điền ô trống rồi sao chép
Tính năng gửi email xác nhận đơn cần khóa API của dịch vụ email. Hãy:
- Đặt tên biến RESEND_API_KEY, chỉ dùng ở server.
- Thêm vào .env.example kèm chú thích và vào schema trong src/lib/env.ts.
- Không viết giá trị thật ở bất kỳ đâu trong code hay test; test dùng giá trị giả.
- Ở môi trường khác production, chỉ ghi log "đã giả lập gửi email" thay vì gửi thật.
Sau khi xong, nhắc tôi những biến cần thêm trên hosting cho từng môi trường.
Sai lầm thường gặp
-
Sự cố 01
Dùng chung database giữa local và production.
Triệu chứng“Cho tiện, có dữ liệu thật để thử.” Một lệnh sai là mất đơn thật.
Cách tránhMỗi môi trường một database và một bộ khóa; máy local không bao giờ có khóa production.
-
Sự cố 02
Viết khóa thẳng vào code hoặc đặt tiền tố NEXT_PUBLIC_ cho khóa bí mật.
Triệu chứngKhóa lộ qua Git hoặc qua code trình duyệt.
Cách tránhKhóa chỉ nằm trong biến môi trường phía server; review mọi thay đổi tìm chữ
key,secret,token. -
Sự cố 03
Commit file .env.
Triệu chứngChỉ cần một lần là khóa nằm vĩnh viễn trong lịch sử Git.
Cách tránhKiểm tra
.gitignorengay khi tạo dự án; dùng.env.exampleđể chia sẻ danh sách biến. -
Sự cố 04
Xóa file chứa khóa và nghĩ là xong.
Triệu chứngKhóa vẫn nằm trong lịch sử Git và trong các bản sao.
Cách tránhThu hồi khóa trước, tạo khóa mới, rồi mới dọn dẹp.
-
Sự cố 05
Staging khác xa production.
Triệu chứngStaging dùng cấu hình khác, phiên bản khác, nên lỗi chỉ lộ trên production.
Cách tránhGiữ staging giống production nhất có thể; chỉ khác ở dữ liệu và khóa.
-
Sự cố 06
Thử nghiệm gửi thông báo thật cho khách.
Triệu chứngStaging nhắn Zalo cho số điện thoại khách thật.
Cách tránhBiến bật/tắt tích hợp gửi ra ngoài, mặc định tắt ở local và staging; dữ liệu thử dùng số điện thoại giả.
Checklist
Bài tập thực hành
-
Bài 1
Cơ bản
Phân loại cấu hình
Với mỗi mục sau, quyết định: biến môi trường bí mật, biến môi trường công khai, hay không nên là biến môi trường: (a) mật khẩu database; (b) ngưỡng miễn phí ship 500.000đ; (c) địa chỉ Supabase; (d) khóa gửi tin Zalo ZNS; (e) tên môi trường “staging”; (f) danh sách vị bánh kem; (g) bí mật xác minh webhook ngân hàng.
Kết quả mong đợi
Bí mật: (a), (d), (g). Công khai: (c), (e). Không nên là biến môi trường: (b) và (f), vì đó là dữ liệu hoặc quy tắc kinh doanh, nên nằm trong database hoặc code có test.
-
Bài 2
Cơ bản
Viết .env.example cho dự án của bạn
Liệt kê mọi dịch vụ bên ngoài dự án của bạn sẽ dùng (database, email, thanh toán, AI…). Viết file
.env.examplevới tên biến và chú thích tiếng Việt cho từng cái, ghi rõ bí mật hay công khai.Kết quả mong đợi
Một file mẫu mà người mới (hoặc agent ở phiên mới) đọc là biết cần chuẩn bị những khóa nào.
-
Bài 3
Nâng cao
Diễn tập sự cố lộ khóa
Tạo một khóa API thử nghiệm ở một dịch vụ bạn dùng (loại không tốn tiền hoặc có giới hạn chi tiêu thấp). Giả định nó bị lộ. Thực hiện đầy đủ quy trình sáu bước trong bài: thu hồi, tạo mới, cập nhật biến môi trường trên hosting, triển khai lại, kiểm tra log. Ghi lại thời gian từng bước.
Kết quả mong đợi
Bạn biết chính xác trang quản trị nào để thu hồi khóa, mất bao lâu để ứng dụng chạy lại với khóa mới. Khi sự cố thật xảy ra, bạn không phải tìm tòi trong lúc hoảng.
Thuật ngữ trong bài
- Môi trường (environment)
- Một bản cài đặt riêng của ứng dụng với dữ liệu và cấu hình riêng
- Local / development
- Môi trường trên máy người làm phần mềm
- Staging
- Môi trường thử nghiệm giống production, dùng để kiểm tra trước khi phát hành
- Production
- Môi trường thật, khách và nhân viên đang dùng
- Preview
- Bản xem trước tự động cho từng thay đổi
- Cấu hình (config)
- Những gì thay đổi giữa các môi trường: địa chỉ, khóa
- Biến môi trường
- Cặp TÊN=giá trị đặt ngoài code, code đọc theo tên
.env/.env.local- File chứa biến môi trường trên máy local; không vào Git
.env.example- File mẫu chỉ có tên biến; luôn vào Git
.gitignore- Danh sách file Git bỏ qua
NEXT_PUBLIC_- Tiền tố khiến biến được nhúng vào code trình duyệt (công khai)
- 12-factor
- Bộ 12 nguyên tắc xây ứng dụng dễ triển khai, vận hành
- Dev/prod parity
- Giữ các môi trường giống nhau nhất có thể
- Thu hồi / xoay vòng khóa
- Vô hiệu hóa khóa cũ, thay bằng khóa mới
Đọc thêm
- The Twelve-Factor App: đọc phần “III. Config” và “X. Dev/prod parity”.
- Next.js Documentation: tìm “Environment Variables”.
- Supabase Docs: tìm “API keys” và “Local development”.
- Quản lý secrets: API key, mật khẩu và file .env09-03
- Môi trường preview, staging và feature flag10-06
Nghiệm thu tờ 01-08
Đọc xong và tự kiểm theo phiếu nghiệm thu, rồi đánh dấu tờ này.

