⚙️ Chọn hosting và deploy bản đầu tiên

By Better Wise

Đây là bài mở màn của cả khóa: app của bạn đang chạy ở localhost, và mục tiêu hôm nay là nó chạy được ở một URL công khai. Tôi từng mất một đêm vì deploy gộp quá nhiều thứ vào lần đầu, nên bài này sẽ đi theo kiểu checklist ngắn: làm bước nào, theo thứ tự nào.

Sau 30 phút, bạn sẽ:

  • Phân biệt được ba nhóm hosting chính và biết nhóm nào dành cho mình.
  • Chọn được hosting phù hợp cho app nhỏ trong khoảng 10 phút.
  • Deploy được bản chạy đầu tiên lên URL công khai do hosting cấp.
  • Biết đọc log và kiểm tra đúng ba thứ ngay sau lần deploy đầu.

Trả lời ba câu hỏi trước khi nhìn vào bảng giá

Đừng mở trang pricing trước. Trả lời ba câu này đã:

  1. App của bạn là static (chỉ front-end, build ra HTML/CSS/JS) hay có server (API, SSR, kết nối database)?
  2. App có cần database không, và bạn muốn hosting bán kèm hay dùng dịch vụ ngoài?
  3. Bạn có sẵn sàng tự quản lý máy chủ không? Người mới thì câu trả lời nên là không.

Nguyên tắc duy nhất của bài này: chọn hosting đơn giản nhất mà app chạy được. Mục tiêu là có URL công khai, không phải dựng hạ tầng hoàn hảo.

Ba nhóm hosting và khi nào chọn nhóm nào

Nhóm Ví dụ Chọn khi Tránh khi
Static hosting Vercel, Netlify, Cloudflare Pages, GitHub Pages App chỉ có front-end, không cần server riêng App cần API hoặc cron job chạy nền
PaaS Render, Railway, Fly.io, Koyeb App có server, cần database, bạn không muốn quản lý máy chủ Cần cấu hình hệ điều hành đặc biệt
VPS DigitalOcean, Vultr, Hetzner, Lightsail Bạn cần toàn quyền máy chủ, hoặc chạy dài hạn để tối ưu chi phí Đây là lần deploy đầu tiên của bạn
Shared hosting (cPanel) Các nhà cung cấp hosting PHP truyền thống App là PHP hoặc WordPress App là Node, Python, Go

Với app nhỏ do AI viết, phần lớn trường hợp rơi vào static hosting hoặc PaaS. VPS là nơi app của tôi sập lúc 2 giờ sáng: tôi quên bật lại service sau khi máy chủ reboot, và không có ai báo cho tôi biết suốt 6 tiếng.

Important: Ở lần deploy đầu, hãy chọn nhóm hosting mà bạn không phải SSH vào máy chủ. Mỗi thứ bạn phải tự cài (nginx, SSL, firewall) là một thứ có thể sai lúc nửa đêm.

Bài tập 1. App của bạn là Next.js có API route và dùng Postgres. Chọn nhóm hosting nào và vì sao?

Xem gợi ý

Next.js có server (SSR, API route) và cần Postgres, nên chọn PaaS như Render hoặc Railway (thường có Postgres kèm theo). Một lựa chọn khác là Vercel cho phần Next.js, cộng Postgres từ Neon hoặc Supabase. Cả hai đều đúng; VPS thì không, vì bạn sẽ phải tự lo database, SSL và firewall ngay lần đầu.

Chuẩn bị repo trước khi bấm Deploy

Hosting hiện đại deploy từ Git repository, không phải upload file. Vì vậy sửa repo trước, bấm nút sau:

  • Code đã push lên GitHub (hoặc GitLab).
  • npm run build chạy sạch ở local, không lỗi, không cảnh báo chặn build.
  • Có file .env.example liệt kê tên biến môi trường, không có giá trị thật.
  • .env nằm trong .gitignore và chưa từng bị commit.
  • App đọc cổng từ biến môi trường, không hardcode.
// Đúng: hosting tự inject PORT
const port = process.env.PORT || 3000;
app.listen(port);

Dòng cuối quan trọng hơn vẻ ngoài của nó. Rất nhiều PaaS cấp một cổng ngẫu nhiên qua biến môi trường, nên app hardcode 3000 sẽ start thành công trong log rồi không nhận được request nào. Trước khi deploy, hãy chạy _____ ở local, và nhớ rằng app phải listen trên _____ thay vì một số cố định.

Thêm một chi tiết nhỏ nhưng đáng làm ngay: tạo route kiểm tra sức khỏe, ví dụ /healthz trả về 200. Bài sau này khi nói về uptime monitoring, bạn sẽ dùng lại chính route đó.

Deploy bản đầu tiên, theo đúng thứ tự

  1. Tạo project mới trên hosting và chọn "Deploy from Git repository".
  2. Cấp quyền cho hosting đọc đúng repo đó, không cấp toàn bộ tài khoản.
  3. Điền build command và start command, ví dụ npm run build và npm start.
  4. Thêm biến môi trường cho môi trường production (DATABASE_URL, API key...).
  5. Bấm Deploy, rồi mở tab Logs và đọc từ đầu đến cuối, đừng chỉ nhìn màu xanh.
  6. Mở URL công khai hosting cấp (dạng ten-app.onrender.com) bằng cửa sổ ẩn danh.

Mẹo về thứ tự: nếu app có database, hãy deploy phiên bản không có database trước cho chạy được, rồi thêm database ở lần deploy thứ hai. Gộp cả hai vào lần đầu thì khi lỗi bạn không biết lỗi nằm ở đâu.

URL dạng *.onrender.com hay *.vercel.app là URL tạm do hosting cấp. Nó hoàn toàn đủ để test và đủ để bạn tự hào. Domain riêng là nội dung bài tiếp theo, chưa cần hôm nay.

