Databases hold highly sensitive data. An RDS instance in a subnet with an internet gateway route is more exposed than it needs to be, and that exposure compounds if publicly_accessible is enabled later or security group rules drift.
Use private subnets in DB subnet groups to enforce stronger network boundaries. This is a foundational architecture call: moving an instance to a different subnet group later requires a reboot or instance recreation, so getting it right upfront matters.
Retrofit consideration
Moving an existing RDS instance to a different subnet group requires a reboot or, in many cases, recreation of the instance. Plan for downtime, or use a blue-green deployment with read replicas to migrate traffic before cutting over.
Implementation
Choose the approach that matches how you manage Terraform.
This control is enforced automatically with Compliance.tf modules. Start free trial
If you use terraform-aws-modules/rds/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.
This control evaluates whether the subnets in an RDS instance's DB subnet group are public. In Terraform, an aws_db_instance references a db_subnet_group_name, which points to an aws_db_subnet_group listing subnet_ids. Each subnet has an associated route table, either attached explicitly via aws_route_table_association or inherited from the VPC's main route table. It fails if any route table entry for those subnets has a gateway_id pointing to an internet gateway (igw-*). To pass, every subnet in the DB subnet group must use route tables with only routes to NAT gateways, VPC endpoints, transit gateways, or local VPC CIDR. Setting publicly_accessible = false on aws_db_instance matters for full protection, but the subnet route table check is the primary evaluation target.
Common pitfalls
Default VPC subnets are public
If you don't specify db_subnet_group_name, RDS falls back to the default DB subnet group, which usually includes public-routed subnets. Always create a dedicated aws_db_subnet_group pointing to private subnets rather than relying on the default.
Subnet group covers multiple AZs with mixed visibility
Get this wrong and RDS may place your primary instance in a public subnet without any error. An aws_db_subnet_group spanning multiple AZs might include both public and private subnets, and RDS chooses which subnet to use. You can't control that choice. Verify every subnet_ids entry in the group resolves to a private subnet.
Publicly accessible false does not make the subnet private
publicly_accessible = false prevents RDS from assigning a public endpoint, but the subnet's route table is unaffected. This control checks the route table, not the flag. Both need to be correct for meaningful network segmentation.
Route table changes after initial deployment
A subnet that passes today can fail tomorrow if someone adds an aws_route with gateway_id pointing to an aws_internet_gateway. The Terraform plan won't catch that after the fact. Route table drift won't trigger any alarm unless Config or equivalent continuous monitoring is in place.
Audit evidence
Auditors expect evidence that RDS instances are not reachable from the internet at the network layer. Config rule results for each RDS instance are the most direct artifact. VPC Flow Logs showing no inbound traffic from public IP ranges to the RDS ENIs work as supporting evidence. Screenshots of the "Connectivity & security" tab in the RDS console, combined with the route tables for those subnets confirming no internet gateway routes, form a complete point-in-time package.
For ongoing assurance, Config conformance pack evaluation history or a CSPM tool's continuous scan results across the audit period carry more weight than screenshots alone.
Framework-specific interpretation
SOC 2: CC6.1 and CC6.6 together require restricting system access to authorized users and protecting against external threats. Public subnet placement puts RDS within reach of external networks, which directly undermines both criteria. Examiners will look for subnet-level evidence, not just the publicly_accessible flag.
PCI DSS v4.0: Requirements 1.2.1, 1.2.7, and 1.3.1 call for restrictive network controls and separation between trusted and untrusted zones. For CDE-adjacent database systems, that means no direct paths from external networks, which private subnet placement enforces at the routing layer.
HIPAA Omnibus Rule 2013: 164.312(a)(1) calls for technical safeguards against unauthorized access to ePHI. For RDS instances holding patient data, that starts at the network layer: keeping them out of public-routed subnets limits the paths an attacker can use to reach the database.
ISO/IEC 27001:2022: A.8.22 (Segregation in networks) requires separating information processing facilities based on security classification. Database tiers belong behind network boundaries, not in subnets with public routes. A.8.20 (Network security) adds the expectation of limiting attack surface through architecture, which this control directly addresses.
NIST SP 800-53 Rev 5: SC-7 (Boundary Protection) and AC-4 (Information Flow Enforcement) both apply here. SC-7 requires managed interfaces at system boundaries; AC-4 restricts information flows to approved paths. Placing RDS in a private subnet addresses the network-layer expectations of both controls.
NIST Cybersecurity Framework v2.0: PR.IR-01 says networks and environments should be protected from unauthorized logical access. Private subnets with no internet gateway route are the baseline mechanism for that protection when it comes to database tiers.
FedRAMP Moderate Baseline Rev 4: At the Moderate baseline, SC-7 expects network boundaries to actively limit external access to sensitive tiers. Placing database instances in private subnets is the network-layer implementation of that requirement.
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.