Sponsored Content

DEV Community

Cover image for How Can an App Reach a Database That the Internet Cannot?
Anik Sikder
Anik Sikder

Posted on Originally published at aniksikder.me

How Can an App Reach a Database That the Internet Cannot?

In the previous article, we answered a fundamental question:

How does one server find another server?

We explored DNS, Private DNS, and Service Discovery the mechanisms that turn something like:

postgres.internal.company
Enter fullscreen mode Exit fullscreen mode

into a reachable destination inside a network.

But finding a server is only half of the story.

Knowing where a server is doesn't automatically mean you're allowed to connect to it.

That leads us to a more interesting question.

Most developers eventually learn this architecture:

User
  ↓
Application
  ↓
Database
Enter fullscreen mode Exit fullscreen mode

But how does this actually work at the network level?

If the application can connect to PostgreSQL, why can't someone on the Internet connect to the same database?

This sounds like a simple networking question.

It isn't.

The answer involves:

  • Network routing
  • Public and private subnets
  • VPCs
  • Security groups
  • Firewall rules
  • Network isolation
  • Access boundaries
  • Defense in depth

And these concepts ultimately determine something much bigger:

Which systems are allowed to talk to which other systems?

That's one of the most important questions in production infrastructure.

Because there is a critical difference between:

Can I find the server?
Enter fullscreen mode Exit fullscreen mode

and:

Can I reach the server?
Enter fullscreen mode Exit fullscreen mode

And an even more important difference between:

Can I reach the server?
Enter fullscreen mode Exit fullscreen mode

and:

Am I allowed to connect?
Enter fullscreen mode Exit fullscreen mode

In the previous article, we focused on finding the destination.

In this article, we'll focus on controlling access to that destination.

Let's break it down.


The Architecture We Usually See

Imagine a typical SaaS application:

                    Internet
                        β”‚
                        β–Ό
                 Load Balancer
                        β”‚
                        β–Ό
                  Application
                        β”‚
                        β–Ό
                   PostgreSQL
Enter fullscreen mode Exit fullscreen mode

The application needs the database.

Customers don't.

That distinction is extremely important.

The customer needs:

Internet
   ↓
Application
Enter fullscreen mode Exit fullscreen mode

The application needs:

Application
   ↓
Database
Enter fullscreen mode Exit fullscreen mode

But there should usually be no direct path like this:

Internet
   ↓
Database
Enter fullscreen mode Exit fullscreen mode

This is where cloud networking starts becoming interesting.


The First Principle: Not Everything Needs to Be Reachable

A common mistake in infrastructure design is thinking:

If a service needs to work,
it should be reachable.
Enter fullscreen mode Exit fullscreen mode

Modern infrastructure follows almost the opposite principle:

Make only what needs to be reachable,
reachable.
Enter fullscreen mode Exit fullscreen mode

For example:

Public
  β”‚
  β”œβ”€β”€ Load Balancer
  β”‚
  └── API Gateway

Private
  β”‚
  β”œβ”€β”€ Application Servers
  β”œβ”€β”€ Background Workers
  β”œβ”€β”€ Redis
  └── PostgreSQL
Enter fullscreen mode Exit fullscreen mode

The database doesn't need to accept connections from the public Internet.

So why expose it?

You don't.

This simple decision dramatically reduces the attack surface.


Public Subnet vs Private Subnet

Inside a cloud network such as a VPC, resources are commonly organized into different network boundaries.

Two of the most important concepts are:

Public Subnet
Private Subnet
Enter fullscreen mode Exit fullscreen mode

Think about a building.

There might be:

Public Area
     ↓
Reception
Meeting Room
Waiting Area
Enter fullscreen mode Exit fullscreen mode

And deeper inside:

Restricted Area
     ↓
Operations
Finance
Infrastructure
Records
Enter fullscreen mode Exit fullscreen mode

Not everyone who can enter the building should be able to enter every room.

Cloud networks work with a similar principle.


What Is a Public Subnet?

A public subnet is a subnet whose routing configuration provides a path toward an Internet Gateway.

Resources placed there can be Internet-facing when their own network configuration also permits it for example, when they have a public IP or are exposed through a public load balancer.

Typical examples include:

Load Balancers
Reverse Proxies
Internet-facing Web Servers
Bastion Hosts
Enter fullscreen mode Exit fullscreen mode

