{"id":32704,"date":"2023-02-06T15:36:27","date_gmt":"2023-02-06T15:36:27","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=32704"},"modified":"2026-02-21T07:23:27","modified_gmt":"2026-02-21T07:23:27","slug":"how-to-create-eks-cluster-on-aws","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/how-to-create-eks-cluster-on-aws\/","title":{"rendered":"How to Create EKS Cluster on AWS"},"content":{"rendered":"\n<p>Here\u2019s a clean, <strong>console-only<\/strong> workflow to spin up a <strong>current<\/strong> Amazon EKS cluster (Kubernetes <strong>1.33<\/strong> as of today) and verify it end-to-end. I\u2019ll also point out the 2026-specific gotchas (AL2 deprecation, default add-ons, Pod Identity). Citations sit right after each section.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">0) Prereqs (one-time)<\/h1>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Permissions:<\/strong> You\u2019ll need IAM rights to create EKS clusters, roles, VPC resources, and EC2 node groups.<\/li>\n\n\n\n<li><strong>Network:<\/strong> Have a VPC with at least <strong>2 subnets in different AZs<\/strong> (3 private + 3 public is the common pattern). EKS requires subnets in \u22652 AZs and recommends using <strong>private<\/strong> subnets for nodes. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/network-reqs.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/li>\n\n\n\n<li><strong>Kubernetes version:<\/strong> EKS supports up to <strong>v1.33<\/strong> now (note: v1.32 was the last to support AL2). From <strong>1.33 onward use AL2023 or Bottlerocket<\/strong> for node AMIs. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/platform-versions.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">1) Create the two IAM roles (cluster + node) in the <strong>IAM console<\/strong><\/h1>\n\n\n\n<p><strong>A. Cluster role (eksClusterRole)<\/strong><br>IAM \u2192 Roles \u2192 <strong>Create role<\/strong> \u2192 <em>AWS Service<\/em> \u2192 <strong>EKS \u2192 EKS \u2013 Cluster<\/strong> \u2192 Next \u2192 name it <code>eksClusterRole<\/code> \u2192 <strong>Create<\/strong>. This attaches <strong>AmazonEKSClusterPolicy<\/strong> and sets the correct trust policy for <code>eks.amazonaws.com<\/code>. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/cluster-iam-role.html\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/p>\n\n\n\n<p><strong>B. Node role (AmazonEKSNodeRole)<\/strong><br>IAM \u2192 Roles \u2192 <strong>Create role<\/strong> \u2192 <em>AWS Service<\/em> \u2192 <strong>EC2<\/strong> \u2192 Next \u2192 attach:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>AmazonEKSWorkerNodePolicy<\/strong><\/li>\n\n\n\n<li><strong>AmazonEC2ContainerRegistryPullOnly<\/strong> (newer name; replaces old ReadOnly in many guides)<\/li>\n<\/ul>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Don\u2019t attach <code>AmazonEKS_CNI_Policy<\/code> here unless you\u2019re intentionally not using IRSA\/Pod Identity for the VPC CNI. AWS recommends giving the CNI its <strong>own role<\/strong> via IRSA\/Pod Identity.<br>Name it <code>AmazonEKSNodeRole<\/code> \u2192 <strong>Create<\/strong>. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/create-node-role.html\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">2) Create the <strong>cluster<\/strong> in the <strong>EKS console<\/strong><\/h1>\n\n\n\n<p>EKS console \u2192 <strong>Clusters<\/strong> \u2192 <strong>Create cluster<\/strong>:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Name:<\/strong> e.g., <code>prod-eks-133<\/code><\/li>\n\n\n\n<li><strong>Kubernetes version:<\/strong> <strong>1.33<\/strong> (default\/recommended). (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/platform-versions.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/li>\n\n\n\n<li><strong>Cluster service role:<\/strong> <strong>eksClusterRole<\/strong> (from step 1A). (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/cluster-iam-role.html\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/li>\n\n\n\n<li><strong>Secrets encryption (recommended):<\/strong> pick a <strong>KMS key<\/strong> for etcd secrets encryption.<\/li>\n\n\n\n<li><strong>Networking:<\/strong> choose your <strong>VPC<\/strong> and select at least <strong>2 private subnets<\/strong> (add public subnets if you plan internet-facing LBs). EKS requires \u22656 free IPs per subnet (16+ recommended). (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/network-reqs.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/li>\n\n\n\n<li><strong>Cluster endpoint access:<\/strong> \u201cPublic and private\u201d (restrict public CIDRs) or \u201cPrivate only\u201d if you have reachability.<\/li>\n\n\n\n<li><strong>Control plane logs (recommended):<\/strong> enable <strong>api, audit, authenticator, controllerManager, scheduler<\/strong>.<\/li>\n\n\n\n<li><strong>Add-ons:<\/strong> When you create via console, EKS auto-adds <strong>VPC CNI, CoreDNS, kube-proxy as EKS add-ons<\/strong> (managed). You can adjust versions post-create. <strong>EBS CSI driver<\/strong> is optional here; you can add it later. <strong>Create<\/strong>. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/eks-add-ons.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/li>\n<\/ol>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Provisioning the control plane takes a few minutes. The doc\u2019s \u201cCreate a cluster\u201d page captures the flow and AZ capacity note. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/create-cluster.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">3) Add <strong>compute<\/strong>: Managed node group (console)<\/h1>\n\n\n\n<p>Cluster \u2192 <strong>Compute<\/strong> tab \u2192 <strong>Add node group<\/strong>:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Name:<\/strong> <code>mng-al2023<\/code><\/li>\n\n\n\n<li><strong>Node IAM role:<\/strong> <strong>AmazonEKSNodeRole<\/strong> (from step 1B). (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/create-node-role.html\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/li>\n\n\n\n<li><strong>AMI family:<\/strong> <strong>Amazon Linux 2023<\/strong> <em>(or Bottlerocket)<\/em> \u2014 <strong>do not use AL2<\/strong> on 1.33. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/kubernetes-versions-standard.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/li>\n\n\n\n<li><strong>Instance type:<\/strong> start with <code>t3.large<\/code> (adjust to your workloads).<\/li>\n\n\n\n<li><strong>Size:<\/strong> min\/desired\/max (e.g., 2\/3\/6).<\/li>\n\n\n\n<li><strong>Subnets:<\/strong> select the <strong>private subnets<\/strong>.<\/li>\n\n\n\n<li><strong>Remote access:<\/strong> optional; leave off unless you need SSH.<\/li>\n\n\n\n<li><strong>Create<\/strong> and wait for nodes to register.<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">4) Verify <strong>add-ons<\/strong> (console)<\/h1>\n\n\n\n<p>Cluster \u2192 <strong>Add-ons<\/strong> tab:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Confirm <strong>Amazon VPC CNI<\/strong>, <strong>kube-proxy<\/strong>, and <strong>CoreDNS<\/strong> are <strong>Installed<\/strong>.<\/li>\n\n\n\n<li>Click each and select the <strong>Recommended<\/strong> version if an update is suggested.<\/li>\n\n\n\n<li>(Optional but strongly recommended) <strong>Install EBS CSI driver<\/strong> add-on for dynamic PersistentVolumes. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/eks-add-ons.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/li>\n<\/ul>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>If you\u2019ll publish Services publicly <strong>via Ingress\/ALB<\/strong>, plan to install the <strong>AWS Load Balancer Controller<\/strong> (Helm) and ensure subnets are tagged (<code>kubernetes.io\/role\/elb=1<\/code> for public, <code>\u2026\/internal-elb=1<\/code> for private). This part is not built-in to EKS by default. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/lbc-helm.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">5) (Modern auth) <strong>EKS Pod Identity<\/strong> (optional but preferred over classic IRSA)<\/h1>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Enable the <strong>EKS Pod Identity Agent<\/strong> add-on (Cluster \u2192 Add-ons \u2192 <em>Get more add-ons<\/em> \u2192 <strong>EKS Pod Identity Agent<\/strong> \u2192 Install).<\/li>\n\n\n\n<li>Then create <strong>Pod Identity associations<\/strong> to map a service account to an IAM role per workload. This is the current recommended path for pods to access AWS APIs, and it simplifies policy reuse. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/pod-id-agent-setup.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">6) \u201c<strong>Check<\/strong>\u201d the cluster (fastest via <strong>AWS CloudShell<\/strong> from the console)<\/h1>\n\n\n\n<p>From the AWS console header, open <strong>CloudShell<\/strong> in the same Region:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\"># 1) Merge kubeconfig for your cluster<\/span>\naws eks update-kubeconfig --region &lt;REGION&gt; --name &lt;CLUSTER_NAME&gt;\n\n<span class=\"hljs-comment\"># 2) See nodes &amp; system pods<\/span>\nkubectl get nodes -o wide\nkubectl get pods -A\n\n<span class=\"hljs-comment\"># 3) Smoke test: run nginx and expose it<\/span>\nkubectl create deploy hello-nginx --image=nginx\nkubectl expose deploy hello-nginx --port=<span class=\"hljs-number\">80<\/span> --type=ClusterIP\nkubectl port-forward deploy\/hello-nginx <span class=\"hljs-number\">8080<\/span>:<span class=\"hljs-number\">80<\/span>\n<span class=\"hljs-comment\"># Then in CloudShell \"Preview\" or curl http:\/\/127.0.0.1:8080<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The <code>update-kubeconfig<\/code> step and kubectl connectivity are the official flow. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/create-kubeconfig.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/p>\n\n\n\n<p><strong>Optional (Service LoadBalancer test):<\/strong><br>If you didn\u2019t install the AWS Load Balancer Controller and just want an external IP fast, you can <code>kubectl expose<\/code> with <code>type=LoadBalancer<\/code> (this provisions an AWS load balancer via the legacy provider). For production <strong>Ingress\/ALB<\/strong>, install the <strong>AWS Load Balancer Controller<\/strong> and use <code>Ingress<\/code> resources. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/aws-load-balancer-controller.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">2026 tips &amp; gotchas (read this!)<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Kubernetes 1.33:<\/strong> pick AL2023 or Bottlerocket for nodes (AL2 is <strong>not<\/strong> released for 1.33). (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/kubernetes-versions-standard.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/li>\n\n\n\n<li><strong>Default add-ons:<\/strong> when created via <strong>console<\/strong>, VPC CNI + CoreDNS + kube-proxy are installed as <strong>EKS add-ons<\/strong> (managed). Keep them on <strong>Recommended<\/strong> versions. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/eks-add-ons.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/li>\n\n\n\n<li><strong>Subnet tags for ALB:<\/strong> If\/when you install the AWS Load Balancer Controller, tag subnets (<code>kubernetes.io\/role\/elb=1<\/code> for public, <code>\u2026\/internal-elb=1<\/code> for private) or specify subnets on the Ingress annotation. (<a href=\"https:\/\/kubernetes-sigs.github.io\/aws-load-balancer-controller\/v2.6\/deploy\/subnet_discovery\/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">kubernetes-sigs.github.io<\/a>)<\/li>\n\n\n\n<li><strong>Pod to AWS access:<\/strong> Prefer <strong>EKS Pod Identity<\/strong> over older IRSA flow for new clusters; install the <strong>Pod Identity Agent<\/strong> and create <strong>associations<\/strong> per service account. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/pod-identities.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/li>\n\n\n\n<li><strong>Node role policies:<\/strong> Use <strong>AmazonEKSWorkerNodePolicy<\/strong> + <strong>AmazonEC2ContainerRegistryPullOnly<\/strong>; attach <strong>CNI policy<\/strong> via the CNI\u2019s own service account role (IRSA\/Pod Identity), not to the node role, unless you intentionally choose otherwise. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/create-node-role.html\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">Clean up (to avoid costs)<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Delete test resources: <code>kubectl delete deploy\/hello-nginx svc\/hello-nginx<\/code>.<\/li>\n\n\n\n<li><strong>Compute tab \u2192<\/strong> delete <strong>Node group(s)<\/strong>.<\/li>\n\n\n\n<li><strong>Cluster \u2192 Delete<\/strong> (after node groups are gone).<br>General flow and constraints are in the create\/cleanup docs. (<a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/create-cluster.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">AWS Documentation<\/a>)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here\u2019s a clean, console-only workflow to spin up a current Amazon EKS cluster (Kubernetes 1.33 as of today) and verify it end-to-end. I\u2019ll also point out the 2026-specific gotchas (AL2 deprecation, default add-ons, Pod Identity). Citations sit right after each section. 0) Prereqs (one-time) 1) Create the two IAM roles (cluster + node) in the&#8230;<\/p>\n","protected":false},"author":48,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","_joinchat":[],"footnotes":""},"categories":[2],"tags":[],"class_list":["post-32704","post","type-post","status-publish","format-standard","hentry","category-uncategorised"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/32704","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/48"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=32704"}],"version-history":[{"count":4,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/32704\/revisions"}],"predecessor-version":[{"id":58817,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/32704\/revisions\/58817"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=32704"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=32704"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=32704"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}