<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Phu Hoang</title>
    <description>The latest articles on DEV Community by Phu Hoang (@posibble).</description>
    <link>https://dev.to/posibble</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2579967%2Fa483120c-9617-420e-970f-73195a7368e8.png</url>
      <title>DEV Community: Phu Hoang</title>
      <link>https://dev.to/posibble</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/posibble"/>
    <language>en</language>
    <item>
      <title>OpenVPN on AWS: Secure Access to Private Infrastructure</title>
      <dc:creator>Phu Hoang</dc:creator>
      <pubDate>Fri, 12 Jun 2026 01:11:08 +0000</pubDate>
      <link>https://dev.to/posibble/openvpn-on-aws-secure-access-to-private-infrastructure-534n</link>
      <guid>https://dev.to/posibble/openvpn-on-aws-secure-access-to-private-infrastructure-534n</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Many DevOps engineers and Site Reliability Engineers (SREs) have likely worked on projects where the highest priority during the early stages was always:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Get the system running first.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When deadlines are tight, infrastructure must be provisioned quickly, and environments need to be ready immediately for developers to deploy applications, many trade-offs are often made in exchange for deployment speed. SSH is exposed publicly for easier access. Grafana, Jenkins, or ArgoCD are directly accessible from the Internet for easier troubleshooting. Sometimes even databases are temporarily opened to public access simply so the team can move faster.&lt;/p&gt;

&lt;p&gt;At that moment, everyone knows:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This isn’t ideal.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But most people also think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;““It can be improved later.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then real users start arriving, traffic increases, and the production environment begins operating at scale. And that is exactly when security debt starts demanding repayment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is it really safe when every service is exposed to the Internet?&lt;/li&gt;
&lt;li&gt;How can engineers still access private resources without exposing the entire system?&lt;/li&gt;
&lt;li&gt;How do we maintain operational convenience without turning production into a massive attack surface?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we will explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What VPN actually is&lt;/li&gt;
&lt;li&gt;Why VPN remains extremely important&lt;/li&gt;
&lt;li&gt;How OpenVPN works&lt;/li&gt;
&lt;li&gt;A hands-on demonstration of deploying OpenVPN Access Server on AWS and securely accessing private resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article shares practical lessons learned from operating real-world infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Section 1 — VPN Fundamentals
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. What Is a VPN?
&lt;/h3&gt;

&lt;p&gt;VPN (Virtual Private Network) is a technology that creates a private and encrypted connection over the public Internet infrastructure.&lt;/p&gt;

&lt;p&gt;Instead of exposing the entire system directly to the Internet for access, we create a secure tunnel between the user’s computer and the internal system.&lt;/p&gt;

&lt;p&gt;Once the VPN connection is established successfully, your device behaves almost as if it were located inside the company’s private network or cloud infrastructure.&lt;/p&gt;

&lt;p&gt;That is why, after enabling VPN, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSH into private EC2 instances&lt;/li&gt;
&lt;li&gt;Access internal databases&lt;/li&gt;
&lt;li&gt;Open non-public Grafana dashboards&lt;/li&gt;
&lt;li&gt;Interact with private Kubernetes API endpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From the system’s perspective, your laptop essentially becomes “a member” of the internal network.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. What Problems Does VPN Solve?
&lt;/h3&gt;

&lt;p&gt;One common misconception is that VPN is only used to change IP addresses, hide locations and stay anonymous. In reality, those are merely side effects. The true value of VPN lies in creating a secure connection between users and internal systems.&lt;/p&gt;

&lt;h4&gt;
  
  
  Accessing Private Resources from the Internet
&lt;/h4&gt;

&lt;p&gt;❓If everything is publicly exposed to the Internet, access certainly becomes convenient — but it also means anyone on the Internet can “knock on the door.”&lt;/p&gt;

&lt;p&gt;In properly designed systems, many resources are intentionally kept private, such as: EC2 instances, databases, Kubernetes control planes, Jenkins servers. However, operations teams still need daily access to them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developers need SSH access for debugging&lt;/li&gt;
&lt;li&gt;DevOps engineers need monitoring access&lt;/li&gt;
&lt;li&gt;SREs need to handle midnight incidents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ VPN solves this problem by creating a secure connection between the user device and the private network. After connecting to the VPN, users can access private resources as if they were operating inside the same internal network.&lt;/p&gt;

&lt;h4&gt;
  
  
  Reducing the Attack Surface
&lt;/h4&gt;

&lt;p&gt;❓Every service exposed to the Internet — such as SSH, RDP, PostgreSQL, or administrative dashboards — can become a target for port scanning, brute-force attacks, vulnerability exploitation. As systems grow larger, temporarily exposing “just a few services” often evolves into dozens of security groups, numerous exceptions, complex rule chains. Eventually, the environment becomes difficult to manage securely.&lt;/p&gt;

&lt;p&gt;✅ VPN reverses this mindset. Instead of exposing services and attempting to protect them afterward, we keep services private from the beginning. Only users with valid accounts, successful authentication, authorized VPN access can reach internal resources.&lt;/p&gt;

&lt;h4&gt;
  
  
  Replacing Traditional IP Whitelisting &amp;amp; Protecting Data on Public Networks
&lt;/h4&gt;

&lt;p&gt;❓IP whitelisting is a common approach for restricting access by allowing only specific IP addresses. This works well... Until engineers work remotely, travel for business, use mobile networks, sit in coffee shops. Their IP addresses constantly change, leading to requests such as “Can you whitelist my new IP”, “My Wi-Fi changed”, “I’m at the airport”, “I urgently need production access”.&lt;/p&gt;

&lt;p&gt;✅ VPN approaches the problem differently. Instead of trusting IP addresses, we trust user identities, certificates, authentication mechanisms, multi-factor authentication (MFA). As long as authentication succeeds, users can securely access the system from anywhere. VPN also creates an encrypted communication channel between the user device and the VPN server, protecting data during transmission.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. How Does VPN Work?
&lt;/h3&gt;

&lt;p&gt;Behind the seemingly simple “Connect” button, many components work together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9u3r1de8otd7swff8y0p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9u3r1de8otd7swff8y0p.png" alt="VPN Work" width="462" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication: Users must authenticate with the VPN server using username/password, certificates, MFA. Only authorized users can establish VPN connections&lt;/li&gt;
&lt;li&gt;Tunneling Protocol &amp;amp; Encryption: After authentication succeeds, the VPN establishes an encrypted tunnel between the VPN client and VPN server. All traffic transmitted through this tunnel is encrypted. As a result VPN significantly reduces the risk of traffic sniffing when using public Wi-Fi. Common protocols include TLS, SSL, IPSec&lt;/li&gt;
&lt;li&gt;Virtual Network Interface: Once the VPN connection is established, the operating system creates a virtual network interface. For example: Windows creates a new adapter, Linux creates &lt;code&gt;tun0&lt;/code&gt; or &lt;code&gt;tap0&lt;/code&gt; , macOS creates a similar interface. At the same time, the device receives an IP address belonging to the VPN network. At that moment, your laptop officially becomes part of the VPN network&lt;/li&gt;
&lt;li&gt;Traffic Routing / Route Table: The VPN client receives required routes from the VPN server. As a result, traffic destined for private networks goes through the VPN tunnel, normal Internet traffic may still go directly to the Internet depending on configuration. This is exactly how Split Tunnel works&lt;/li&gt;
&lt;li&gt;NAT: In many VPN deployment models, the VPN server performs NAT or routing to allow VPN clients to communicate with the private network behind it. Depending on the architecture, VPN clients may be routed directly, or NATed through the VPN server before accessing internal resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Full Tunnel vs Split Tunnel
&lt;/h3&gt;

&lt;p&gt;After a VPN connection is established, there are two common routing models.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criteria&lt;/th&gt;
&lt;th&gt;Full Tunnel&lt;/th&gt;
&lt;th&gt;Split Tunnel&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Traffic through VPN&lt;/td&gt;
&lt;td&gt;All traffic&lt;/td&gt;
&lt;td&gt;Only internal traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VPN Server bandwidth usage&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internet performance&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;td&gt;Better&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Level of control&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Suitable for&lt;/td&gt;
&lt;td&gt;Enterprises needing full traffic control&lt;/td&gt;
&lt;td&gt;DevOps, SRE, Cloud Infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Throughout this guide, we will use Split Tunnel to access private resources on AWS while allowing normal Internet traffic to go directly to the Internet.&lt;/p&gt;

&lt;p&gt;Now that we understand the fundamentals of VPN technology, let’s explore why OpenVPN remains one of the most widely adopted VPN solutions today.&lt;/p&gt;




&lt;h2&gt;
  
  
  Section 2 — OpenVPN Solution Overview
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Popular VPN Solutions
&lt;/h3&gt;

