
Xây dựng Blog Platform trên AWS từ A đến Z trong 1 tuần
1. Vì sao mình quyết định "chuyển nhà"?
Blog cũ của Cloud Mentor Pro chạy trên Tailwind Next.js Starter Blog — một template static site rất phổ biến. Đơn giản, đẹp, và hoàn toàn miễn phí. Nhưng sau một thời gian vận hành, mình nhận ra những giới hạn ngày càng rõ ràng:
Không có hệ thống tác giả:
Mọi bài viết đều phải push code lên GitHub. Author muốn đăng bài phải biết Git, phải có quyền trên repo, và cần mình review PR. Điều này tạo ra bottleneck lớn khi team mở rộng.
Không có tính năng tương tác:
Reader không thể like, bookmark hay follow tác giả yêu thích. Không có thông báo khi có bài mới. Không có cách nào đo lường mức độ quan tâm của cộng đồng.
Không có comment:
Blog cũ hoàn toàn không có hệ thống bình luận. Reader đọc xong không có chỗ để phản hồi, đặt câu hỏi hay thảo luận — mất đi một phần quan trọng của cộng đồng.
Không có dashboard quản trị:
Muốn biết bài nào được đọc nhiều? Muốn kiểm duyệt nội dung trước khi publish? Không có cách nào làm điều đó ngoài... đọc tay.
Mình quyết định xây lại từ đầu — một nền tảng blog thực sự, không phải static site.
2. Những tính năng của blog mới
Dành cho Admin
- Dashboard thống kê: tổng bài viết, lượt xem, người dùng
- Duyệt bài viết
- Quản lý người dùng, phân quyền
- Quản lý Events và Ads trên sidebar
- Audit trail: lịch sử mọi thao tác quan trọng
Dành cho Author
- Dashboard cá nhân: thống kê bài viết, lượt xem, số người theo dõi
- Editor WYSIWYG (Rich text) lẫn Source mode trên cùng giao diện
- Upload ảnh trực tiếp lên S3 từ editor
- Nhận email thông báo khi bài được duyệt hoặc từ chối
- Update thông tin hồ sơ
- Xem danh sách bài viết đã lưu

Dành cho Reader
- Like, bookmark bài viết
- Follow tác giả
- Tìm kiếm bài viết (full-text search)
- Bình luận qua Giscus (GitHub Discussions)
- Notification bell cho các hoạt động liên quan
- Trang profile tác giả với danh sách bài viết và thống kê
- Search theo tags

Hệ thống
- Authentication: AWS Cognito (email + Google OAuth)
- Email transactional: SES qua SQS queue (async)
- CDN: CloudFront cho media (ảnh)
- CI/CD tự động từ GitHub push
- SSG/ISR cho trang public, SSR cho dashboard/admin
Điểm nổi bật
Editor và Reader dùng cùng một format: Bài viết được viết bằng MDX — render trên trang đọc và trong editor là giống nhau. Author thấy ngay kết quả thực tế.
Rich text + Markdown song song: Có thể switch qua lại giữa WYSIWYG và source markdown bất cứ lúc nào mà không mất nội dung.
Workflow kiểm duyệt rõ ràng: Mọi bài đều đi qua DRAFT → PENDING → PUBLISHED. Admin duyệt có lý do từ chối. Author edit bài đã publish → tự động cập nhật nội dung mới, không cần duyệt lại
Serverless hoàn toàn: Không có EC2, không có server cần quản lý. Scale tự động, trả tiền theo lượng dùng thực tế.
Single-table DynamoDB: Toàn bộ dữ liệu (users, posts, tags, likes, bookmarks, follows, events, ads) trong một DynamoDB table duy nhất với access pattern tối ưu.
3. Kiến trúc hệ thống

