- Terraform dùng HCL (DSL riêng), ecosystem cực lớn 3000+ provider, state file local hoặc remote.
- Pulumi dùng TypeScript/Python/Go thật, control flow tự nhiên, test bằng unit test thường.
- Cả 2 đều quản lý được Cloud VPS, DNS, firewall, S3, K8s cluster.
- Terraform thắng khi cần multi-cloud, hire dễ. Pulumi thắng khi team đã code TS/Python, muốn loop, condition.
- Cho startup VN tự host VPS: Terraform an toàn, ổn định. Pulumi nếu muốn IaC + app cùng repo TypeScript.
Bạn đang quản lý 5-10 VPS, vài record DNS Cloudflare, S3 bucket cho backup, tay chỉnh từng cái qua web panel - mệt và dễ sai. Đến lúc cần IaC (Infrastructure as Code). Câu hỏi quen thuộc: chọn Terraform (HCL) hay Pulumi (TypeScript/Python)?
Cả 2 đều giải quyết bài toán: define infrastructure bằng code, version control, apply tự động, drift detection. Khác biệt nằm ở ngôn ngữ và triết lý. Bài này phân tích từng yếu tố cho startup VN tự host trên Cloud VPS - không phải enterprise dùng AWS hàng triệu USD.
Test case: quản lý 3 VPS (Cloud VPS TND), 5 DNS record Cloudflare, 1 R2 bucket, 1 user MySQL. Đo thời gian setup, code length, learning curve.
1. Bảng so sánh nhanh Pulumi vs Terraform
| Tiêu chí | Terraform | Pulumi |
|---|---|---|
| Ngôn ngữ | HCL (DSL) | TS, Python, Go, C#, Java |
| Learning curve | 1 tuần (HCL syntax) | 1 ngày (nếu biết TS/Python) |
| Provider count | 3000+ | 150+ (qua TF bridge) |
| State management | File local hoặc remote | Cloud (free tier) hoặc S3 |
| Test unit code | Khó (terraform test mới có) | Dễ (jest, pytest) |
| Loop, conditional | count, for_each (giới hạn) | JS for, if natural |
| Pricing | Free (open core) | Free cho cá nhân |
| Hire dev biết | Dễ (phổ biến hơn) | Khó hơn |
| VPS provider VN | Custom provider | Custom resource |
2. Terraform hello world: define 3 VPS DigitalOcean
# main.tf
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}
variable "do_token" {}
provider "digitalocean" {
token = var.do_token
}
resource "digitalocean_droplet" "web" {
count = 3
image = "ubuntu-24-04-x64"
name = "web-${count.index + 1}"
region = "sgp1"
size = "s-2vcpu-2gb"
ssh_keys = [data.digitalocean_ssh_key.main.id]
}
data "digitalocean_ssh_key" "main" {
name = "default-key"
}
output "ips" {
value = digitalocean_droplet.web[*].ipv4_address
}terraform init
terraform plan -var="do_token=$DO_TOKEN"
terraform apply -var="do_token=$DO_TOKEN" -auto-approve
# Output:
# ips = ["1.2.3.4", "1.2.3.5", "1.2.3.6"]3. Pulumi tương đương bằng TypeScript
// index.ts
import * as pulumi from '@pulumi/pulumi';
import * as digitalocean from '@pulumi/digitalocean';
const sshKey = digitalocean.getSshKey({ name: 'default-key' });
const droplets = [];
for (let i = 1; i <= 3; i++) {
droplets.push(new digitalocean.Droplet(`web-${i}`, {
image: 'ubuntu-24-04-x64',
name: `web-${i}`,
region: 'sgp1',
size: 's-2vcpu-2gb',
sshKeys: [sshKey.then(k => k.id)],
}));
}
export const ips = droplets.map(d => d.ipv4Address);pulumi stack init dev
pulumi config set digitalocean:token $DO_TOKEN --secret
pulumi upCode Pulumi cảm giác tự nhiên hơn với dev TypeScript. Loop dùng for thật, không cần học count/for_each.
4. State management - vấn đề lớn nhất của IaC
Cả 2 đều lưu state file (JSON) ghi nhớ resource đã tạo. State file cực kỳ quan trọng - mất là không quản lý được nữa.
- Terraform: mặc định state local (terraform.tfstate). Cần config S3/R2 backend remote khi team chia sẻ. Có locking qua DynamoDB hoặc tùy backend.
- Pulumi: mặc định state trên Pulumi Cloud (free cho cá nhân, có UI đẹp). Có thể chuyển sang S3, Azure Blob nếu muốn self-host.
# Terraform S3 backend
terraform {
backend "s3" {
bucket = "my-tf-state"
key = "prod/terraform.tfstate"
region = "ap-southeast-1"
dynamodb_table = "tf-lock"
encrypt = true
}
}
# Pulumi S3 backend
pulumi login s3://my-pulumi-state?region=ap-southeast-15. Quản lý DNS Cloudflare và VPS provider VN
Cả 2 đều có provider Cloudflare chính chủ:
# Terraform Cloudflare
resource "cloudflare_record" "app" {
zone_id = var.zone_id
name = "app"
value = digitalocean_droplet.web[0].ipv4_address
type = "A"
proxied = true
ttl = 1
}
# Pulumi Cloudflare
import * as cloudflare from '@pulumi/cloudflare';
new cloudflare.Record('app', {
zoneId: config.get('zoneId'),
name: 'app',
value: droplets[0].ipv4Address,
type: 'A',
proxied: true,
});Với VPS provider VN (TND, BizflyCloud, Vinahost), không có official Terraform/Pulumi provider. Workaround: dùng http provider gọi API, hoặc local-exec chạy curl. Quản lý semi-manual.
6. Loop và conditional - Pulumi thắng tuyệt đối
# Terraform - phải dùng for_each
resource "digitalocean_droplet" "web" {
for_each = {
web1 = { size = "s-1vcpu-1gb" }
web2 = { size = "s-2vcpu-2gb" }
web3 = { size = "s-4vcpu-4gb" }
}
image = "ubuntu-24-04-x64"
name = each.key
region = "sgp1"
size = each.value.size
}
# Pulumi - loop bình thường
const sizes = [
{ name: 'web1', size: 's-1vcpu-1gb' },
{ name: 'web2', size: 's-2vcpu-2gb' },
{ name: 'web3', size: 's-4vcpu-4gb' },
];
const droplets = sizes.map(s => new digitalocean.Droplet(s.name, {
image: 'ubuntu-24-04-x64',
name: s.name,
region: 'sgp1',
size: s.size,
}));Khi cần logic phức tạp (đọc CSV, gọi API external, transform data), Pulumi viết như app TypeScript thường. Terraform phải dùng locals, dynamic block, vòng vèo.
7. Testing infrastructure code
# Pulumi test với jest
import { describe, expect, test } from '@jest/globals';
import * as pulumi from '@pulumi/pulumi';
pulumi.runtime.setMocks({
newResource: (args) => ({ id: 'mock-id', state: args.inputs }),
call: () => ({}),
});
describe('infrastructure', () => {
test('creates 3 droplets', async () => {
const infra = await import('./index');
expect(infra.ips.length).toBe(3);
});
});
# Terraform test (mới ra v1.6+)
# tests/main.tftest.hcl
run "create_droplets" {
assert {
condition = length(digitalocean_droplet.web) == 3
error_message = "Expected 3 droplets"
}
}Pulumi test framework trưởng thành hơn vì là code thật. Terraform test feature còn mới, ít ví dụ.
8. CI/CD pipeline với GitHub Actions
# .github/workflows/iac.yml (Terraform)
name: Terraform
on: [push]
jobs:
apply:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- run: terraform init
- run: terraform plan -no-color
- run: terraform apply -auto-approve
if: github.ref == 'refs/heads/main'
# .github/workflows/iac.yml (Pulumi)
name: Pulumi
on: [push]
jobs:
up:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm install
- uses: pulumi/actions@v5
with:
command: up
stack-name: dev9. Pricing và self-host
- Terraform: open source free hoàn toàn, có Enterprise tier paid (Terraform Cloud). State backend tự host trên S3.
- Pulumi: CLI free, Pulumi Cloud free cho cá nhân/individual. Team 3+ user trả ~75 USD/user/tháng. Hoặc self-host state trên S3 miễn phí.
Với startup VN tiết kiệm chi phí, cả 2 đều có thể chạy 100% free khi self-host state trên Cloudflare R2 (10GB free, 1 triệu read/tháng).
10. Migration từ Terraform sang Pulumi
Pulumi có tool tf2pulumi convert HCL sang TypeScript:
pulumi convert --from terraform --language typescript
# Đọc main.tf rồi sinh index.ts
# 80% syntax convert auto, phần còn lại fix tayMigration ngược (Pulumi -> Terraform) khó hơn vì Pulumi dùng đầy đủ feature ngôn ngữ. Phải viết lại tay.
11. Quyết định cho startup VN
Chọn Terraform khi:
- Team có DevOps hoặc SRE đã quen Terraform.
- Cần multi-cloud (AWS + GCP + Azure).
- Roadmap cần Terragrunt, Atlantis cho workflow phức tạp.
- Muốn hire dev DevOps dễ (Terraform phổ biến hơn ở VN).
Chọn Pulumi khi:
- Team đã code TS/Python, không có DevOps riêng.
- Muốn IaC + app code cùng monorepo TypeScript.
- Cần logic phức tạp: read CSV, gọi API, transform.
- Test infrastructure quan trọng (audit, compliance).
12. Best practice IaC cho cả 2 tool
- State backend luôn remote (S3/R2) với encryption + locking.
- Secret không bao giờ commit - dùng env var hoặc Vault.
- Plan trước apply, review trong PR.
- Tag mọi resource:
env=prod, owner=team-x, cost-center=eng. - Module hoá: 1 module = 1 component (VPS, DNS, DB).
- CI/CD apply tự động khi merge main, manual approve.
- Drift detection 1 lần/ngày qua cron.
Quản lý VPS bằng IaC - tự động hóa từ ngày đầu
Cloud VPS TND sẵn AlmaLinux 9, Ubuntu 22/24, Debian 12/13. SSD CEPH, snapshot 1-click, backup hằng ngày, network 200Mbps trong nước. Provision VPS qua API, tích hợp Terraform/Pulumi qua custom resource.
Xem 8 cấu hình Cloud VPS →FAQ
Pulumi có thật sự nhanh hơn Terraform khi apply không?
Tốc độ tương đương vì cả 2 đều gọi API provider giống nhau. Pulumi có lợi thế nhẹ ở parallel execution và compile time. Khác biệt < 10%, không đáng kể.
Có nên dùng Ansible thay cho Terraform/Pulumi không?
Ansible khác mục đích: nó là configuration management (cài app, sửa file trên VPS đã tồn tại). Terraform/Pulumi là provisioning (tạo VPS, DNS từ 0). Thường dùng cả 2: TF/Pulumi tạo VPS, Ansible cài app.
State file bị mất thì làm sao?
Khôi phục từ backup (S3 versioning) hoặc import từng resource thủ công: terraform import / pulumi import. Đau khổ nhưng làm được. Vì vậy LUÔN backup state file.
Có provider Terraform cho VPS VN không?
Chưa có official cho TND, BizflyCloud, Vinahost. Có thể viết custom provider hoặc dùng terraform-provider-http gọi REST API. Hỏi support TND để có docs API VPS.
Pulumi YAML có dùng được không nếu không biết code?
Có. Pulumi hỗ trợ YAML markup gần giống Terraform HCL. Phù hợp infra đơn giản. Tuy nhiên mất lợi thế lớn nhất của Pulumi là dùng code thật.
Terraform Cloud có free tier không?
Có. Free tier cho team <5 user, không giới hạn workspace. Hết free khi cần SSO, audit log, agent self-hosted. Lúc đó cân nhắc tự host state trên S3.
- GPU VPS cho AI startup: ROI 2026
- Hermes AI Agent: cài đặt trên VPS + gắn proxy chạy automation ổn định
- Cảnh báo bảo mật khẩn dịp nghỉ lễ 30/4 - 1/5: Lỗ hổng cPanel & WHM CVE-2026-41940 đang bị khai thác
- Tự host n8n automation trên VPS 2GB: workflow cá nhân không tốn Zapier
- OpenClaw skills: viết custom skill bằng JavaScript cho agent của bạn
- Transfer domain từ GoDaddy, Namecheap về TND