&lt;p&gt;Today, many VPN solutions are available on the market. Each solution is designed for different use cases and environments.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;th&gt;Highlights&lt;/th&gt;
&lt;th&gt;Suitable For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OpenVPN&lt;/td&gt;
&lt;td&gt;Open-source, stable, widely adopted&lt;/td&gt;
&lt;td&gt;Personal use, enterprise, cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WireGuard&lt;/td&gt;
&lt;td&gt;High performance, modern, simple configuration&lt;/td&gt;
&lt;td&gt;Cloud, Kubernetes, homelab&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IPSec VPN&lt;/td&gt;
&lt;td&gt;Traditional VPN standard integrated into networking devices&lt;/td&gt;
&lt;td&gt;Enterprise networks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS Client VPN&lt;/td&gt;
&lt;td&gt;AWS-managed VPN service&lt;/td&gt;
&lt;td&gt;Pure AWS environments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Site-to-Site VPN&lt;/td&gt;
&lt;td&gt;Connects two separate networks&lt;/td&gt;
&lt;td&gt;Hybrid cloud, data centers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In this article, we choose OpenVPN because it is almost a “classic” technology in the infrastructure world:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Beginner-friendly&lt;/li&gt;
&lt;li&gt;Large community support&lt;/li&gt;
&lt;li&gt;Rich documentation&lt;/li&gt;
&lt;li&gt;Multi-platform support&lt;/li&gt;
&lt;li&gt;Excellent for understanding VPN fundamentals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, many modern VPN solutions still inherit concepts similar to OpenVPN, so understanding OpenVPN also helps explain how other VPN systems operate.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. OpenVPN Community Edition vs Access Server
&lt;/h3&gt;

&lt;p&gt;OpenVPN currently has two common deployment approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenVPN Community Edition&lt;/li&gt;
&lt;li&gt;OpenVPN Access Server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although both are based on the same OpenVPN technology, they target different audiences.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criteria&lt;/th&gt;
&lt;th&gt;Community Edition&lt;/th&gt;
&lt;th&gt;Access Server&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Completely free&lt;/td&gt;
&lt;td&gt;Free with limited concurrent connections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Management Interface&lt;/td&gt;
&lt;td&gt;CLI&lt;/td&gt;
&lt;td&gt;Web UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User Management&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Certificate Management&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Automated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Routing Configuration&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Web UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MFA Support&lt;/td&gt;
&lt;td&gt;Manual setup&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment Complexity&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary Use Case&lt;/td&gt;
&lt;td&gt;Learning, deep customization&lt;/td&gt;
&lt;td&gt;Fast deployment, easy operations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In this article, instead of spending too much time on manual configuration, we will use Access Server to focus more on VPN concepts, networking flow and operational workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. OpenVPN Access Server Architecture
&lt;/h3&gt;

&lt;p&gt;One reason OpenVPN Access Server is widely adopted is because its architecture is relatively simple and easy to deploy.&lt;/p&gt;

&lt;p&gt;In a basic deployment model, the Access Server acts as the gateway into the internal network.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fndk3h64g1klpkkj1zjk2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fndk3h64g1klpkkj1zjk2.png" alt="OpenVPN Architecture" width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When users connect to the VPN:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;OpenVPN Access Server authenticates the user&lt;/li&gt;
&lt;li&gt;A VPN tunnel is established&lt;/li&gt;
&lt;li&gt;An IP address belonging to the VPN network is assigned&lt;/li&gt;
&lt;li&gt;Required routes are pushed to the VPN client&lt;/li&gt;
&lt;li&gt;Traffic is forwarded to internal resources&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With the core concepts covered, we can now move on to deploying a practical OpenVPN environment on AWS.&lt;/p&gt;




&lt;h2&gt;
  
  
  Section 3 — Deploying OpenVPN Access Server on AWS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Demo Objective — Hands-On Time
&lt;/h3&gt;

&lt;p&gt;At this stage, the theory should provide a solid understanding of what VPN is, how it works and why it appears in nearly every modern cloud system. So instead of continuing with concepts, we will build a real AWS lab environment to observe how VPN traffic behaves in a production-like environment.&lt;/p&gt;

&lt;p&gt;The objectives of this demo are straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect a personal laptop to AWS through VPN&lt;/li&gt;
&lt;li&gt;Access an EC2 instance located inside a private subnet&lt;/li&gt;
&lt;li&gt;SSH into an instance without a public IP&lt;/li&gt;
&lt;li&gt;Observe how traffic is routed through the VPN tunnel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If everything works correctly, our laptop connected from the public Internet will be able to access private infrastructure as if it were located inside the same internal network.&lt;/p&gt;

&lt;p&gt;That is the real value of VPN in cloud environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. AWS Architecture
&lt;/h3&gt;

&lt;p&gt;To focus on VPN and networking flow instead of complicated infrastructure, we will use a minimal architecture consisting of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 VPC&lt;/li&gt;
&lt;li&gt;1 Public Subnet&lt;/li&gt;
&lt;li&gt;1 Private Subnet&lt;/li&gt;
&lt;li&gt;1 OpenVPN Access Server&lt;/li&gt;
&lt;li&gt;1 Private EC2 Instance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3f7q2nemakp6ea7wthj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3f7q2nemakp6ea7wthj.png" alt="AWS Architecture" width="800" height="597"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenVPN Access Server accepts VPN connections from the Internet&lt;/li&gt;
&lt;li&gt;The private EC2 instance has no public IP&lt;/li&gt;
&lt;li&gt;All access to the private EC2 instance must go through the VPN&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architecture reflects how many organizations secure production environments on AWS.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Preparing AWS Infrastructure
&lt;/h3&gt;

&lt;p&gt;The goal of this article is to focus on VPN and networking concepts rather than detailed infrastructure provisioning. Therefore, instead of manually creating every AWS resource, we will use a prepared Terraform configuration to provision the lab environment quickly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://raw.githubusercontent.com/orez-fu/blog_content/refs/heads/master/vpn/part_1/main.tf" rel="noopener noreferrer"&gt;Terraform Source Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Terraform provisions the following components:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VPC&lt;/td&gt;
&lt;td&gt;Private network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public Subnet&lt;/td&gt;
&lt;td&gt;Hosts OpenVPN Access Server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private Subnet&lt;/td&gt;
&lt;td&gt;Hosts Private EC2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internet Gateway&lt;/td&gt;
&lt;td&gt;Provides Internet access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Route Table&lt;/td&gt;
&lt;td&gt;Controls traffic routing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security Group&lt;/td&gt;
&lt;td&gt;Controls network access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EC2 Instance&lt;/td&gt;
&lt;td&gt;OpenVPN Server with Elastic IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EC2 Instance&lt;/td&gt;
&lt;td&gt;Private test server without Public IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Elastic IP&lt;/td&gt;
&lt;td&gt;Public IP for OpenVPN Server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Instead of installing OpenVPN manually by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generating CA certificates&lt;/li&gt;
&lt;li&gt;Creating certificates&lt;/li&gt;
&lt;li&gt;Configuring daemons&lt;/li&gt;
&lt;li&gt;Setting up routing&lt;/li&gt;
&lt;li&gt;Configuring NAT&lt;/li&gt;
&lt;li&gt;Managing iptables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;we will use the official OpenVPN Access Server AMI from AWS Marketplace. This is a common real-world approach because it enables faster deployment, easier maintenance, practical lab and PoC environments.&lt;/p&gt;

&lt;p&gt;Before Terraform can provision the EC2 instance using this AMI, AWS requires the account to subscribe to the Marketplace product first.&lt;/p&gt;

&lt;p&gt;Access the OpenVPN Access Server Marketplace product:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/marketplace/procurement/?productId=fe8020db-5343-4c43-9e65-5ed4a825c931&amp;amp;redirectUrl=https%3A%2F%2Faws.amazon.com%2Fmarketplace%2Fpp%2Fprodview-y3m73u6jd5srk" rel="noopener noreferrer"&gt;AWS Marketplace Procurement Page&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then follow these steps:&lt;/p&gt;

&lt;p&gt;→ Select &lt;strong&gt;Continue to Subscribe&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Accept the terms and conditions (&lt;strong&gt;Accept Terms&lt;/strong&gt;)&lt;/p&gt;

&lt;p&gt;→ Wait until the status changes to &lt;strong&gt;Subscribed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once completed, you can monitor the subscription status. Please note that the OpenVPN Access Server version used in this article is the &lt;strong&gt;BYOL (Bring Your Own License)&lt;/strong&gt; edition. This version allows free usage with a limited number of concurrent connections, making it suitable for learning and testing purposes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm3mce9vwsitao2390e6l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm3mce9vwsitao2390e6l.png" alt="AWS AMI Subscription" width="799" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After completing the Marketplace subscription, we can begin setting up the lab environment. Make sure your computer has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;AWS CLI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Configure &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html" rel="noopener noreferrer"&gt;AWS credentials&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Install &lt;a href="https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli" rel="noopener noreferrer"&gt;Terraform CLI&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, download the Terraform source code and provision the AWS infrastructure using the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
terraform plan
terraform apply &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The provisioning process may take several minutes.&lt;/p&gt;