A simplified architecture might look like:

                 Internet
                     β”‚
                     β–Ό
              Internet Gateway
                     β”‚
                     β–Ό
               Public Subnet
                     β”‚
                     β–Ό
              Load Balancer
Enter fullscreen mode Exit fullscreen mode

This is where Internet-facing traffic enters the system.


What Is a Private Subnet?

A private subnet is designed for resources that should not be directly reachable from the public Internet.

Typical resources include:

Application Servers
PostgreSQL
MySQL
Redis
RabbitMQ
Kafka
Background Workers
Internal Services
Enter fullscreen mode Exit fullscreen mode

A simplified architecture:

Internet
   βœ–
   β”‚
   β–Ό
Private Subnet
   β”‚
   β”œβ”€β”€ Application
   β”œβ”€β”€ Redis
   └── PostgreSQL
Enter fullscreen mode Exit fullscreen mode

The important idea is not:

"Nobody can access these machines."

It is:

"They are not directly reachable from the public Internet."

That distinction matters.


Private Does Not Mean Isolated

This is one of the most common misunderstandings.

Many developers hear:

Private Subnet
Enter fullscreen mode Exit fullscreen mode

and think:

Nothing can connect to it.
Enter fullscreen mode Exit fullscreen mode

Not true.

A private subnet can communicate with other resources inside the same VPC.

For example:

Application Server
10.0.1.10
       β”‚
       β–Ό
PostgreSQL
10.0.2.20
Enter fullscreen mode Exit fullscreen mode

Both resources may exist inside:

VPC
10.0.0.0/16
Enter fullscreen mode Exit fullscreen mode

The traffic can remain inside the cloud network.

It doesn't need to travel across the public Internet.


So How Does the App Reach the Database?

Suppose:

Application Server
10.0.1.10
Enter fullscreen mode Exit fullscreen mode

needs to connect to:

PostgreSQL
10.0.2.20:5432
Enter fullscreen mode Exit fullscreen mode

The application creates a TCP connection:

10.0.1.10
      β”‚
      β”‚ TCP :5432
      β–Ό
10.0.2.20
Enter fullscreen mode Exit fullscreen mode

The VPC routing system determines how packets should move between those network destinations.

If the route exists and the relevant security controls permit the traffic, the connection succeeds.

There is no requirement for the database to have a public IP.

In fact, keeping the database private is usually the safer design.


Then What Stops Everyone Else?

This is where Security Groups become important.

Routing answers:

Where should this packet go?

Security controls answer:

Is this traffic allowed?

These are different questions.


Security Groups: The Access Control Layer

In AWS, a Security Group acts as a stateful virtual firewall associated with resources such as EC2 instances and certain managed services.

Imagine a building.

The subnet is the area of the building.

The security group is the access control at the door.

The database exists.

The network path may exist.

But access can still be restricted.

For PostgreSQL, the standard port is:

5432
Enter fullscreen mode Exit fullscreen mode

A dangerous rule might look like:

Inbound

Port: 5432
Source: 0.0.0.0/0
Enter fullscreen mode Exit fullscreen mode

That effectively allows connection attempts from anywhere that can reach the database.

That is rarely what you want for a production database.


The Better Rule

Instead of:

Allow everyone
Enter fullscreen mode Exit fullscreen mode

define:

Allow application servers
Enter fullscreen mode Exit fullscreen mode

For example:

Database Security Group
        β”‚
        └── Inbound
              β”‚
              β”œβ”€β”€ Protocol: TCP
              β”œβ”€β”€ Port: 5432
              └── Source: App Security Group
Enter fullscreen mode Exit fullscreen mode

Now the access relationship becomes:

Internet User
      βœ–

Random Server
      βœ–

Unrelated Service
      βœ–

Application Server
      βœ”
Enter fullscreen mode Exit fullscreen mode

This is much more meaningful than simply allowing a large IP range.


Why Security Group References Matter

Consider an application running on multiple servers.

Initially:

10.0.1.10
Enter fullscreen mode Exit fullscreen mode

So someone might configure:

Allow 10.0.1.10 β†’ PostgreSQL:5432
Enter fullscreen mode Exit fullscreen mode

Then autoscaling happens.

Now there are:

10.0.1.10
10.0.1.11
10.0.1.12
10.0.1.13
Enter fullscreen mode Exit fullscreen mode

Tomorrow there may be twenty.

The infrastructure is dynamic.

