Networking basics for cloud apps

You don't need to be a network engineer to run a backend in the cloud, but you do need a working mental model of VPCs, subnets, and security groups — because the default answer to 'why can't my app reach the database' is almost always a networking misconfiguration.

Intermediate

4 min read

The VPC: your own private network

A VPC (Virtual Private Cloud) is an isolated, private network inside a cloud provider's infrastructure — your own slice of IP address space that nothing outside it can reach unless you explicitly allow it. Every resource you run (servers, databases, load balancers) lives inside a VPC. Think of it as the cloud equivalent of an office network behind a firewall, except you define the firewall rules in code.

VPC (10.0.0.0/16)
 ├─ public subnet  (10.0.1.0/24) — has a route to the internet
 ├─ private subnet (10.0.2.0/24) — no direct route to the internet
 └─ private subnet (10.0.3.0/24) — database lives here

Subnets: public vs private

A VPC is carved into subnets — smaller address ranges, each pinned to a specific availability zone. The distinction that actually matters day to day is public vs private:

  • Public subnet: has a route to an internet gateway, so resources here can have a public IP and be reached directly from the internet. Load balancers and bastion hosts typically live here.
  • Private subnet: has no route to an internet gateway. Resources here have no public IP and can't be reached from the internet at all. Application servers and databases belong here — there's no reason a database needs to be directly internet-addressable, and putting it in a public subnet is a common, serious misconfiguration.

Private subnets still need outbound internet access sometimes (pulling a package, calling a third-party API) without being inbound-reachable. That's what a NAT gateway does: it sits in a public subnet and lets private-subnet resources initiate outbound connections, while nothing from outside can initiate a connection in.

Security groups: the firewall that actually matters most

A security group is a stateful virtual firewall attached to individual resources (an EC2 instance, an RDS database, a load balancer) that controls what traffic is allowed in and out. "Stateful" means if you allow an inbound connection, the matching outbound response is automatically allowed — you don't have to write a return-traffic rule.

resource "aws_security_group" "app" {
  name   = "app-sg"
  vpc_id = aws_vpc.main.id
 
  ingress {
    from_port       = 3000
    to_port         = 3000
    protocol        = "tcp"
    security_groups = [aws_security_group.alb.id]  # only the load balancer, not the whole internet
  }
 
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
 
resource "aws_security_group" "db" {
  name   = "db-sg"
  vpc_id = aws_vpc.main.id
 
  ingress {
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [aws_security_group.app.id]  # only the app tier, nothing else
  }
}

The pattern above — referencing another security group instead of a CIDR block — is the important idiom: the database only accepts connections from things wearing the "app" security group, regardless of their IP. This survives instances being replaced, autoscaled, or moved to a new subnet.

The classic mistake: 0.0.0.0/0 on a database port

0.0.0.0/0 means "any IPv4 address on the internet." Opening a database's port to 0.0.0.0/0 — often done temporarily to debug a connection issue and then forgotten — means anyone on the internet can attempt to connect. Combined with a weak or default password, this is one of the most common real-world breach vectors for cloud databases. The fix is always the same: the database's security group should only allow the specific security group(s) of things that legitimately need to reach it, never a broad CIDR range, and the database itself should live in a private subnet with no route to the internet regardless.

NACLs vs security groups

Subnets also have network ACLs — a second, coarser firewall layer that's stateless (you must explicitly allow both directions) and evaluated before security groups. In practice, most teams leave NACLs at their permissive defaults and do all their access control at the security-group level, since security groups are attached to the actual resource and are far easier to reason about correctly.

Further reading

Check your understanding

A quick comprehension check — not tracked, not graded, just for you.

1. Why should a production database live in a private subnet rather than a public one?

2. A private-subnet application needs to call a third-party API over the internet, but must not be reachable from the internet inbound. What provides this?

3. Why is referencing another security group in an ingress rule (instead of a CIDR block) considered better practice?

4. What is the risk of opening a database's security group to 0.0.0.0/0 on its port, even temporarily?