&lt;p&gt;Once completed, Terraform outputs the public IP address assigned to the OpenVPN Access Server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhk3gr6zwnhf6bhzt181.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhk3gr6zwnhf6bhzt181.png" alt="Terraform Outputs" width="799" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Installing OpenVPN Access Server
&lt;/h3&gt;

&lt;h4&gt;
  
  
  4.1 Creating an OpenVPN Access Server Account
&lt;/h4&gt;

&lt;p&gt;Visit the following link: &lt;a href="https://myaccount.openvpn.com/signin/product-select" rel="noopener noreferrer"&gt;https://myaccount.openvpn.com/signin/product-select&lt;/a&gt; and register for a new account, or sign in using your Google or Microsoft account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Firsv7g9a300dme7rwxw0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Firsv7g9a300dme7rwxw0.png" alt="OpenVPN Signup" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the &lt;strong&gt;Access Server&lt;/strong&gt; product and complete the account information setup process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1c1qycixux608uxl6xv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1c1qycixux608uxl6xv.png" alt="OpenVPN Signup" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will then be redirected to the OpenVPN application console. From the &lt;strong&gt;Subscription&lt;/strong&gt; menu, you will see the default subscription plan set to &lt;strong&gt;Free Plan&lt;/strong&gt;, along with the &lt;strong&gt;Activation Key&lt;/strong&gt; used to activate your OpenVPN Access Server instance. Copy this key for use in the next step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8mpmhgndrn0vp88nefbt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8mpmhgndrn0vp88nefbt.png" alt="OpenVPN Get Activation Key" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  4.2 Initial Setup
&lt;/h4&gt;

&lt;p&gt;Once the EC2 instance has finished provisioning, OpenVPN Access Server is actually already pre-installed inside the instance through the AWS Marketplace AMI. However, the system still requires an initial setup before it can operate fully.&lt;/p&gt;

&lt;p&gt;Access the &lt;code&gt;openvpn-access-server&lt;/code&gt; EC2 instance through the AWS Console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5rzcxkpbxlptf51zynw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5rzcxkpbxlptf51zynw.png" alt="AWS Connect Public EC2" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, when accessing the instance for the first time, OpenVPN Access Server will prompt you to perform the initial configuration. Alternatively, you can run the following command to start or re-run the setup process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ovpn-init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During the initial setup, you will be prompted to configure several settings. (You can type &lt;strong&gt;Yes&lt;/strong&gt; or press &lt;strong&gt;Enter&lt;/strong&gt; to use the default configuration.)&lt;/p&gt;

&lt;p&gt;The setup process includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accepting the license agreement&lt;/li&gt;
&lt;li&gt;Confirming the Primary Node role&lt;/li&gt;
&lt;li&gt;Selecting the network interface&lt;/li&gt;
&lt;li&gt;Choosing the encryption algorithm&lt;/li&gt;
&lt;li&gt;Configuring the ports for the Web Admin UI and OpenVPN Daemon&lt;/li&gt;
&lt;li&gt;Setting the password for the Web Admin UI&lt;/li&gt;
&lt;li&gt;Entering the Activation Key collected in the previous step&lt;/li&gt;
&lt;li&gt;Configuring other optional settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the installation is completed, you will see both the &lt;strong&gt;Admin UI&lt;/strong&gt; and &lt;strong&gt;Client UI&lt;/strong&gt; URLs. Please note that these URLs use private IP addresses by default, so you will need to replace them with the corresponding public IP address when accessing them externally.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39mkl58k8td4pah6vfxb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39mkl58k8td4pah6vfxb.png" alt="AWS Init OPVN Server" width="799" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  4.3 Accessing the Admin UI &amp;amp; Client UI
&lt;/h4&gt;

&lt;p&gt;After the initial setup is completed, OpenVPN Access Server provides two interfaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Admin UI&lt;/strong&gt;: used to manage users, routing, authentication, and VPN configurations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client UI&lt;/strong&gt;: used to download the OpenVPN Client application, download connection profiles, and connect to the VPN.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Access the Admin UI in your web browser using the Public IP Address of the &lt;code&gt;openvpn-access-server&lt;/code&gt; EC2 instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;Public-IP&amp;gt;:943/admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fakhonzo82o5bl4d2bldl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fakhonzo82o5bl4d2bldl.png" alt="OpenVPN Admin UI Login" width="799" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Log in using the username &lt;code&gt;openvpn&lt;/code&gt; and the password configured in the previous step. If you skipped that step, you will be prompted to set a new password.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq2fx9jri6l6xqvjsc0wx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq2fx9jri6l6xqvjsc0wx.png" alt="OpenVPN Admin UI Status" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, OpenVPN Access Server is typically configured to use the EC2 instance’s private IP address as the Server Address. You can verify this information under &lt;strong&gt;Server Details &amp;gt; Server address&lt;/strong&gt;. However, in this lab environment, clients will connect from the public Internet. If the private IP address remains unchanged, the generated VPN profile will contain a private address, preventing external clients from connecting successfully.&lt;/p&gt;

&lt;p&gt;Therefore, after logging into the Admin UI, we need to update the Server Address to use the Public IP Address, specifically the Elastic IP Address associated with the EC2 instance.&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;VPN Server&lt;/strong&gt; menu:&lt;/p&gt;

&lt;p&gt;→ Enter the Public IP Address in the &lt;strong&gt;Hostname (or IP address)&lt;/strong&gt; field&lt;/p&gt;

&lt;p&gt;→ Click the &lt;strong&gt;Save&lt;/strong&gt; button&lt;/p&gt;

&lt;p&gt;→ Click the &lt;strong&gt;Restart&lt;/strong&gt; button&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjismi6vhq4v0v63yi7z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjismi6vhq4v0v63yi7z.png" alt="OpenVPN Admin UI VPN Server" width="799" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wait for OpenVPN Access Server to restart. Afterward, you should verify that the Server Address has been updated to the public IP address.&lt;/p&gt;

&lt;p&gt;The next step is very important. &lt;strong&gt;Access Controls&lt;/strong&gt;, also known as &lt;strong&gt;Routing Policy&lt;/strong&gt;, are used to route connections to private resources. In the Admin UI, create the first policy through the &lt;strong&gt;Access Controls&lt;/strong&gt; menu:&lt;/p&gt;

&lt;p&gt;→ Click the &lt;strong&gt;New Access Rule&lt;/strong&gt; button&lt;/p&gt;

&lt;p&gt;→ Enter the username &lt;code&gt;openvpn&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ Enter the CIDR &lt;code&gt;10.0.0.0/16&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;Save rule&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Click the &lt;strong&gt;Restart&lt;/strong&gt; button to apply the configuration changes&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw294ov7p42ja4iokj4ys.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw294ov7p42ja4iokj4ys.png" alt="OpenVPN Admin UI Access Control" width="799" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the Access Controls configuration is completed, OpenVPN Access Server will begin pushing routes to VPN clients, allowing traffic destined for the VPC CIDR to be routed through the VPN tunnel into the AWS VPC. You will verify this behavior in the following steps.&lt;/p&gt;

&lt;p&gt;Next, open a new browser tab and access the Client UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;Public-IP&amp;gt;:943/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since you have already logged into the OpenVPN Admin UI, your session credentials will also be reused for the Client UI.&lt;/p&gt;

&lt;p&gt;In this application, you will perform the following tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download and install the OpenVPN Client application for your computer or smartphone. Be sure to uncheck the option &lt;strong&gt;Include connection profile (User-locked)&lt;/strong&gt; so that the connection profile can be customized later. OpenVPN clients are also available for multiple platforms including Windows, macOS, Linux, iOS, and Android.&lt;/li&gt;
&lt;li&gt;Download the &lt;strong&gt;Connection Profile&lt;/strong&gt;. All required routing information, certificates, and VPN configurations are already embedded inside this profile.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdqpq7xtgmm5s38715y5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdqpq7xtgmm5s38715y5.png" alt="OpenVPN Client UI" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Configuring OpenVPN Client
&lt;/h3&gt;

&lt;p&gt;At this point, the infrastructure setup is nearly complete. OpenVPN Access Server is now running on AWS, the client profile has been downloaded, and the private EC2 instance is quietly waiting inside the Private Subnet to be accessed.&lt;/p&gt;