Các AWS Service sử dụng
| Service | Mục đích | Ghi chú |
|---|---|---|
| Amplify Hosting | Deploy Next.js frontend | CI/CD tự động từ GitHub, hỗ trợ SSG + ISR |
| API Gateway HTTP API | REST API cho frontend | Rẻ hơn 70% so với REST API |
| Lambda | Business logic (Node.js 24 + TypeScript) | ESBuild bundle, cold start < 500ms |
| DynamoDB | Database chính | Single-table design, on-demand billing |
| S3 | Lưu trữ media (ảnh) | Presigned URL upload trực tiếp từ browser |
| CloudFront | CDN cho media | Edge Singapore/Tokyo, gần user VN |
| Cognito | Authentication | Email + Google OAuth |
| SES | Gửi email transactional | Notify approve/reject, OTP xác thực email, bài viết mới từ Author đã follow |
| SQS | Queue cho email async | Tránh Lambda timeout khi gửi mail |
| SAM | Infrastructure as Code | Deploy toàn bộ backend 1 lệnh |
4. Quản lý đa môi trường
Toàn bộ hạ tầng chạy trên cùng một AWS account với 3 môi trường tách biệt bằng naming convention:
blog-table-dev ← DynamoDB dev
blog-table-prod ← DynamoDB prod
blog-media-bucket-dev
blog-media-bucket-prod
blog-platform-dev ← SAM stack dev
blog-platform-prod ← SAM stack prod
Local development:
- Frontend:
.env.localtrỏ vào API dev đang chạy trên AWS - Không cần mock, không cần emulator — gọi thẳng DynamoDB dev và Lambda dev thật
Tách biệt env trên Amplify:
- Branch
develop→ Amplify appblog-dev→ env vars trỏ API dev - Branch
production→ Amplify appblog-prod→ env vars trỏ API prod
Cách này đơn giản, không cần multi-account setup phức tạp, phù hợp với team nhỏ và chi phí tối thiểu.
5. CI/CD Pipeline
Backend — GitHub Actions + SAM
# .github/workflows/deploy-backend.yml
push to main →
1. npm install & build (ESBuild)
2. sam build
3. sam deploy --config-env prod
Mỗi Lambda được bundle riêng bằng ESBuild — cold start nhanh, package nhỏ. SAM tự động tạo/update CloudFormation stack, rollback nếu deploy fail.
Frontend — Amplify CI/CD
Amplify tự detect Next.js, tự build và deploy khi có push lên GitHub. Không cần viết workflow — zero config.
ISR (Incremental Static Regeneration) được Amplify hỗ trợ native: trang chủ revalidate 60 giây, trang bài viết 300 giây.
6. Chi phí
Chi phí xây dựng
| Khoản | Chi phí |
|---|---|
| Claude Code Pro (extra) | ~$70 |
| Thời gian phát triển | 1 tuần |
| AWS (dev environment) | Nằm trong Free Tier |
Mình hit giới hạn 150% Weekly limits của Claude Code Pro trong quá trình build — đủ để thấy đây là một dự án không nhỏ. Phần lớn thời gian là thiết kế DynamoDB schema, viết SAM template, và debug các edge case của Next.js App Router.
Chi phí vận hành ước tính (100 users, 200 bài, ~5.000 request/ngày)
| Service | Ước tính/tháng | Ghi chú |
|---|---|---|
| DynamoDB on-demand | ~$0.5 | 5K reads + 1K writes/ngày |
| Lambda | ~$0 | Nằm trong Free Tier (1M requests) |
| API Gateway | ~$0.02 | $1/triệu requests |
| S3 (media) | ~$0.5 | 5GB storage + transfer |
| CloudFront | ~$0.1 | 10GB transfer/tháng |
| SES | ~$0.1 | 1.000 email/tháng |
| Amplify Hosting | ~$2–4 | Build minutes + bandwidth |
| Cognito | ~$0 | Free Tier 50.000 MAU |
| Tổng | ~$3–5/tháng |
Đây là điểm mạnh lớn nhất của kiến trúc serverless: chi phí tỉ lệ thuận với lượng dùng thực tế. Nếu không có traffic, gần như miễn phí.
Bước đầu chưa thể ước lượng chi phí chính xác, mình cần quan sát trong khoảng 2-3 tháng tới.
7. Migration từ blog cũ sang blog mới
Migration ảnh
Blog cũ lưu ảnh trên S3 bucket static.cloudmentor.pro. Thay vì copy thủ công, mình dùng S3 Batch Replication để sync toàn bộ sang bucket mới media.cloudmentor.pro, sau đó tạo CloudFront distribution mới trỏ vào bucket mới.
Domain ảnh trong nội dung bài viết được thay thế tự động trong migration script:
function replaceDomain(text: string): string {
return text.replaceAll('static.cloudmentor.pro', 'media.cloudmentor.pro');
}
Migration bài viết
Blog cũ có ~95 file .mdx . Mình viết TypeScript script để:
- Đọc từng file
.mdx, parse frontmatter (title, date, tags, authors, images) - Map author slug (
phongnx,minhbt...) → thông tin user trong DynamoDB - Chuẩn hóa tags (ví dụ
Disaster Recovery→disaster-recovery) - Ghi vào DynamoDB với đầy đủ fields
Script log chi tiết từng bài, và tóm tắt cuối cùng.
Redirect URL
URL cũ: /blog/{folder}/{slug} → URL mới: /posts/{slug}
Blog cũ: https://old-blog.cloudmentor.pro/
Blog mới:
8. Đánh giá về Performance
Frontend:
- Trang chủ và bài viết dùng ISR → TTFB < 100ms (serve từ Amplify edge cache)
- Trang đọc bài: SSG, load ảnh qua CloudFront CDN Singapore
- Dashboard/Admin: Client-side render, không cache
Backend:
- Lambda cold start: ~300–500ms (ESBuild bundle nhỏ)
- DynamoDB query bằng GSI: < 5ms (single-digit milliseconds)
- Search index: cache trong Lambda memory 5 phút → giảm DynamoDB scan
Điểm có thể cải thiện (Phase 2):
- Elasticsearch/OpenSearch hoặc Third party nếu cần full-text search phức tạp hơn
- Scheduled posts với EventBridge
9. Trải nghiệm và bài học
Dùng Claude Code để build cả frontend lẫn backend
Đây là lần đầu tiên mình dùng AI để build một hệ thống hoàn chỉnh từ đầu. Claude Code không chỉ viết code — nó còn giúp thiết kế DynamoDB schema, review access pattern, phát hiện N+1 query, và đề xuất cải thiện performance.
Workflow thực tế: mình mô tả tính năng, Claude Code implement, mình review và test. Với cách này, 1 tuần hoàn thành được lượng công việc mà trước đây phải mất từ 1 đến vài tháng.
Bài học: AI không thay thế được việc hiểu hệ thống. Mình vẫn cần biết DynamoDB hoạt động như thế nào, Lambda cold start là gì, ISR khác gì SSG — để review và đưa ra quyết định đúng.
Thiết kế DynamoDB Single-Table
Sai lầm phổ biến nhất khi mới dùng DynamoDB là thiết kế như SQL — mỗi entity một table. Single-table design yêu cầu tư duy ngược: xác định access pattern trước, rồi mới thiết kế schema.
Ví dụ: để list tất cả bài theo author, thay vì JOIN, ta dùng GSI với gsi2pk = AUTHOR#<userId>. Để lookup bài theo slug, dùng GSI với pk = slug.
Chọn giải pháp Search phù hợp
Full-text search trên DynamoDB không native support. Các lựa chọn:
- OpenSearch: mạnh nhất, nhưng $50+/tháng cho cluster nhỏ nhất
- DynamoDB Scan + filter: đơn giản, không cần thêm service, nhưng đọc toàn bộ table mỗi lần — chi phí tăng theo số lượng bài
- Search index endpoint: build một endpoint trả về toàn bộ post metadata (title, slug, excerpt, tags), cache trong Lambda memory 5 phút, search phía client với string matching
Với quy mô 200 bài, mình chọn giải pháp thứ 3 — đủ nhanh, chi phí thấp, tôi ưu ops.
Serverless không có nghĩa là không có vấn đề
Lambda cold start vẫn là thực tế. Cognito JWT cache nghĩa là thay đổi profile không reflect ngay. ISR có thể serve stale data trong vài phút. Cần hiểu những trade-off này để không bị bất ngờ khi production.
Infrastructure as Code từ ngày đầu
Dùng SAM để định nghĩa toàn bộ AWS resources trong template.yaml — khi cần tạo môi trường mới (dev/prod) chỉ cần chạy sam deploy. Một số resource vẫn tạo tay trên Console như CloudFront distribution và ACM Certificate vì chỉ cần setup một lần và ít thay đổi.
Kết
Xây một blog platform nghe có vẻ overkill — và đúng là như vậy nếu bạn chỉ cần một chỗ để viết. Nhưng nếu mục tiêu là xây dựng một cộng đồng tech, bạn cần công cụ tốt hơn một static site.
Tổng chi phí xây dựng: 1 tuần + $70 cho AI assistant. Chi phí vận hành: $3–5/tháng. Đây là con số mình nghĩ là rất hợp lý cho một nền tảng có đầy đủ tính năng của một blog platform thực sự.
Nếu bạn muốn tìm hiểu thêm về bất kỳ phần nào — hãy để lại comment bên dưới.