Networking · Feb 2026 · 14 min read

Building a Secure Alibaba Cloud VPC: From Public Internet to Private Architecture

A realistic multi-tier network build — public frontend, private backend, database-tier isolation, bastion access, and the troubleshooting session that happens when a route table is wrong.

Part 5 of the Alibaba Cloud Engineering Lab Series.

Architecture

Most VPC tutorials explain terminology. This one builds a real three-tier network and deliberately breaks a route so the troubleshooting is genuine, not scripted.

Internet
   │
   ▼
 SLB (public VSwitch, 10.0.1.0/24)
   │
   ▼
 App Tier (private VSwitch, 10.0.2.0/24) ── NAT Gateway ── Internet (outbound only)
   │
   ▼
 Data Tier (private VSwitch, 10.0.3.0/24, no internet route at all)

 Bastion Host (public VSwitch, restricted security group) ──▶ App/Data tiers via SSH

Before the how, the what — three terms this build leans on:


Problem

The naive version of this architecture puts everything in one VSwitch with one permissive security group — a frontend, backend, and database all mutually reachable, and the database directly internet-facing "temporarily" during setup, which in practice means indefinitely.


Implementation

VPC and tiered VSwitches:

resource "alicloud_vpc" "main" {
  vpc_name   = "vpc-secure-lab"
  cidr_block = "10.0.0.0/16"
}

resource "alicloud_vswitch" "public" {
  cidr_block = "10.0.1.0/24"
  vpc_id     = alicloud_vpc.main.id
  zone_id    = "ap-southeast-1a"
}

resource "alicloud_vswitch" "app_private" {
  cidr_block = "10.0.2.0/24"
  vpc_id     = alicloud_vpc.main.id
  zone_id    = "ap-southeast-1a"
}

resource "alicloud_vswitch" "data_private" {
  cidr_block = "10.0.3.0/24"
  vpc_id     = alicloud_vpc.main.id
  zone_id    = "ap-southeast-1b"
}

NAT Gateway for the app tier's outbound-only path (package updates, external API calls) without any inbound route from the internet:

resource "alicloud_nat_gateway" "app_nat" {
  vpc_id      = alicloud_vpc.main.id
  vswitch_id  = alicloud_vswitch.public.id
  nat_type    = "Enhanced"
}

Security groups, least privilege. Four groups — one per tier — declared first, then the rules between them:

resource "alicloud_security_group" "slb_sg" {
  security_group_name = "sg-slb-tier"
  vpc_id = alicloud_vpc.main.id
}

resource "alicloud_security_group" "app_sg" {
  security_group_name = "sg-app-tier"
  vpc_id = alicloud_vpc.main.id
}

resource "alicloud_security_group" "data_sg" {
  security_group_name = "sg-data-tier"
  vpc_id = alicloud_vpc.main.id
}

resource "alicloud_security_group" "bastion_sg" {
  security_group_name = "sg-bastion"
  vpc_id = alicloud_vpc.main.id
}
resource "alicloud_security_group_rule" "app_from_slb_only" {
  type              = "ingress"
  security_group_id = alicloud_security_group.app_sg.id
  ip_protocol       = "tcp"
  port_range        = "8080/8080"
  source_security_group_id = alicloud_security_group.slb_sg.id
}

resource "alicloud_security_group_rule" "data_from_app_only" {
  type              = "ingress"
  security_group_id = alicloud_security_group.data_sg.id
  ip_protocol       = "tcp"
  port_range        = "3306/3306"
  source_security_group_id = alicloud_security_group.app_sg.id
}

The data tier's security group accepts traffic only from the app tier's security group — not a CIDR range, a security-group reference. That distinction matters: CIDR-based rules break the moment IPs shift; security-group references stay correct as instances scale.

Bastion access:

resource "alicloud_security_group_rule" "bastion_ssh" {
  type              = "ingress"
  security_group_id = alicloud_security_group.bastion_sg.id
  ip_protocol       = "tcp"
  port_range        = "22/22"
  cidr_ip           = var.admin_office_cidr # not 0.0.0.0/0
}

Failure / Challenge

After deploying, the app-tier instances couldn't reach the internet for package installation — apt-get update hung and timed out. The NAT Gateway was provisioned, but the app-tier VSwitch's route table still had its default route pointing nowhere (the implicit local-only route), because Terraform's alicloud_route_entry resource for the NAT default route hadn't been declared — the NAT Gateway existing isn't enough; the route table has to actually point traffic at it.


Solution

resource "alicloud_route_entry" "app_default_route" {
  route_table_id        = alicloud_vpc.main.route_table_id
  destination_cidrblock = "0.0.0.0/0"
  nexthop_type           = "NatGateway"
  nexthop_id              = alicloud_nat_gateway.app_nat.id
}

This is the Alibaba Cloud equivalent of forgetting a Route Table association on an Azure UDR, or an AWS route table missing its NAT Gateway target — the same class of "the component exists, but nothing points traffic at it" mistake shows up on every cloud's networking stack.


Cost / Performance

ComponentMonthly Cost (approx.)
NAT Gateway (Enhanced)~$45
SLB (s2.small)~$18
Bastion ECS (t6.small)~$8
Total network overhead~$71/mo

That's the fixed cost of a properly segmented network, independent of the compute it protects — worth budgeting explicitly rather than discovering it as an unexplained line item later.


Lessons Learned

GitHub Repository: secure-alibaba-cloud-vpc-lab — the three-tier VPC, NAT gateway, and security-group tier isolation, ready to run.

VPC · Alibaba Cloud · Networking · Security Groups · NAT Gateway · Route Tables