&lt;p&gt;Now comes the most interesting part: turning your laptop on the public Internet into a member of the private AWS network through three simple steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the OpenVPN Client application installed earlier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F907ygl88qmpspx06wm7t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F907ygl88qmpspx06wm7t.png" alt="OpenVPN Client Upload Profile" width="691" height="1190"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload the Connection Profile file downloaded in the previous step.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstrvytsz0w2lb5wurtt3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstrvytsz0w2lb5wurtt3.png" alt="OpenVPN Client Import Profile" width="689" height="1177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Connect&lt;/strong&gt; and enter your password.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fecnqjxwqkf2td88x8svt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fecnqjxwqkf2td88x8svt.png" alt="OpenVPN Client Connect" width="390" height="670"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The moment the &lt;strong&gt;Connect&lt;/strong&gt; button is pressed, many things begin happening behind the scenes almost invisibly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenVPN Client starts the handshake process with the VPN Server&lt;/li&gt;
&lt;li&gt;Authentication is performed&lt;/li&gt;
&lt;li&gt;A TLS tunnel is established&lt;/li&gt;
&lt;li&gt;Certificates are validated&lt;/li&gt;
&lt;li&gt;The route table is updated&lt;/li&gt;
&lt;li&gt;A virtual network interface is created&lt;/li&gt;
&lt;li&gt;A VPN IP address is assigned to the device&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The entire process usually takes only a few seconds. If everything works correctly, the OpenVPN Client status will change to &lt;strong&gt;Securely Connected&lt;/strong&gt;, along with a small traffic graph.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fen6iktjn326p6wqn4yoe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fen6iktjn326p6wqn4yoe.png" alt="OpenVPN Client Connected" width="393" height="667"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the VPN connection is successfully established, your operating system will display a new virtual network interface. You can verify the new interface using the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# For Windows&lt;/span&gt;
ipconfig /all

&lt;span class="c"&gt;# For macOS&lt;/span&gt;
ifconfig

&lt;span class="c"&gt;# For Linux (Ubuntu, Debian, RHEL, ...)&lt;/span&gt;
ip addr show
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fglbq1j9dmz4h9d4nx5iu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fglbq1j9dmz4h9d4nx5iu.png" alt="Verify New Interface" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to dig a little deeper, you can also inspect your operating system’s route table. You will notice that traffic destined for the AWS VPC is now routed through the VPN interface instead of going directly to the Internet.&lt;/p&gt;

&lt;p&gt;This mechanism that allows a private EC2 instance to remain without a Public IP address while still being securely accessible from your personal laptop.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Testing VPN Connectivity
&lt;/h3&gt;

&lt;p&gt;From the AWS VPC’s perspective, your computer has now become a member of the internal network. You now have a “secure tunnel” that allows access to resources inside the Private Subnet — in this case, establishing an SSH connection to the private instance &lt;code&gt;private-test-server&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When provisioning the AWS infrastructure, Terraform also creates an SSH key pair to provide authentication for the private instance. Run the following command to connect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; vpn-demo-ssh-key.pem ec2-user@&amp;lt;Private-Instance-IP&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flghvbe8i6lalv2h3iq9c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flghvbe8i6lalv2h3iq9c.png" alt="Verify SSH Private EC2" width="799" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please note that you should configure secure file permissions for &lt;code&gt;vpn-demo-ssh-key.pem&lt;/code&gt; before executing the SSH command above.&lt;/p&gt;

&lt;p&gt;Once you successfully connect to the private EC2 instance, it proves that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The VPN tunnel is working correctly&lt;/li&gt;
&lt;li&gt;Routing has been configured properly&lt;/li&gt;
&lt;li&gt;Private resources can be accessed without exposing them to the public Internet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the true power of VPNs in cloud environments:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keeping infrastructure private while still allowing operations teams to securely access resources whenever necessary.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At this point, everything is working successfully. You can try disconnecting the VPN session in the OpenVPN Client. The SSH connection will eventually time out because the private EC2 instance does not have a Public IP address and cannot be accessed directly from the Internet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasxj3h2g48yz2fm7sq5t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasxj3h2g48yz2fm7sq5t.png" alt="Verify SSH Private EC2 Failed" width="509" height="89"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Section 4 — Conclusion
&lt;/h2&gt;

&lt;p&gt;VPNs are far more than tools for masking IP addresses. It is also one of the most important solutions for protecting infrastructure and controlling access in cloud environments. Through this article, we explored the fundamental concepts of VPNs, how VPNs work, the problems they help solve, and successfully deployed OpenVPN Access Server on AWS to securely access private resources.&lt;/p&gt;

