Right, let’s talk about the brain of your EKS cluster: the control plane. When you hear “managed,” your brain might conjure images of AWS handling all the tedious bits while you kick back. And for the most part, that’s true. But “managed” doesn’t mean “magic.” It means “we run the fiddly bits you probably don’t want to, and you still need to know how they work so you don’t accidentally set the whole thing on fire.”

AWS runs the Kubernetes API servers and the etcd cluster for you. This is a godsend. etcd is the single source of truth for your entire cluster—all your pod definitions, secrets, configs, everything. It’s a notoriously finicky beast that demands low latency, consistent I/O, and a team of on-call engineers with a strong preference for dark roast coffee. AWS handles its replication, backups, and failure recovery. You get to stop worrying about etcd members going down and whether your --initial-cluster-state flag is correct. This alone is worth the price of admission.

The API Server: Your Front Door

Everything you do—every kubectl get pod, every kubectl apply -f—hits the API server first. It’s the grand central station for all cluster communication. Because AWS manages it, they handle the scaling, the patching, and the high availability. You get a highly available endpoint spread across multiple Availability Zones in your chosen region. This is fantastic for resilience, but it introduces a crucial concept: the Endpoint Access setting.

You have three choices here, and picking the wrong one is a classic “why can’t I talk to my cluster?!” moment.

  • PUBLIC: The API server gets a public endpoint. Your kubectl commands talk to it over the internet. This is, to be frank, a bit yolo. It’s convenient for testing but makes my security spidey-sense tingle.
  • PRIVATE: The API server endpoint is only accessible from within your VPC. This is the most secure and sensible choice for production. It means your kubectl commands must originate from inside the VPC (e.g., from a bastion host, a VPN connection, or AWS Direct Connect).
  • PUBLIC_AND_PRIVATE: The best of both worlds, or the worst, depending on your security posture. It gives you both endpoints. You can use the public one for convenience from your laptop and the private one for internal automation, all while locking down the public endpoint with CIDR blocks.

You set this during cluster creation. If you mess it up, you can update it later, but it’s easier to get it right the first time.

# Creating a cluster with a private endpoint (the sane default for prod)
eksctl create cluster \
    --name my-prod-cluster \
    --region us-west-2 \
    --endpoint-private-access \
    --endpoint-public-access false
# ... other flags

The Security Dance: IAM and Kubernetes RBAC

Here’s where the AWS and Kubernetes worlds collide in a way that’s both brilliant and, initially, a bit confusing. You can’t just give someone a kubeconfig file and call it a day. The EKS API server is configured to use IAM for authentication. Not usernames and passwords. IAM.

When you run kubectl, it uses the aws-iam-authenticator tool (or the built-in logic in the AWS CLI v2) to make a signed request to AWS STS (GetCallerIdentity). The API server sees this request, verifies the signature against AWS’s public keys, and says, “Yep, this is indeed the IAM user/role arn:aws:sts::123456789012:assumed-role/MyAdminRole/abc123.”

But that’s only half the battle. Authentication (who you are) is handled by AWS IAM. Authorization (what you’re allowed to do) is still handled by Kubernetes RBAC. The API server now takes that confirmed IAM ARN and checks for a Kubernetes RoleBinding or ClusterRoleBinding that maps that ARN to a Kubernetes Role or ClusterRole.

If you don’t have that mapping, you’ll get a brutally unhelpful Error from server (Forbidden): pods is forbidden: User "arn:aws:iam::123456789012:role/MyAdminRole" cannot list resource "pods" in API group "" at the cluster scope. It looks like an IAM error, but it’s actually a Kubernetes RBAC error. This trips up everyone.

So, your first order of business after creating a cluster is to map your IAM entity to a Kubernetes RBAC role. Here’s how you give your own IAM user full admin rights (be careful with this one):

# map-admin.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: my-user-admin-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: arn:aws:iam::123456789012:user/MyAwesomeUsername
kubectl apply -f map-admin.yaml

The Price of Admission

Let’s talk money. The EKS control plane isn’t free. It costs ~$0.10/hour per cluster ($72/month) as of this writing. This catches some people off guard. You’re paying for them to manage, patch, scale, and back up the API servers and etcd. For most teams, this is an incredible bargain compared to the engineering time required to run it yourself. But if you’re just kicking the tires, remember to eksctl delete cluster when you’re done, or that ten cents will keep ticking away, laughing at you.

The Rough Edge: Control Plane Logging

By default, your managed API server doesn’t log a damn thing to CloudWatch. It’s the most common “wait, how do I debug that?” moment. You have to explicitly enable audit logs, which are a verbose, JSON-laden firehose of every single request made to the API server. It’s essential for security auditing and debugging weird issues, but it will absolutely fill up your CloudWatch logs and your wallet if you’re not careful. Enable it, but have a lifecycle policy ready to archive or delete those logs after a sensible retention period. You can turn it on via the console, CLI, or in your eksctl config.