Hardcoding individual IP addresses creates unnecessary operational complexity.

Instead, define an application security group:

app-sg
Enter fullscreen mode Exit fullscreen mode

Attach it to application servers.

Then configure the database:

db-sg

Inbound:
5432
Source:
app-sg
Enter fullscreen mode Exit fullscreen mode

Now the rule expresses the actual architectural relationship:

Application Layer
       β”‚
       β”‚ allowed
       β–Ό
Database Layer
Enter fullscreen mode Exit fullscreen mode

The exact server IP becomes less important.


Network Access Is a Chain of Decisions

A database connection isn't simply:

Can I ping the database?
Enter fullscreen mode Exit fullscreen mode

Multiple layers can influence whether traffic succeeds.

Conceptually:

Application
    β”‚
    β–Ό
DNS Resolution
    β”‚
    β–Ό
Route
    β”‚
    β–Ό
Network ACL
    β”‚
    β–Ό
Security Group
    β”‚
    β–Ό
Database Listener
    β”‚
    β–Ό
PostgreSQL
Enter fullscreen mode Exit fullscreen mode

If any required layer is incorrectly configured, the connection can fail.

This is why production networking can feel confusing.

There isn't always one single "firewall."

There are multiple boundaries.


A Production-Style Architecture

A more realistic architecture might look like this:

                         Internet
                             β”‚
                             β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚  Load Balancer  β”‚
                    β”‚     Public      β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
                             β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚ Application     β”‚
                    β”‚    Servers      β”‚
                    β”‚    Private      β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                  β”‚                     β”‚
                  β–Ό                     β–Ό
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
          β”‚    Redis     β”‚      β”‚  PostgreSQL  β”‚
          β”‚   Private    β”‚      β”‚   Private    β”‚
          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

Notice the boundaries.

The Internet can reach the load balancer.

The load balancer can reach the application.

The application can reach PostgreSQL.

But the Internet does not get a direct path to PostgreSQL.


This Is Defense in Depth

Now imagine the application itself contains a vulnerability.

An attacker manages to compromise a public-facing component.

A poorly designed network might look like:

Compromised Server
       β”‚
       β”œβ”€β”€ Database βœ”
       β”œβ”€β”€ Redis βœ”
       β”œβ”€β”€ Internal APIs βœ”
       └── Everything Else βœ”
Enter fullscreen mode Exit fullscreen mode

The blast radius becomes enormous.

A better architecture might look like:

Compromised Server
       β”‚
       β”œβ”€β”€ Database βœ”
       β”œβ”€β”€ Redis βœ–
       β”œβ”€β”€ Internal Service βœ–
       └── Other Systems βœ–
Enter fullscreen mode Exit fullscreen mode

The attacker may still have compromised one component.

But the compromise doesn't automatically become a compromise of the entire environment.

That is the value of segmentation.


Security Is About Blast Radius

One of the most useful ways to think about infrastructure security is:

What happens if one component is compromised?

Not:

Can anything ever be hacked?

That second question is unrealistic.

Production systems should be designed with the assumption that individual components can fail or become compromised.

The goal is to limit the damage.

For example:

Internet
   β”‚
   β–Ό
Public Service
   β”‚
   β–Ό
Private Application
   β”‚
   β–Ό
Private Database
Enter fullscreen mode Exit fullscreen mode

Each layer has a limited set of allowed relationships.

This creates smaller blast radiuses.


Why This Matters Beyond Security

Network isolation isn't only about preventing attackers.

It also improves operational control.

Suppose a database is publicly accessible.

Then many different systems can potentially attempt to connect:

Internet
   β”‚
   β”œβ”€β”€ Bots
   β”œβ”€β”€ Scanners
   β”œβ”€β”€ Attackers
   β”œβ”€β”€ Unknown Services
   └── Legitimate Clients
Enter fullscreen mode Exit fullscreen mode

Now the database must deal with traffic it never needed in the first place.

A private database changes the model:

Application Layer
       β”‚
       β–Ό
Database
Enter fullscreen mode Exit fullscreen mode

The allowed communication becomes explicit.

That improves:

  • Security
  • Reliability
  • Troubleshooting
  • Operational visibility
  • Compliance
  • Change management

The Business Impact Is Hidden in the Architecture

A network diagram may look purely technical.

But every boundary represents a business decision.

Consider a database containing:

Customer Accounts
Payment Records
Orders
Personal Information
Internal Business Data
Enter fullscreen mode Exit fullscreen mode

If that database is accidentally exposed, the consequences can extend far beyond engineering:

Misconfiguration
      ↓
Unauthorized Access
      ↓
Data Exposure
      ↓
Incident Response
      ↓
Downtime / Investigation
      ↓
Customer Impact
      ↓
Financial & Reputation Cost
Enter fullscreen mode Exit fullscreen mode

The infrastructure decision happened at the technical layer.

The consequences don't stay there.

This is why architecture is ultimately connected to business continuity.


"Nobody Knows the Database IP" Is Not Security

Another common assumption is:

Nobody knows my database IP.

Therefore:
I'm safe.
Enter fullscreen mode Exit fullscreen mode

That's security through obscurity.

A better assumption is:

Assume the attacker knows the address.
Enter fullscreen mode Exit fullscreen mode

Then ask:

Can they establish a connection?
Enter fullscreen mode Exit fullscreen mode

If the answer is:

No route
+
No public exposure
+
Security group denies access
Enter fullscreen mode Exit fullscreen mode

then knowing the address doesn't help much.

The important boundary is not secrecy.

It is reachability and authorization.


Public Exposure vs Private Access

Let's compare the two models.

Public Database

Internet
    β”‚
    β–Ό
Public IP
    β”‚
    β–Ό
PostgreSQL:5432
Enter fullscreen mode Exit fullscreen mode

The database is directly exposed to Internet traffic.

Even if authentication is strong, the attack surface is unnecessarily large.


Private Database

Internet
    β”‚
    βœ–
    β”‚
    β–Ό
Private Network
    β”‚
    β–Ό
PostgreSQL:5432
Enter fullscreen mode Exit fullscreen mode

And:

Application
    β”‚
    βœ”
    β–Ό
PostgreSQL:5432
Enter fullscreen mode Exit fullscreen mode

The database only needs to serve the application.

So the network should reflect that requirement.


A Simple Mental Model

Think of your infrastructure as a building.

                 STREET
                   β”‚
                   β–Ό
             Public Lobby
                   β”‚
                   β–Ό
             Employee Area
                   β”‚
                   β–Ό
                Vault
Enter fullscreen mode Exit fullscreen mode

The public can enter the lobby.

Employees can enter restricted areas.

Only authorized systems can reach the vault.

Now translate that into cloud architecture:

                 Internet
                    β”‚
                    β–Ό
              Public Subnet
                    β”‚
                    β–Ό
          Private Application Layer
                    β”‚
                    β–Ό
            Private Database Layer
Enter fullscreen mode Exit fullscreen mode

And Security Groups define:

Who can move between the layers.
Enter fullscreen mode Exit fullscreen mode

That's the mental model worth remembering.


The Bigger Engineering Principle

A strong production architecture doesn't ask:

How do we make everything communicate?

It asks:

What actually needs to communicate?

Then it creates the smallest set of allowed paths.

For example:

Load Balancer
      β”‚
      β–Ό
Application
      β”‚
      β”œβ”€β”€β”€β”€β”€β”€β–Ί PostgreSQL
      β”‚
      └──────► Redis
Enter fullscreen mode Exit fullscreen mode

But:

Internet ─────X────► PostgreSQL

Internet ─────X────► Redis

Redis ─────────X────► PostgreSQL
Enter fullscreen mode Exit fullscreen mode

unless those connections are explicitly required.

This is the principle of least privilege applied to networking.


Key Takeaways

  • Public subnets are designed for Internet-facing resources.
  • Private subnets are used for resources that should not be directly reachable from the public Internet.
  • A private subnet does not mean the resources inside it cannot communicate.
  • VPC routing enables communication between private resources when routes exist.
  • Security Groups control which traffic is allowed.
  • Databases generally should not be directly exposed to the public Internet.
  • Security Group references can express application-to-database relationships more effectively than hardcoded IP addresses.
  • Network segmentation limits blast radius when a component is compromised.
  • Security is not about making systems unreachable.
  • Security is about making only the necessary systems reachable by the necessary systems.
  • Infrastructure decisions can directly affect security, reliability, compliance, operational cost, and customer trust.

The most important idea is simple:

Great infrastructure is not about
making everything reachable.

Great infrastructure is about
making only the right things reachable.
Enter fullscreen mode Exit fullscreen mode

Top comments (0)