&lt;p&gt;Although VPNs significantly improve security, the VPN Server itself is also a critical access point that must be carefully protected. Some recommended security best practices include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use strong passwords and avoid using default accounts&lt;/li&gt;
&lt;li&gt;Enable MFA whenever possible&lt;/li&gt;
&lt;li&gt;Restrict Security Groups following the principle of least privilege&lt;/li&gt;
&lt;li&gt;Rotate credentials and certificates regularly&lt;/li&gt;
&lt;li&gt;Avoid directly exposing internal resources to the public Internet&lt;/li&gt;
&lt;li&gt;Monitor access logs to detect suspicious activities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding VPN fundamentals is essential for building secure cloud infrastructure. OpenVPN provides a practical and accessible way to implement secure remote access while keeping critical systems private.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;OpenVPN Documentation (&lt;a href="https://openvpn.net/as-docs/" rel="noopener noreferrer"&gt;https://openvpn.net/as-docs/&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;AWS VPC Documentation (&lt;a href="https://docs.aws.amazon.com/vpc/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/vpc/?utm_source=chatgpt.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;AWS EC2 Documentation (&lt;a href="https://docs.aws.amazon.com/ec2/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/ec2/?utm_source=chatgpt.com&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>aws</category>
      <category>vpn</category>
    </item>
    <item>
      <title>Amazon EKS From The Ground Up - Part 2: Worker Nodes with AWS Managed Nodes</title>
      <dc:creator>Phu Hoang</dc:creator>
      <pubDate>Sat, 10 Jan 2026 08:03:00 +0000</pubDate>
      <link>https://dev.to/posibble/amazon-eks-from-the-ground-up-part-2-worker-nodes-in-aws-managed-nodes-way-en-1lhh</link>
      <guid>https://dev.to/posibble/amazon-eks-from-the-ground-up-part-2-worker-nodes-in-aws-managed-nodes-way-en-1lhh</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/posibble/amazon-eks-from-the-ground-up-part-1-control-plane-infrastructure-47pf"&gt;&lt;strong&gt;Part 1&lt;/strong&gt;&lt;/a&gt;, we successfully finished building the EKS &lt;strong&gt;Control Plane&lt;/strong&gt;, we set up the VPC, Subnets, NAT Gateway, the Kubernetes API Server, and configured &lt;code&gt;kubectl&lt;/code&gt; connectivity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPC, Subnets, NAT&lt;/li&gt;
&lt;li&gt;Kubernetes API Server&lt;/li&gt;
&lt;li&gt;An IAM role with enough permissions for the Control Plane&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl&lt;/code&gt; already connected to the cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But if you run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you’ll see… &lt;strong&gt;nothing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That’s the real state of the cluster we created: it has a &lt;strong&gt;brain (Control Plane)&lt;/strong&gt;, but no &lt;strong&gt;hands and feet (Worker Nodes)&lt;/strong&gt;. Without compute capacity, the Kubernetes Scheduler can only stare at Pods stuck in the &lt;code&gt;Pending&lt;/code&gt; state.&lt;/p&gt;

&lt;p&gt;In this Part 2, we’ll “grow limbs” for your EKS cluster by deploying Worker Nodes using &lt;strong&gt;AWS Managed Nodes&lt;/strong&gt; - the most practical middle ground between production readiness and operational effort.&lt;/p&gt;

&lt;p&gt;This post won’t stop at “click and it works.” We’ll also explore what AWS quietly builds for you in a Managed Node Group, and contrast it with the &lt;strong&gt;Self-managed&lt;/strong&gt; way so you understand the fundamentals and can debug with confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  📝A Quick Primer on Worker Nodes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is a Worker Node, really?
&lt;/h3&gt;

&lt;p&gt;In the most accurate - and simplest - definition:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A Worker Node is an EC2 instance configured to run Kubernetes workloads (Pods).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At minimum, every worker node runs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kubelet&lt;/code&gt; — the agent that talks to the Kubernetes API Server&lt;/li&gt;
&lt;li&gt;a container runtime — typically &lt;code&gt;containerd&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kube-proxy&lt;/code&gt; — manages service/network rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS VPC CNI&lt;/strong&gt; — the plugin that allocates Pod IPs from your VPC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Following production best practices, EKS worker nodes usually live inside &lt;strong&gt;private subnets&lt;/strong&gt; without public IPs. That significantly reduces exposure: workloads are not directly reachable from the Internet, and outbound connectivity is handled through a &lt;strong&gt;NAT Gateway&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Worker Node Deployment Models
&lt;/h3&gt;

&lt;p&gt;AWS offers three main ways to run compute for EKS, each with a different balance of control, operational cost, and “serverless-ness”:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criteria&lt;/th&gt;
&lt;th&gt;Managed Nodes&lt;/th&gt;
&lt;th&gt;Self-managed Nodes&lt;/th&gt;
&lt;th&gt;AWS Fargate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure&lt;/td&gt;
&lt;td&gt;EC2 Instances (partly managed by AWS)&lt;/td&gt;
&lt;td&gt;EC2 Instances (fully managed by you)&lt;/td&gt;
&lt;td&gt;Serverless (no nodes to manage)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operational cost (OpEx)&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Very low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Highest&lt;/td&gt;
&lt;td&gt;Lowest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Billing&lt;/td&gt;
&lt;td&gt;Per EC2 instance&lt;/td&gt;
&lt;td&gt;Per EC2 instance&lt;/td&gt;
&lt;td&gt;Per Pod (CPU/Mem)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Fargate hides almost everything—including the “node layer.” That’s convenient, but if your goal is to understand EKS fundamentals, the interesting engineering happens in Managed Nodes vs Self-managed Nodes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criteria&lt;/th&gt;
&lt;th&gt;Managed Nodes (Managed Node Group)&lt;/th&gt;
&lt;th&gt;Self-managed Nodes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Node creation&lt;/td&gt;
&lt;td&gt;EKS Console or CLI command creates a Node Group&lt;/td&gt;
&lt;td&gt;EC2 Launch Template + ASG +  User Data (bootstrap)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ASG management&lt;/td&gt;
&lt;td&gt;AWS manages the Auto Scaling Group lifecycle&lt;/td&gt;
&lt;td&gt;You manage the ASG entirely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster Join&lt;/td&gt;
&lt;td&gt;AWS automatically handles the wiring and credentials for join&lt;/td&gt;
&lt;td&gt;You must provide user data calling &lt;code&gt;/etc/eks/bootstrap.sh&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node auth mapping (IAM→ RBAC)&lt;/td&gt;
&lt;td&gt;AWS typically maps the Node Role automatically&lt;/td&gt;
&lt;td&gt;You must manually update &lt;code&gt;aws-auth&lt;/code&gt; to map the Node Role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upgrades/Updates&lt;/td&gt;
&lt;td&gt;Built-in, managed rolling update workflows&lt;/td&gt;
&lt;td&gt;You design and manage &lt;code&gt;drain&lt;/code&gt; / &lt;code&gt;replace&lt;/code&gt; strategies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debug level&lt;/td&gt;
&lt;td&gt;Fewer common traps, higher abstraction&lt;/td&gt;
&lt;td&gt;More control, but more responsibility for low-level configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What AWS does for you in a Managed Node Group
&lt;/h3&gt;

&lt;p&gt;When you click &lt;strong&gt;Create Node Group&lt;/strong&gt;, AWS typically handles a long checklist that Self-managed nodes would require you to build manually:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creates an &lt;strong&gt;Auto Scaling Group&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Picks an &lt;strong&gt;EKS-Optimized AMI&lt;/strong&gt; compatible with your cluster version&lt;/li&gt;
&lt;li&gt;Creates/manages a &lt;strong&gt;Launch Template&lt;/strong&gt; (or uses one you provide)&lt;/li&gt;
&lt;li&gt;Attaches an &lt;strong&gt;IAM Instance Profile&lt;/strong&gt; using your Node IAM Role&lt;/li&gt;
&lt;li&gt;Injects the bootstrap configuration so nodes can join the cluster&lt;/li&gt;
&lt;li&gt;Automates the node registration path&lt;/li&gt;
&lt;li&gt;Provides a &lt;strong&gt;rolling update workflow&lt;/strong&gt; for node group upgrades&lt;/li&gt;
&lt;li&gt;Typically handles node role mapping into the cluster auth mechanism&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Recommendation: Use Managed Nodes for most production setups to reduce operational overhead. Choose Self-managed only when you have very specific requirements (custom OS hardening, special bootstrap, deep control of the node lifecycle).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Cluster IAM Role vs Node IAM Role
&lt;/h3&gt;

&lt;p&gt;This is one of the most common points of confusion, so let’s make it crystal clear.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cluster IAM Role
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Used by the &lt;strong&gt;EKS Control Plane&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Allows the Control Plane to manage ENIs and interact with your VPC resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This role is &lt;strong&gt;not&lt;/strong&gt; meant for workloads.&lt;/p&gt;

&lt;h4&gt;
  
  
  Node IAM Role
&lt;/h4&gt;

&lt;p&gt;Worker nodes need a Node IAM Role to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Join the cluster&lt;/li&gt;
&lt;li&gt;Allow the VPC CNI to attach ENIs and allocate Pod IPs&lt;/li&gt;
&lt;li&gt;Pull images from ECR&lt;/li&gt;
&lt;li&gt;Access other required AWS APIs (later: secrets, parameters, logs, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your worker nodes won’t become &lt;code&gt;Ready&lt;/code&gt; without (at least) these managed policies:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Policy&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AmazonEKSWorkerNodePolicy.html" rel="noopener noreferrer"&gt;&lt;code&gt;AmazonEKSWorkerNodePolicy&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Join cluster, talk to the API Server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AmazonEKS_CNI_Policy.html" rel="noopener noreferrer"&gt;&lt;code&gt;AmazonEKS_CNI_Policy&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Attach ENIs, allocate Pod IPs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AmazonEC2ContainerRegistryReadOnly.html" rel="noopener noreferrer"&gt;&lt;code&gt;AmazonEC2ContainerRegistryReadOnly&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Pull images from ECR&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  AWS IAM &amp;amp; Kubernetes Authentication
&lt;/h3&gt;

&lt;p&gt;Access to an EKS cluster is a combination of two layers: &lt;strong&gt;AWS IAM&lt;/strong&gt; &amp;amp; &lt;strong&gt;Kubernetes RBAC&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  IAM = Authentication
&lt;/h4&gt;

&lt;p&gt;IAM answers: &lt;strong&gt;“Who are you?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When a principal (an EC2 node or a human user) calls the Kubernetes API Server, EKS uses IAM authentication to verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which IAM principal (Role/User) the request comes from&lt;/li&gt;
&lt;li&gt;Whether the request has a valid SigV4 signature&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ This is why worker nodes must have a proper Node IAM Role attached.&lt;/p&gt;

&lt;h4&gt;
  
  
  Kubernetes RBAC = Authorization
&lt;/h4&gt;

&lt;p&gt;RBAC answers: &lt;strong&gt;“What are you allowed to do?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even if IAM authentication succeeds, the API call can still fail with &lt;code&gt;Forbidden&lt;/code&gt; if RBAC doesn’t grant the required permissions.&lt;/p&gt;

&lt;h4&gt;
  
  
  Bridging the two worlds: mapping IAM → Kubernetes identity
&lt;/h4&gt;

&lt;p&gt;After IAM authentication, EKS maps IAM identity into Kubernetes users/groups so RBAC can evaluate permissions. Two common mechanisms exist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;aws-auth ConfigMap&lt;/strong&gt; (classic, still widely used), example:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;mapRoles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
  &lt;span class="s"&gt;- rolearn: arn:aws:iam::&amp;lt;account-id&amp;gt;:role/EKSNodeRole&lt;/span&gt;
    &lt;span class="s"&gt;username: system:node:{{EC2PrivateDNSName}}&lt;/span&gt;
    &lt;span class="s"&gt;groups:&lt;/span&gt;
      &lt;span class="s"&gt;- system:bootstrappers&lt;/span&gt;
      &lt;span class="s"&gt;- system:nodes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;EKS Access Entries / Access Policies&lt;/strong&gt; (newer Console-based approach)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nodes&lt;/strong&gt; must be mapped into groups like &lt;code&gt;system:bootstrappers&lt;/code&gt; and &lt;code&gt;system:nodes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Humans/admins&lt;/strong&gt; are commonly mapped to &lt;code&gt;system:masters&lt;/code&gt; or granted an equivalent Access Policy&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hands-on Time
&lt;/h2&gt;

&lt;p&gt;The AWS architecture after completing the steps below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F15xyqwg5xsilfrfuvwve.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F15xyqwg5xsilfrfuvwve.png" alt="part_2_aws_architecture.png" width="800" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 0 — Preparation
&lt;/h3&gt;

&lt;p&gt;Make sure all resources from Part 1 are created correctly and your cluster is &lt;code&gt;ACTIVE&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 — Create the IAM Role for Worker Nodes
&lt;/h3&gt;

&lt;p&gt;Open the AWS Console:&lt;/p&gt;

&lt;p&gt;→ Go to &lt;strong&gt;IAM&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Choose &lt;strong&gt;Roles&lt;/strong&gt; → &lt;strong&gt;Create role&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Configure &lt;strong&gt;Trusted entity&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trusted entity type: &lt;strong&gt;AWS service&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Service or use case: &lt;strong&gt;EC2&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use case: &lt;strong&gt;EC2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb75z1neletp23m4ijeox.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb75z1neletp23m4ijeox.png" alt="part_2_IAM_Role_create_step_1.png" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;→ Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Attach policies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AmazonEKSWorkerNodePolicy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AmazonEKS_CNI_Policy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AmazonEC2ContainerRegistryReadOnly&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;→ Role name: &lt;code&gt;EKSNodeRole&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;Create role&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj5exsoblbo20litvd46o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj5exsoblbo20litvd46o.png" alt="part_2_IAM_Role_create_step_3.png" width="800" height="713"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 — Create a Managed Node Group
&lt;/h3&gt;

&lt;p&gt;→ Open the &lt;strong&gt;EKS&lt;/strong&gt; service&lt;/p&gt;

&lt;p&gt;→ Select cluster &lt;code&gt;demo-eks-cluster&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ Go to &lt;strong&gt;Compute&lt;/strong&gt; → &lt;strong&gt;Add node group&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb50cnftd7fl7zey42p26.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb50cnftd7fl7zey42p26.png" alt="part_2_eks_compute_add.png" width="800" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Configure node group
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;eks-mng-general&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Node IAM role: &lt;code&gt;EKSNodeRole&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fffnxs5h7hi41lweoptn2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fffnxs5h7hi41lweoptn2.png" alt="part_2_eks_add_node_group_step_1.png" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;→ Click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Configure compute &amp;amp; scaling
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;AMI type: &lt;strong&gt;EKS optimized&lt;/strong&gt; (Amazon Linux / Bottlerocket)&lt;/li&gt;
&lt;li&gt;Capacity type: &lt;strong&gt;On-Demand&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Instance type: &lt;code&gt;t3.medium&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Disk size: 20 GiB&lt;/li&gt;
&lt;li&gt;Scaling:

&lt;ul&gt;
&lt;li&gt;Desired: 2&lt;/li&gt;
&lt;li&gt;Min: 1&lt;/li&gt;
&lt;li&gt;Max: 3&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;→ Keep other settings as default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftisi3i2bmwqaa8mdrldb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftisi3i2bmwqaa8mdrldb.png" alt="part_2_eks_add_node_group_step_2.png" width="800" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Configure networking
&lt;/h4&gt;

&lt;p&gt;Select &lt;strong&gt;only the two private subnets&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is critical: subnet selection here determines where your worker nodes live. Private subnets are ideal for production worker nodes because they don’t expose instances to the Internet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxq0cz5fonl5iyn8rtzk5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxq0cz5fonl5iyn8rtzk5.png" alt="part_2_eks_add_node_group_step_3.png" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;Next&lt;/strong&gt; → &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Wait until the node group status becomes &lt;strong&gt;Active&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8wf3fbu839xqmrmygf8p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8wf3fbu839xqmrmygf8p.png" alt="part_2_eks_node_group_active.png" width="800" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Join the Cluster - AWS handles it
&lt;/h3&gt;

&lt;p&gt;You don’t need to manually configure bootstrap steps for a Managed Node Group - but understanding the join flow is what makes you effective at troubleshooting.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.1 Node join flow
&lt;/h4&gt;

&lt;p&gt;When you create a Managed Node Group, AWS launches EC2 worker nodes. Each instance gets an &lt;strong&gt;Instance Profile&lt;/strong&gt; containing your &lt;strong&gt;Node IAM Role&lt;/strong&gt; (&lt;code&gt;EKSNodeRole&lt;/code&gt;). The join flow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The EC2 instance boots using an EKS-Optimized AMI&lt;/li&gt;
&lt;li&gt;Bootstrap config provides &lt;code&gt;kubelet&lt;/code&gt; with:

&lt;ul&gt;
&lt;li&gt;cluster name&lt;/li&gt;
&lt;li&gt;API endpoint&lt;/li&gt;
&lt;li&gt;cluster CA certificate&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubelet&lt;/code&gt; calls the Kubernetes API Server to register the node&lt;/li&gt;
&lt;li&gt;EKS performs &lt;strong&gt;IAM authentication&lt;/strong&gt; and identifies the IAM Role from the instance profile&lt;/li&gt;
&lt;li&gt;EKS maps IAM identity → Kubernetes identity via &lt;strong&gt;aws-auth / Access Entries&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If mapping is valid (node is in &lt;code&gt;system:nodes&lt;/code&gt;), the node becomes &lt;strong&gt;Ready&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In short: nodes don’t “join by Kubernetes magic.” They join because:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;IAM authentication proves identity + Kubernetes group mapping allows the node role to function.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  3.2 What Managed Node Group eliminates compared to Self-managed
&lt;/h4&gt;

&lt;p&gt;With Self-managed nodes, you must build the join path yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create Launch Template (AMI, instance profile, user data)&lt;/li&gt;
&lt;li&gt;Ensure bootstrap via &lt;code&gt;/etc/eks/bootstrap.sh &amp;lt;cluster-name&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create ASG and subnet placement&lt;/li&gt;
&lt;li&gt;Manually update &lt;code&gt;aws-auth&lt;/code&gt; to map the node role into:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;system:bootstrappers&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;system:nodes&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Managed Node Groups remove most of this plumbing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 — Verify
&lt;/h3&gt;

&lt;p&gt;This is the moment your EKS cluster finally starts to feel alive.&lt;/p&gt;

&lt;h4&gt;
  
  
  4.1 Verify with kubectl
&lt;/h4&gt;

&lt;p&gt;→ Open a terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get nodes &lt;span class="nt"&gt;-o&lt;/span&gt; wide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ Then check system pods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You should see &lt;strong&gt;two nodes&lt;/strong&gt; (based on desired size), in &lt;code&gt;Ready&lt;/code&gt; state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;coredns&lt;/code&gt;, &lt;code&gt;metrics-server&lt;/code&gt;, and &lt;code&gt;kube-proxy&lt;/code&gt; should transition to &lt;code&gt;Running&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgwhce8dpvqhww2p50aqz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgwhce8dpvqhww2p50aqz.png" alt="part_2_verify_kubectl.png" width="800" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  4.2 What AWS resources were created behind the scenes?
&lt;/h4&gt;

&lt;p&gt;Now let’s satisfy curiosity and confirm what AWS created inside your account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(1) Auto Scaling Group&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Open EKS Service &lt;/p&gt;

&lt;p&gt;→ Select EKS cluster &lt;code&gt;demo-eks-cluster&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;Compute&lt;/strong&gt; tab&lt;/p&gt;

&lt;p&gt;→ Select node group &lt;code&gt;eks-mng-general&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe64k3fdjwlq4pvl97zo3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe64k3fdjwlq4pvl97zo3.png" alt="part_2_verify_node_group.png" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;→ In &lt;strong&gt;Details&lt;/strong&gt;, click the &lt;strong&gt;Auto Scaling Group&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8o3hxqrn62muwta1o2mw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8o3hxqrn62muwta1o2mw.png" alt="part_2_verify_asg.png" width="800" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside the ASG page, you’ll find:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Desired / Min / Max configuration&lt;/li&gt;
&lt;li&gt;EC2 instances in &lt;code&gt;InService&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Launch Template reference&lt;/li&gt;
&lt;li&gt;Security Groups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1o7ivyljqg2wwcb4scrt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1o7ivyljqg2wwcb4scrt.png" alt="part_2_verify_asg_detail.png" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(2) Launch Template&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From the ASG page:&lt;/p&gt;

&lt;p&gt;→ Click the &lt;strong&gt;Launch template&lt;/strong&gt; link.&lt;/p&gt;

&lt;p&gt;You’ll see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AMI ID&lt;/li&gt;
&lt;li&gt;Instance type&lt;/li&gt;
&lt;li&gt;Security groups attached&lt;/li&gt;
&lt;li&gt;User data/bootstrap wiring (partly hidden, but it’s there)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu20mcj1umutg04usank5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu20mcj1umutg04usank5.png" alt="part_2_verify_launch_template.png" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(3) Security Group for worker nodes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From the ASG details page&lt;/p&gt;

&lt;p&gt;→ Click the &lt;strong&gt;Security group IDs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Review inbound/outbound rules applied to worker nodes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Pitfalls
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1) Node group is Active, but &lt;code&gt;kubectl get nodes&lt;/code&gt; shows nothing
&lt;/h3&gt;

&lt;p&gt;Likely causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The node group is using the wrong IAM role (not &lt;code&gt;EKSNodeRole&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Node IAM role is missing required policies&lt;/li&gt;
&lt;li&gt;Wrong subnet selection or private subnet route tables are incorrect&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2) Instances keep launching and terminating in the ASG
&lt;/h3&gt;

&lt;p&gt;Likely causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instance type capacity shortage → try a more common type (&lt;code&gt;t3.large&lt;/code&gt;, &lt;code&gt;m5.large&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;Subnet/AZ constraints → expand to more AZs/subnets&lt;/li&gt;
&lt;li&gt;EC2 quota limits → request quota increase&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3) Pods stuck in &lt;code&gt;Pending&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Likely causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Insufficient node resources (CPU/memory) → choose a larger instance type&lt;/li&gt;
&lt;li&gt;Taints/labels preventing scheduling → remove taints or adjust selectors&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4) &lt;code&gt;ImagePullBackOff&lt;/code&gt; / &lt;code&gt;ErrImagePull&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Likely causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Private subnets have no NAT gateway, or routes are wrong&lt;/li&gt;
&lt;li&gt;DNS resolution is broken → check VPC settings (&lt;strong&gt;DNS resolution&lt;/strong&gt; and &lt;strong&gt;DNS hostnames&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In Part 2, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Added production-style worker nodes (private subnets) so workloads finally have somewhere to run&lt;/li&gt;
&lt;li&gt;Clearly separated Cluster Role vs Node Role&lt;/li&gt;
&lt;li&gt;Covered the IAM → Kubernetes authentication story&lt;/li&gt;
&lt;li&gt;Explored what a Managed Node Group creates behind the scenes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, in &lt;strong&gt;Part 3&lt;/strong&gt;, we’ll go deeper into EKS networking: VPC CNI, ENIs, Pod IP allocation, and traffic flow debugging.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>eks</category>
      <category>devops</category>
    </item>
    <item>
      <title>Amazon EKS From The Ground Up - Part 1: Control Plane &amp; Infrastructure</title>
      <dc:creator>Phu Hoang</dc:creator>
      <pubDate>Thu, 25 Dec 2025 10:25:30 +0000</pubDate>
      <link>https://dev.to/posibble/amazon-eks-from-the-ground-up-part-1-control-plane-infrastructure-47pf</link>
      <guid>https://dev.to/posibble/amazon-eks-from-the-ground-up-part-1-control-plane-infrastructure-47pf</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As a DevOps engineer, the ability to provision and manage Kubernetes clusters efficiently is essential. Amazon EKS (Elastic Kubernetes Service) makes this significantly easier—you can create an EKS cluster with just a single command or a few clicks in the AWS Console.&lt;/p&gt;

&lt;p&gt;While you’re sipping your coffee, however, &lt;strong&gt;a lot is happening behind the scenes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This article is the first part of a deep-dive series that breaks down those behind-the-scenes actions. We will examine the components that make up an Amazon EKS cluster, focusing specifically on the &lt;strong&gt;Infrastructure layer&lt;/strong&gt; and &lt;strong&gt;IAM &amp;amp; Security&lt;/strong&gt;, which form the foundation of everything that comes later.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Brief Concept of EKS
&lt;/h2&gt;

&lt;p&gt;Amazon EKS is a managed Kubernetes service that allows you to run Kubernetes without having to operate the Control Plane yourself.&lt;/p&gt;

&lt;p&gt;In simple terms, EKS handles the two most difficult aspects of running Kubernetes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Setting up and managing the Control Plane&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Control Plane is the “brain” of Kubernetes. It consists of the API server, the etcd database (which stores cluster state), the scheduler, and the controller manager. AWS ensures that this Control Plane is highly available, secure, and continuously patched.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Deep integration with AWS services&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;EKS tightly integrates Kubernetes with core AWS services such as VPC networking, IAM-based security, and Elastic Load Balancing.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Building the Foundation – Infrastructure First
&lt;/h2&gt;

&lt;p&gt;A very common misconception among newcomers is the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;EKS = Kubernetes, and AWS handles everything.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In reality, EKS is split into two distinct responsibility domains:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component Partition&lt;/th&gt;
&lt;th&gt;Components&lt;/th&gt;
&lt;th&gt;Management Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS-managed (Control Plane)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;API Server, etcd, Scheduler, Controller Manager&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;AWS&lt;/strong&gt; (not configurable)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Your AWS Account (Data Plane &amp;amp; Networking)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;VPC, Subnets, Security Groups, IAM Roles, EC2 Worker Nodes or Fargate Pods&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;You&lt;/strong&gt; (must be designed and maintained)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For this reason, the remainder of this article focuses on &lt;strong&gt;the infrastructure that you are responsible for&lt;/strong&gt;, before the EKS Control Plane is even created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmoq6sdk57gxjrbuop5dp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmoq6sdk57gxjrbuop5dp.png" alt="part_1_architecture" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 0 – Preparing the Tools
&lt;/h2&gt;

&lt;p&gt;Before creating the EKS cluster, ensure you have the following ready:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An AWS account with access to the AWS Console&lt;/li&gt;
&lt;li&gt;AWS CLI installed and configured&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl&lt;/code&gt; installed&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Although this article is console-first, we will use the CLI to verify and explain what happens under the hood.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1 – Preparing the VPC
&lt;/h2&gt;

&lt;p&gt;The EKS Control Plane itself does &lt;strong&gt;not&lt;/strong&gt; run inside your VPC. However, you still need a VPC for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 worker nodes&lt;/li&gt;
&lt;li&gt;Pod IP address allocation&lt;/li&gt;
&lt;li&gt;Security groups to control traffic&lt;/li&gt;
&lt;li&gt;Internet or NAT access for outbound communication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short: &lt;strong&gt;without a properly designed VPC, EKS cannot function.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1.1. Actions in the AWS Console
&lt;/h3&gt;

&lt;p&gt;→ Open the &lt;strong&gt;Amazon VPC&lt;/strong&gt; service&lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;Create VPC&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Choose &lt;strong&gt;VPC and more&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Recommended Configuration (Practice / Production-Ready Baseline)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eks-demo-vpc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IPv4 CIDR&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10.0.0.0/16&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Availability Zones&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;High availability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public subnets&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Used for NAT Gateways and public load balancers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private subnets&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Where worker nodes will run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NAT Gateway&lt;/td&gt;
&lt;td&gt;1 per AZ&lt;/td&gt;
&lt;td&gt;Required for outbound access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS Hostnames&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Enabled&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Required for nodes to resolve the API server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xwh5t807c2s2zk3ml1p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xwh5t807c2s2zk3ml1p.png" alt="image.png" width="800" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 – Adding Tags to Subnets
&lt;/h2&gt;

&lt;p&gt;Amazon EKS does &lt;strong&gt;not&lt;/strong&gt; automatically infer which subnets it should use. Many EKS features—especially the AWS Load Balancer Controller—depend entirely on &lt;strong&gt;subnet tags&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1. Required Tags
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cluster discovery tag&lt;/strong&gt; (required on &lt;strong&gt;all&lt;/strong&gt; subnets):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubernetes.io/cluster/demo-eks-cluster = shared
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Public load balancer subnets&lt;/strong&gt; (public subnets only):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubernetes.io/role/elb=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Internal load balancer subnets&lt;/strong&gt; (private subnets only):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubernetes.io/role/internal-elb =1
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Incorrect or missing tags are one of the most common causes of load balancer failures in EKS.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2. Actions in the AWS Console
&lt;/h3&gt;

&lt;p&gt;→ Open &lt;strong&gt;VPC → Subnets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Locate subnets named &lt;code&gt;eks-demo-subnet-*&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ Select &lt;code&gt;eks-demo-subnet-public1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ Open the &lt;strong&gt;Tags&lt;/strong&gt; tab → &lt;strong&gt;Manage tags&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fem8reh809q22sledevkr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fem8reh809q22sledevkr.png" alt="image.png" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;→ Add tags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kubernetes.io/cluster/demo-eks-cluster = shared&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kubernetes.io/role/elb = 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;→ Save&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldrizcuxko86xl9yguug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldrizcuxko86xl9yguug.png" alt="image.png" width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repeat for &lt;code&gt;eks-demo-subnet-public2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv7pr5x4430cshv3ayn2l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv7pr5x4430cshv3ayn2l.png" alt="image.png" width="800" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For each private subnets, add tags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kubernetes.io/cluster/demo-eks-cluster = shared&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kubernetes.io/role/internal-elb = 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ge22vljm6npfaaj5ru4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ge22vljm6npfaaj5ru4.png" alt="image.png" width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbb7qvrpeypa2qt5p6g1r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbb7qvrpeypa2qt5p6g1r.png" alt="image.png" width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 - Add a dedicated Security Group for the EKS Cluster
&lt;/h2&gt;

&lt;p&gt;In this step, we’ll create a &lt;strong&gt;separate Security Group&lt;/strong&gt; for the EKS cluster. The goal is to establish a clean security boundary early (even if you’re still in the “control plane only” stage).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inbound:&lt;/strong&gt; allow internal traffic within the same Security Group so EKS-related components that share this SG can communicate with each other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outbound:&lt;/strong&gt; allow outbound traffic so the cluster and its components can reach required endpoints (e.g., AWS APIs, image registries, etc.).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: This is a learning/bootstrapping-friendly baseline. In production, you typically tighten inbound/outbound rules based on actual traffic requirements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.1. Actions in the AWS Console
&lt;/h3&gt;

&lt;p&gt;→ Open &lt;strong&gt;VPC&lt;/strong&gt; service&lt;/p&gt;

&lt;p&gt;→ Go to &lt;strong&gt;Security Groups&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Configure the Security Group&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security group name:&lt;/strong&gt; &lt;code&gt;eks-cluster-sg&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPC:&lt;/strong&gt; &lt;code&gt;eks-demo-vpc&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;→  Add (or keep) this outbound rule:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Port range&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;All traffic&lt;/td&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fno8hk6ia1raa029xs6ef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fno8hk6ia1raa029xs6ef.png" alt="image.png" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;→ Then click &lt;strong&gt;Create security group&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now we’ll allow “intra-SG” traffic:&lt;/p&gt;

&lt;p&gt;→ Select the Security Group &lt;strong&gt;&lt;code&gt;eks-cluster-sg&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Open the &lt;strong&gt;Inbound rules&lt;/strong&gt; tab → click &lt;strong&gt;Edit inbound rules&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Add a new rule:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Port range&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;All traffic&lt;/td&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;Custom → &lt;strong&gt;Security group&lt;/strong&gt;: &lt;code&gt;eks-cluster-sg&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;→ Save the rule.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe41jkdfnu9qucb4m9oph.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe41jkdfnu9qucb4m9oph.png" alt=" " width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 – Creating the IAM Role for the EKS Control Plane
&lt;/h2&gt;

&lt;p&gt;Although AWS manages the Control Plane, it still needs permissions to interact with resources in &lt;strong&gt;your AWS account&lt;/strong&gt;, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating and managing ENIs&lt;/li&gt;
&lt;li&gt;Attaching security groups&lt;/li&gt;
&lt;li&gt;Communicating with VPC resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is achieved through an &lt;strong&gt;IAM Role&lt;/strong&gt; that the EKS service assumes.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1. Actions in the AWS Console
&lt;/h3&gt;

&lt;p&gt;→ Open &lt;strong&gt;IAM → Roles&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;Create role&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6vib2v17i6kl7t79h7dp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6vib2v17i6kl7t79h7dp.png" alt="image.png" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;→ Trusted entity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type: &lt;strong&gt;AWS service&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Service: &lt;strong&gt;EKS&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Use case: &lt;strong&gt;EKS – Cluster&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmkxgzwo9gcj3irhwrz8l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmkxgzwo9gcj3irhwrz8l.png" alt="image.png" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;→ Continue to permissions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AmazonEKSClusterPolicy&lt;/code&gt; is attached automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1fcc6y77da2mhypo8l5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1fcc6y77da2mhypo8l5u.png" alt="image.png" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;→ Name the role: &lt;code&gt;EKSClusterRole&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ Create the role&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxx9jfc34iqzkx8s6ix25.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxx9jfc34iqzkx8s6ix25.png" alt="image.png" width="800" height="777"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2. Trust Policy Explanation
&lt;/h3&gt;

&lt;p&gt;The role’s trust policy contains the following statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"eks.amazonaws.com"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Allow the EKS service to assume this role and call AWS APIs using the permissions defined in &lt;code&gt;AmazonEKSClusterPolicy&lt;/code&gt;.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Granting only this policy follows the &lt;strong&gt;Principle of Least Privilege&lt;/strong&gt; and is sufficient for the Control Plane to operate correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 – Creating the EKS Cluster
&lt;/h2&gt;

&lt;p&gt;With the infrastructure and IAM role in place, we can now create the Control Plane.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1. Actions in the AWS Console
&lt;/h3&gt;

&lt;p&gt;→ Open &lt;strong&gt;Amazon EKS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;Create cluster&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Choose &lt;strong&gt;Custom configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Disable &lt;strong&gt;EKS Auto Mode&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In Cluster Configuration step:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Name: &lt;code&gt;demo-eks-cluster&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ IAM role: &lt;code&gt;EKSClusterRole&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ Kubernetes version: &lt;strong&gt;1.34&lt;/strong&gt; (latest stable at the time of writing)&lt;/p&gt;

&lt;p&gt;→ Leave other settings as default&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsv822piuad9q9gvug45s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsv822piuad9q9gvug45s.png" alt="image.png" width="800" height="770"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In Networking Configuration step:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ VPC: &lt;code&gt;eks-demo-vpc&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ Subnets: &lt;strong&gt;select all 4 subnets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Security group: &lt;code&gt;eks-cluster-sg&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ Endpoint access: &lt;strong&gt;Public and Private&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Public + Private access allows management from your local machine while ensuring worker nodes communicate internally within the VPC.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpca0ju29s7wyacazqve7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpca0ju29s7wyacazqve7.png" alt="image.png" width="800" height="885"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In Logging Configuration step:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Enable the following logs (recommended):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API server&lt;/li&gt;
&lt;li&gt;Audit&lt;/li&gt;
&lt;li&gt;Authenticator&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frq5j0e5cy64evi4bjp1b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frq5j0e5cy64evi4bjp1b.png" alt="image.png" width="800" height="716"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In Add-ons Configuration step:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Keep the AWS-recommended defaults:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;kube-proxy&lt;/li&gt;
&lt;li&gt;Amazon VPC CNI&lt;/li&gt;
&lt;li&gt;Node monitoring agent&lt;/li&gt;
&lt;li&gt;CoreDNS&lt;/li&gt;
&lt;li&gt;Amazon EKS Pod Identity Agent&lt;/li&gt;
&lt;li&gt;External DNS&lt;/li&gt;
&lt;li&gt;Metrics Server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;→ Click &lt;strong&gt;Create&lt;/strong&gt; and wait for the cluster status to become &lt;strong&gt;Healthy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylqf3k88a8ce0dgiylzd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylqf3k88a8ce0dgiylzd.png" alt="image.png" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Behind the scenes, this is equivalent to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws eks create-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; demo-eks-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-arn&lt;/span&gt; arn:aws:iam::&amp;lt;account-id&amp;gt;:role/EKSClusterRole &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resources-vpc-config&lt;/span&gt; &lt;span class="nv"&gt;subnetIds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;subnet-public1&amp;gt;,&amp;lt;subnet-public2&amp;gt;,&amp;lt;subnet-private1&amp;gt;,&amp;lt;subnet-private2&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which do you prefer AWS Console or AWS CLI? 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6 – Connecting and Verifying the Cluster
&lt;/h2&gt;

&lt;p&gt;Once the cluster is &lt;strong&gt;ACTIVE&lt;/strong&gt;, connect to it from your local machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.1 Fetching the kubeconfig
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws eks update-kubeconfig &lt;span class="nt"&gt;--name&lt;/span&gt; demo-eks-cluster &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retrieves the API endpoint&lt;/li&gt;
&lt;li&gt;Updates &lt;code&gt;~/.kube/config&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Configures authentication using your current IAM identity&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  6.2 Verifying the Cluster
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl cluster-info
kubectl get namespaces
kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Expected Results
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cluster-info&lt;/code&gt;: API server is reachable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get namespaces&lt;/code&gt;: default namespaces are listed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get nodes&lt;/code&gt;: &lt;strong&gt;no nodes returned&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is expected.&lt;/p&gt;

&lt;p&gt;The Control Plane is ready, but &lt;strong&gt;no worker nodes exist yet&lt;/strong&gt;, so Kubernetes has no compute capacity to schedule Pods. Core system Pods such as CoreDNS remain in the &lt;code&gt;Pending&lt;/code&gt; state.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cd3qvkbxgcrgirl1769.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cd3qvkbxgcrgirl1769.png" alt="image.png" width="800" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this first part of the series, we built the foundation of an Amazon EKS cluster from the ground up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Designed a production-ready VPC with properly tagged subnets&lt;/li&gt;
&lt;li&gt;Created an IAM role for the EKS Control Plane&lt;/li&gt;
&lt;li&gt;Provisioned the EKS Control Plane with secure endpoint access&lt;/li&gt;
&lt;li&gt;Verified connectivity while intentionally stopping before worker nodes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, we have built the &lt;strong&gt;brain&lt;/strong&gt; of Kubernetes.&lt;/p&gt;

&lt;p&gt;It is powered on, networked, and authorized—but it is still waiting for its “hands”.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>aws</category>
      <category>eks</category>
    </item>
  </channel>
</rss>