Đọc log và kiểm tra sau deploy

Ba thứ cần kiểm tra, theo thứ tự:

  1. Build log: lỗi thường là thiếu dependency hoặc thiếu biến môi trường ngay ở bước build.
  2. Runtime log: app build xong nhưng chết khi khởi động, thường do thiếu biến môi trường, sai PORT, hoặc không kết nối được database.
  3. Kiểm tra tay: trang chủ trả 200, một trang con trả 200, và một hành động ghi dữ liệu (đăng ký thử hoặc tạo một record) chạy được.

Luôn mở bằng cửa sổ ẩn danh. Cache và session local đã nhiều lần làm tôi tưởng app trên internet vẫn chạy đúng.

Một điều đừng hoảng: trên free tier, request đầu tiên có thể chậm 30 đến 60 giây vì app bị sleep khi không có traffic. Đó là cold start, không phải app hỏng. Gọi lại lần hai để xác nhận.

Bài tập 2. Deploy xong, trang chủ trả 502 trong khi build log toàn màu xanh. Bạn xem gì tiếp và nghi lỗi gì trước?

Xem gợi ý

Mở runtime log. Build xanh chỉ nghĩa là build thành công, không nói gì về việc app có khởi động được. Ba nguyên nhân hay gặp nhất: thiếu biến môi trường production, app listen sai PORT, hoặc DATABASE_URL sai nên app crash lúc khởi động. Kiểm tra start command trước, rồi tới danh sách biến môi trường.

Vài thói quen cứu bạn lúc 2 giờ sáng

  • Ghi lại ngay, đừng ghi sau. Tạo file DEPLOY.md trong repo trong lúc đang deploy: dùng hosting nào, build command gì, biến môi trường nào. Lần deploy thứ hai bạn sẽ cảm ơn chính mình.
  • Chỉ auto deploy từ branch main ở giai đoạn này. Bật auto deploy mọi branch là cách nhanh nhất để đưa code dở lên production.
  • Đừng dùng chung biến môi trường dev và prod, kể cả khi tiện. Chuyện này sẽ được nói kỹ ở Module 2, nhưng hãy tách ngay từ project đầu tiên trên hosting.
  • Chọn hosting theo loại app, không theo video YouTube mới nhất. Câu hỏi đúng là "app của tôi có server không", không phải "công cụ nào đang hot".
  • Đừng chạm vào domain và HTTPS hôm nay. Có URL tạm chạy được là đủ để kết thúc bài này.

Chốt lại và bước tiếp theo

Ba điều mang theo: chọn hosting theo loại app (static, PaaS, VPS), deploy từ Git với build command và start command rõ ràng, và kiểm tra bằng cửa sổ ẩn danh cùng runtime log thay vì tin vào màu xanh của build.

Bài tiếp theo, Trỏ domain về hosting, sẽ biến URL tạm ten-app.onrender.com thành tên miền riêng của bạn, kèm cách kiểm tra DNS đã lan truyền hay chưa.

Front: Khi nào nên chọn static hosting? Back: Khi app chỉ có front-end, build ra HTML/CSS/JS và không cần server riêng.

Front: PaaS phù hợp với loại app nào? Back: App có server (API, SSR) hoặc cần database, khi bạn không muốn tự quản lý máy chủ.

Front: Vì sao người mới nên tránh VPS ở lần deploy đầu? Back: Vì phải tự cài nginx, SSL, firewall và cập nhật bảo mật; một lỗi nhỏ là app sập mà không ai báo.

Front: Hosting hiện đại lấy code từ đâu để deploy? Back: Từ Git repository (GitHub, GitLab), không phải upload file thủ công.

Front: Lệnh nào cần chạy sạch ở local trước khi deploy? Back: npm run build

Front: Vì sao không được hardcode port 3000? Back: Vì PaaS cấp cổng riêng qua biến môi trường; app phải đọc process.env.PORT để listen đúng.

Front: File nào liệt kê tên biến môi trường mà không chứa giá trị thật? Back: .env.example

Front: File nào tuyệt đối không được commit? Back: .env, vì chứa secret thật; nó phải nằm trong .gitignore.

Front: Build log xanh nhưng trang trả 502, xem gì trước? Back: Runtime log: app crash khi khởi động do thiếu biến môi trường, sai PORT hoặc lỗi kết nối database.

Front: URL dạng ten-app.onrender.com được gọi là gì? Back: URL tạm do hosting cấp, đủ để test; domain riêng là bước của bài sau.

Front: Vì sao mở URL bằng cửa sổ ẩn danh khi kiểm tra? Back: Để loại trừ cache và session local đang che mất lỗi thật trên production.

Front: Request đầu tiên trên free tier chậm 30 đến 60 giây vì sao? Back: App bị sleep khi không có traffic, nên lần gọi đầu là cold start, không phải app hỏng.

Front: Route health check dùng để làm gì? Back: Trả 200 khi app còn sống, dùng để kiểm tra tay và cho uptime monitoring về sau.

Front: Vì sao nên deploy bản không có database trước? Back: Để cô lập lỗi: nếu gộp cả hai ngay lần đầu, bạn không biết lỗi nằm ở app hay ở database.

Ôn nhanh bằng cloze:

  • Trước khi deploy, chạy _____ ở local để chắc bản build không lỗi.
  • App phải listen trên _____ thay vì một cổng cố định.
  • Hosting hiện đại deploy code trực tiếp từ _____ repository.
  • Nhóm hosting cho bạn toàn quyền máy chủ nhưng bắt bạn tự cài nginx và SSL là _____.
  • URL mà hosting cấp sẵn sau lần deploy đầu là _____, đủ để test trước khi trỏ domain.