DigitalOcean VPC Guide 2026 — Free Private Networking
คู่มือทำจริง ตั้งค่า VPC ส่วนตัว แยก environment เซฟฐานข้อมูล และเชื่อม Droplet ข้าม VPC ด้วย Peering
VPC (Virtual Private Cloud) in DigitalOcean is a private network completely isolated from the public internet. All supported resources—Droplets, Managed Databases, Load Balancers, Kubernetes nodes—can communicate with each other through private IPs within the same VPC without ever reaching the public internet, not even once. This differs from opening ports via public IPs and then relying on Cloud Firewall to filter, because VPC cuts off external access at the network layer (network layer) rather than just at the rule layer—in short, if you're not in the same VPC or haven't peered them together, you can't see each other's private IPs at all. For development teams, the main benefits of VPC are threefold. First, security: databases, caches, and internal APIs can be bound to private IPs only, with no need to expose public IPs risking scanning or brute-force attacks. Second, speed and cost: traffic flowing through a private network within the same region has lower latency than running through the public internet, and isn't counted toward a Droplet's outbound bandwidth quota. Third, environment organization: for example, separating VPCs for production versus staging prevents accidentally connecting to the wrong database across environments. One important clarification: VPC doesn't replace Cloud Firewall—both work together on different layers. VPC controls "who can see whose private IP," while Firewall controls "which ports are open or closed." Teams wanting maximum security should use both together: isolate sensitive resources in a VPC with no public IP at all, then expose only web-facing Droplets (edge) with public IPs and strict Firewall rules. This guide will walk you through real VPC setup from the start, custom VPC creation for dev/prod separation, hiding databases from the internet, and connecting two VPCs with Peering.
Contents
- What is VPC and Why It Matters for Security
- Default VPC per Region (Free and Unlimited)
- Creating Custom VPC for Dev/Prod Environment Separation
- Example: Hiding Database from the Internet
- Connecting Droplets Across VPCs with VPC Peering
- Troubleshooting and Best Practices for Real VPC Usage
- Integrating VPC with Load Balancer and Managed Database
- Limitations of VPC to Keep in Mind
- FAQ
What is VPC and Why It Matters for Security
DigitalOcean's VPC (Virtual Private Cloud) is a private network completely separated from the public internet. All resource types that support it—Droplets, Managed Database, Load Balancer, Kubernetes nodes—can communicate through private IPs within the same VPC without ever going out to the internet, not even once. This is different from opening ports via public IP and then relying on Cloud Firewall to filter, because VPC cuts off access from outside at the network layer (network layer), not just at the rule layer—in simple terms, if you're not in the same VPC, or haven't peered together, you can't see each other's private IPs at all. For development teams, VPC has three main benefits. First is security: databases, caches, internal APIs can be bound to private IPs alone, with no need to expose public IPs risking scans or brute-force attacks. Second is speed and cost: traffic running through private network within the same region has lower latency than running through public internet, and doesn't count toward a Droplet's outbound bandwidth quota. Third is environment organization (environment isolation), such as separating VPC for production from staging to prevent accidentally connecting to the wrong database across environments. One thing to understand is that VPC doesn't replace Cloud Firewall—the two work together on different layers. VPC controls "who can see whose private IP," while Firewall controls "which ports are open or closed." Teams seeking maximum security should use both together: keep sensitive resources in a VPC with no public IP at all, then expose only the front-end Droplet (edge) with public IP and strict Firewall rules. This article will guide you through real setup starting from VPC basics, creating Custom VPC for dev/prod separation, hiding databases from the internet, and connecting two VPCs together with Peering.
- VPC cuts off access at the network layer, unlike Firewall which filters at the port layer
- Traffic within VPC is not counted toward a Droplet's outbound bandwidth quota
- Recommended to separate VPCs by environment, such as dev/staging/production
- Always use VPC together with Cloud Firewall for two layers of security
Default VPC per Region (Free and Unlimited)
Every DigitalOcean account comes with a Default VPC automatically created in each region where resources already exist. Covering all 15 datacenters worldwide: nyc1/nyc2/nyc3 (New York), sfo2/sfo3 (San Francisco), ams3 (Amsterdam), lon1 (London), fra1 (Frankfurt), tor1 (Toronto), syd1 (Sydney), atl1 (Atlanta), ric1 (Richmond), mkc1 (Kansas City), all the way to sgp1 (Singapore) and blr1 (Bangalore)—the closest to Thai users. Every region supports VPC just like Droplets, Kubernetes, and Load Balancers. When you create the first Droplet in any region without specifying a VPC, the system automatically binds it to that region's Default VPC instantly.
A critical point developers often miss is that VPC is tied to a single region only, not across regions. A Droplet in nyc1 and a Droplet in sgp1 can never be in the same VPC from the start (you must use VPC Peering to connect them later, as explained in the next section). So before creating your first set of resources, plan whether you'll use a single region or multiple regions. If your team is small and most users are in Asia, consolidating everything in sgp1 region alone means all Droplets, Managed Databases, Load Balancers automatically land in the same VPC without needing Peering.
Using VPC costs nothing extra, whether Default VPC or Custom VPC created separately, and there's no limit to how many VPCs per account—unlike some major cloud providers that charge for data transfer across subnets or charge separately for NAT Gateways. You can check your existing Default VPCs easily with the command doctl vpcs list, which shows the name, region, and UUID of every VPC, plus indicates which is the default for each region. Knowing this UUID is essential when you want to create a Droplet or Database specifically into your target VPC.
- Default VPC is automatically created per region, covering all 15 datacenters
- VPC is tied to a single region only; cross-region requires Peering
- Thai users recommended sgp1 (Singapore), next best blr1 (Bangalore)
Creating Custom VPC for Dev/Prod Environment Separation
Once a team has multiple environments (dev, staging, production), using a single Default VPC for everything risks accidentally connecting a test Droplet to a production database. The solution is to create separate Custom VPCs for each environment, doable either through the Control Panel under Networking > VPC Network or via doctl/API for automation-focused teams.
Example of creating a production VPC with doctl: doctl vpcs create --name prod-vpc --region sgp1 --ip-range 10.10.0.0/24 and for staging in the same region but different IP range to avoid collisions: doctl vpcs create --name staging-vpc --region sgp1 --ip-range 10.20.0.0/24 After creation you get a UUID back; save it for later when creating Droplets by specifying the --vpc-uuid flag instead of letting the system default to Default VPC: doctl compute droplet create web-prod-01 --region sgp1 --size s-2vcpu-4gb --image ubuntu-24-04-x64 --vpc-uuid <prod-vpc-uuid>
The advantage of separating VPCs by environment is that Droplets in staging-vpc cannot see private IPs of Droplets or Databases in prod-vpc at all, even when they're in the same region and account—they must be explicitly peered to communicate, which is proper least-privilege behavior and guards against human error better than relying on memory of Droplet names or tags.
Teams using Terraform to manage infrastructure can declare VPCs as code too, with the digitalocean_vpc resource from the digitalocean/digitalocean provider on Terraform Registry. This makes it reproducible to create and destroy an entire environment set (VPC + Droplets + Databases together) and check history via state file, ideal for teams with many environments needing consistency.
One constraint worth knowing: IP ranges (CIDR) of different VPCs in the same account must not overlap, especially if you anticipate Peering them in the future, because overlapping IP ranges prevent Peering. So plan your company's IP scheme upfront, for example assigning production to 10.10.x.x, staging to 10.20.x.x, dev to 10.30.x.x, to avoid needing to redo it later.
doctl vpcs create- Create via Control Panel (Networking > VPC Network) or
doctl vpcs create - Specify
--vpc-uuidwhen creating a Droplet to avoid accidentally using Default VPC - Plan IP ranges to not overlap in advance in case you need Peering later
Example: Hiding Database from the Internet
The most common VPC use case is hiding a Managed Database so it has no public IP open to the internet at all. DigitalOcean Managed Databases (PostgreSQL, MySQL, Valkey, MongoDB) are automatically created into their region's VPC and come with a separate private hostname different from the public hostname—use that instead of the public endpoint when your Droplet is in the same VPC.
The secure setup has three main steps. First, create the Database in the same VPC as your application Droplet. Using doctl: doctl databases create app-db --engine pg --region sgp1 --size db-s-1vcpu-1gb --vpc-uuid <prod-vpc-uuid>
Second, disable access from public network by going to the Database cluster's Settings > Trusted Sources tab and removing "Allow public access," or restrict Trusted Sources to only Droplets/Tags in the same VPC. Once done, even if someone obtains the database credentials they cannot connect from outside the VPC—network-level connection is blocked at the source, password or not.
Third, update your app's connection string to point to the private hostname instead of the public one. Check the Database's Connection Details page and select "Private network" before copying the connection string, as DigitalOcean shows both options. Connecting via private hostname also has bonus benefits: lower latency and no consumption of your Droplet's outbound bandwidth quota since traffic never leaves DigitalOcean's internal network.
For teams self-hosting the database on a Droplet instead of using Managed Database, the same principle applies: give the database Droplet no public IPv4 from creation (uncheck public IPv4 during Droplet creation), and have the app connect via private IP within the VPC only. Combine with Cloud Firewall rules allowing only the database port (e.g., 5432 for PostgreSQL) from the app Droplet's private IP—never 0.0.0.0/0. This reduces attack surface to the maximum possible without spending an extra dollar.
- Managed Database has a separate private hostname from public hostname
- Disable public access from Database cluster's Trusted Sources tab
- Copy connection string as "Private network" type from Connection Details
Connecting Droplets Across VPCs with VPC Peering
One thing that surprised us: vPC Peering connects two VPCs so resources inside can talk to each other through private IPs without going through the public internet. It works both ways: peering two VPCs in different regions in the same account (e.g., connecting sgp1 to blr1) and peering two VPCs in the same account, same region, but separated for environment isolation as described earlier.
Create Peering via doctl with: doctl vpcs create-peering --name prod-to-staging --vpc-ids <prod-vpc-uuid>,<staging-vpc-uuid> or through the Control Panel in the VPC page, Peerings tab, then click Create Peering and select the two VPCs you want to connect. The system takes a moment to establish the connection. Once the status becomes Active, Droplets on one side can connect to private IPs on the other side instantly without needing to configure routing tables yourself—DigitalOcean handles routes automatically.
The most important caveat is that Peering is non-transitive—if VPC A peers with B and B peers with C, A still cannot reach C directly. You must create separate Peering between A and C as well. Second, the IP ranges (CIDR) of two VPCs being peered cannot overlap at all. If you didn't plan CIDR upfront as recommended earlier, you may need to create a new VPC and migrate all resources—messier than planning from the start.
Real-world use cases include teams with a Kubernetes cluster (DOKS) in one VPC and Managed Database in another (e.g., data team manages database VPC separately). Peering lets pods connect to the database via private hostname with no public access needed. Another case is expanding from one region to many for global users (e.g., starting at sgp1 then expanding to fra1 for European users) and wanting internal services to communicate across regions via private network rather than exposing public IPs on both sides.
Check all Peerings with doctl vpcs list-peerings and delete unused ones with doctl vpcs delete-peering <peering-id> to reduce unnecessary access surfaces in your system.
- Peering is non-transitive—create separate connection lines for every VPC pair that needs to talk
- CIDR of two VPCs must not overlap before Peering is possible
- DigitalOcean manages routing tables automatically after Peering becomes Active
- Works both cross-region and same-region in the same account
Troubleshooting and Best Practices for Real VPC Usage
The most common problem during VPC setup is two Droplets created at different times trying to connect via private IP but can't, even though they're supposedly in the same region. The top reason is Droplets were accidentally placed in different VPCs without noticing (e.g., old Droplet in Default VPC but new Droplet specified the wrong --vpc-uuid). Quick check: view each Droplet's private IP and VPC UUID via doctl compute droplet get <id> --format Name,PrivateIPv4,VPCUUID and compare both sides to ensure VPCUUID matches.
Second problem is connecting to database via private hostname fails even though public access is off. Usually because the old public connection string is still sitting in a .env file or secret manager. Audit every place storing credentials and switch to private hostname completely. Also verify that the app Droplet is in the same VPC as the Database—if they're in different VPCs, changing hostname alone won't help; you need Peering first as described earlier.
Third problem is Peering stuck in pending status longer than normal. Normally takes a few minutes; if it hangs, first check whether CIDR ranges of the two VPCs overlap, which is the #1 cause of Peering creation failure.
Best practice recommendations for teams using VPC seriously in the long run: plan and document your entire company's VPC names and CIDR scheme upfront before creating anything real. Name VPCs meaningfully, like prod-sgp1-vpc instead of default system-assigned names. Disable public IPv4 on Droplets that don't need to receive internet traffic directly from day one, rather than disabling it later. Always use Cloud Firewall alongside VPC following a deny-by-default principle, opening only the ports/traffic actually needed. Periodically review all existing Peering connections and delete those no longer in use, because unused Peering connections are hidden security risk in your system over time.
- Check VPCUUID of both Droplets first before suspecting other issues
- Database connection problems mostly come from stale public hostname in connection string
- Overlapping CIDR is the #1 reason Peering creation fails
- Name VPCs meaningfully and document CIDR plan as a shared reference before starting
Integrating VPC with Load Balancer and Managed Database
Based on real-world use, digitalOcean's Load Balancer acts as the edge front-end receiving traffic from the internet with a public IP, then distributes it to backend Droplets through private network within the same VPC always. When creating a Load Balancer you can specify the target VPC directly with the flag --vpc-uuid, for example: doctl compute load-balancer create --name web-lb --region sgp1 --vpc-uuid <prod-vpc-uuid> --forwarding-rules entry_protocol:https,entry_port:443,target_protocol:http,target_port:80 --droplet-ids <id1>,<id2> The critical point is every backend Droplet attached to a Load Balancer must be in the same VPC as the Load Balancer. If a Droplet is in a different VPC (e.g., forgot to specify --vpc-uuid during Droplet creation and it fell into Default VPC), Load Balancer cannot forward traffic to that backend—health checks fail immediately. This is a common reason for backend status showing "unhealthy" even though the Droplet runs fine.
On the Managed Database side, integration with a Load Balancer architecture typically follows a three-tier pattern: tier one is the Load Balancer receiving internet traffic, tier two is a group of App Droplets behind the Load Balancer in the same VPC, tier three is Managed Database in the same VPC but with public access completely off. Tier two talks to tier three via the Database's private hostname as explained earlier. But the added step with a Load Balancer is to configure Database Trusted Sources to allow only the Tag of App Droplets, not the entire VPC broadly, preventing other Droplets accidentally in the same VPC (like monitoring or bastion host) from accessing the database unintentionally. Example command to add a specific Tag as Trusted Source: doctl databases firewalls append <db-id> --rule tag:app-backend
The benefit of joining Load Balancer, VPC, and Managed Database this way is all traffic from App Droplet all the way to Database never leaves the internet, not even once. Only one point is exposed to the public: the Load Balancer front-end. This minimizes attack surface to the smallest possible while still serving public-facing traffic normally. Teams scaling horizontally by adding more App Droplets behind the Load Balancer follow the same principle: just verify each new Droplet is in the Load Balancer's VPC and has the Tag already allowed by Database Trusted Sources.
- Load Balancer forwards traffic to backend Droplets via private network always
- Backend Droplets must be in the same VPC as Load Balancer or health check turns "unhealthy"
- Specify VPC at Load Balancer creation time with the flag
--vpc-uuid - Restrict Database Trusted Sources by Tag of App Droplets, not the entire VPC
Limitations of VPC to Keep in Mind
Although VPC offers many benefits, there are limitations to understand before designing real architecture, so you don't need to reshape infrastructure later, which is often messier than planning upfront.
The first and most important limitation is VPC is bound to a single region only; there's no global VPC spanning multiple regions in one entity. If you want resources in different regions to communicate via private network, you must create VPC Peering connecting region pairs (and remember Peering is non-transitive). Alternatively, traffic must go through public network, losing both security and latency benefits. Teams planning to expand to multiple regions should design their entire VPC and CIDR scheme upfront, not piecemeal as they expand.
The second limitation is each region can have only one Default VPC, not multiple. Default VPC is used automatically whenever creating resources without specifying a VPC explicitly. If you forget to specify --vpc-uuid even once when creating a Droplet or Database, that resource instantly goes into the Default VPC without any warning—this is a top reason for "can't see each other" problems mentioned in the Troubleshooting section.
The third limitation is CIDR (IP range) of a VPC is fixed at creation time and cannot be changed or resized afterward; you can only change name and description. If you miscalculate CIDR upfront, like choosing too small a range and running out of IPs, or picking a range that overlaps with a VPC you'll need to peer with later, the only fix is creating a new VPC and migrating all resources—significant downtime and risk of private IP changes. So proper CIDR planning with room for growth upfront is critical.
The final limitation is not all DigitalOcean services support VPC. Spaces (Object Storage) is a clear example—it's accessed only via public endpoint (even encrypted with HTTPS)—there's no option to bind it to VPC as a private-only service like Database. So if your app stores sensitive files in Object Storage, you must rely on file-level encryption and strict access key permissions instead of VPC as a protective layer like you can for Droplets or Managed Databases.
- VPC is bound to a single region; no global VPC spanning multiple regions in one entity
- Each region has only one Default VPC; forgetting --vpc-uuid means resources land in Default instantly
- CIDR is fixed at creation; cannot be resized or changed afterward, only name and description