Deploy a Multi-Container App on EKS

Deploy a two-tier app on Amazon EKS with Kubernetes manifests and an ALB.

Introduction

30 Second Summary

Every app you use daily, from streaming services to food delivery platforms, runs as dozens of smaller services working together behind the scenes. Orchestrating those services so they stay connected, scale on demand, and recover from failures is one of the core challenges of modern software.

In this project, you will deploy a multi-container application on Amazon EKS using Kubernetes. You'll write manifests from scratch, connect services using DNS-based discovery, and expose your app to the internet through an AWS Application Load Balancer.

What You'll Build

A two-tier web application running on Amazon EKS with a backend API service and a frontend web server, connected through Kubernetes Services and exposed to the internet via an ALB Ingress resource.

By the end of this project, you'll have:

  • An EKS Auto Mode cluster running in your AWS account with automated node management and built-in load balancing.
  • A backend Deployment with multiple replicas and internal service discovery via a ClusterIP Service.
  • A frontend Deployment exposed to the internet through an IngressClass and Ingress resource that provisions an AWS Application Load Balancer.
  • Secret Mission: Scale your backend with a Horizontal Pod Autoscaler and load-test it to observe automatic scaling under pressure.

Are there any prerequisites?

This project assumes you already have an AWS account with the AWS CLI configured, eksctl installed (version 0.195.0 or greater), and kubectl installed. You should also have Docker available on your machine and be working from a Linux device.

Before We Start

In this project, you will deploy a two-tier web application on Amazon EKS. The app has two parts: a backend API service and a frontend web server.

Instead of running both containers on a single server, you will use Kubernetes to orchestrate them. Kubernetes handles scheduling containers across multiple machines, restarting them if they crash, and routing traffic between services automatically.

In this step, get ready to:
  • Confirm your AWS CLI credentials are configured.
  • Verify that eksctl is version 0.195.0 or greater.
  • Verify that kubectl is installed.
Verify your AWS CLI credentials

The AWS CLI needs valid credentials so that eksctl can provision cloud resources in your account.

  • Confirm your AWS credentials are configured by running this command:
aws sts get-caller-identity

✔️ I see my account info

You should see a JSON response containing your UserId, Account, and Arn. This confirms your CLI can authenticate with AWS.

ⓧ I see an error

If you see an error about credentials not being found or expired, you need to configure your AWS CLI.

  • Run the following command and enter your Access Key ID, Secret Access Key, default region, and output format when prompted:
aws configure
  • After configuring, re-run the aws sts get-caller-identity command to confirm it works.
Verify your eksctl version

EKS Auto Mode requires eksctl version 0.195.0 or greater. Older versions do not support the Auto Mode flag.

  • Check your eksctl version by running:
eksctl version

✔️ I see version 0.195.0 or higher

Your version number is at or above 0.195.0. You are good to proceed.

ⓧ I see an older version

You need to upgrade eksctl to at least 0.195.0. Download and install the latest release from the official GitHub releases.

  • Upgrade eksctl by running these commands:
# Detect your system architecture
ARCH=$(uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/')
PLATFORM=$(uname -s)_$ARCH

# Download the latest release
curl -sLO "https://github.com/eksctl-io/eksctl/releases/latest/download/eksctl_$PLATFORM.tar.gz"

# Extract and install
tar -xzf eksctl_$PLATFORM.tar.gz -C /tmp && rm eksctl_$PLATFORM.tar.gz
sudo install -m 0755 /tmp/eksctl /usr/local/bin && rm /tmp/eksctl
  • After installing, re-run eksctl version to confirm the output shows 0.195.0 or higher.

ⓧ Command not found

eksctl is not installed on your system. Install it from the official GitHub releases.

  • Install eksctl by running these commands:
# Detect your system architecture
ARCH=$(uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/')
PLATFORM=$(uname -s)_$ARCH

# Download the latest release
curl -sLO "https://github.com/eksctl-io/eksctl/releases/latest/download/eksctl_$PLATFORM.tar.gz"

# Extract and install
tar -xzf eksctl_$PLATFORM.tar.gz -C /tmp && rm eksctl_$PLATFORM.tar.gz
sudo install -m 0755 /tmp/eksctl /usr/local/bin && rm /tmp/eksctl
  • After installing, run eksctl version to confirm the output shows 0.195.0 or higher.

What is eksctl?

eksctl is a command-line tool that automates creating and managing Amazon EKS clusters. It handles the VPC, subnets, IAM roles, and node configuration that you would otherwise set up manually across multiple AWS services.

Verify kubectl is installed

kubectl is the CLI you use to send commands to your Kubernetes cluster. You will use it throughout this project to apply manifests and inspect resources.

  • Check that kubectl is installed by running:
kubectl version --client

✔️ I see a version number

kubectl is installed and ready. You should see a Client Version line in the output.

ⓧ Command not found

kubectl is not installed. Follow the official Kubernetes installation guide to install it for Linux, then re-run the version command.

Why Kubernetes over a single server?

Running two containers on a single server works for development. In production, you need automatic restarts if a container crashes, load balancing across replicas, and the ability to scale each service independently.

Kubernetes provides all of this through declarative manifests. You describe the desired state and Kubernetes makes it happen. EKS Auto Mode takes this further by managing the underlying nodes for you.

All three tools are confirmed working. Next up, you will create your EKS Auto Mode cluster and start deploying containers.

Create Your EKS Auto Mode Cluster

Your multi-container application needs a place to run. Amazon EKS provides a managed Kubernetes control plane, so you can focus on deploying containers instead of managing infrastructure.

In this step, you'll create an EKS Auto Mode cluster using a single command. Auto Mode handles node provisioning, load balancing, and scaling automatically. All you provide is one eksctl command, and AWS takes care of the rest.

In this step, get ready to:
  • Verify your AWS CLI, eksctl, and kubectl are ready.
  • Create an EKS Auto Mode cluster with eksctl.
  • Confirm the cluster is running and kubectl can communicate with it.
Verify your tools are ready

Before creating the cluster, confirm that your three prerequisite tools respond correctly.

  • Verify your AWS credentials are configured by running this command:
aws sts get-caller-identity
  • You should see a JSON response with your Account, UserId, and Arn fields. This confirms your AWS CLI can authenticate.
  • Confirm eksctl is version 0.195.0 or greater by running:
eksctl version
  • You should see a version number like 0.227.0 or higher.
  • Confirm kubectl is installed by running:
kubectl version --client
  • You should see client version information printed in the terminal.

Command not found?

If any of the three commands returns an error, the tool is not installed or not on your PATH. Revisit the tool's official installation guide before continuing.

If aws sts get-caller-identity returns an authentication error, run aws configure and re-enter your access key and secret key.

Help me troubleshoot my AWS CLI or eksctl setup.

Create your EKS Auto Mode cluster

With your tools confirmed, you are ready to create the cluster. This single command provisions the control plane, VPC, subnets, IAM roles, and node pools automatically.

  • Create your EKS Auto Mode cluster by running this command:
eksctl create cluster --name=eks-multi-app --enable-auto-mode

What does this command do?

  • eksctl create cluster tells eksctl to provision a new EKS cluster along with its supporting AWS infrastructure.
  • --name=eks-multi-app sets your cluster name. You will use this name in all future commands.
  • --enable-auto-mode tells EKS to manage node provisioning, scaling, and the AWS Load Balancer Controller for you. You will not need to set up node groups or install add-ons manually.
  • Behind the scenes, eksctl creates CloudFormation stacks that provision the VPC, subnets, IAM roles, and the EKS control plane.

Why EKS Auto Mode instead of managed node groups?

EKS Auto Mode automates node provisioning, load balancing, and scaling as built-in capabilities. This means you spend your time learning Kubernetes concepts (Deployments, Services, Ingress) instead of configuring AWS infrastructure.

With managed node groups, you would need to manually install the AWS Load Balancer Controller, configure node scaling policies, and manage node group updates. Auto Mode handles all of this for you.

  • Wait for the cluster to finish creating. This takes approximately 15 minutes.
  • You will see progress messages as eksctl creates CloudFormation stacks. The final line looks similar to this:
[✓]  EKS cluster "eks-multi-app" in "region-code" region is ready

What should I do while waiting?

The 15-minute wait is a good time to read ahead. In the next step, you will write Kubernetes Deployment and Service manifests for your backend application.

Cluster creation failed?

If you see a permissions error, your IAM user or role may lack the required policies. You need permissions for EC2, EKS, IAM, and CloudFormation.

If you see a resource limit error, your account may have hit VPC or Elastic IP quotas in the current region. Try a different region by adding --region=us-west-2 to the command.

If the command hangs beyond 20 minutes, press Ctrl+C and check the AWS CloudFormation console for the stack status.

Help me debug my EKS cluster creation failure.

Verify the cluster is running

Once eksctl reports the cluster is ready, it automatically configures your kubectl context. Confirm the cluster is accessible by listing system pods.

  • List the system pods running in your cluster by running:
kubectl get pods -n kube-system
  • You should see Kubernetes Metrics Server pods with a Running status. In a new EKS Auto Mode cluster, these are the only pods running in kube-system. Other components like the load balancer controller run off-cluster and are managed by AWS.

Where are the nodes?

If you run kubectl get nodes right now, you may see no nodes listed. This is expected with EKS Auto Mode.

Auto Mode provisions nodes on-demand only when workloads need them. Once you deploy your backend in the next step, nodes will appear automatically.

kubectl cannot connect to the cluster?

If you see a connection refused or timeout error, your kubeconfig may not be set correctly. Manually update it by running:

aws eks update-kubeconfig --name=eks-multi-app

If you still see errors, confirm your AWS CLI identity matches the one that created the cluster by running aws sts get-caller-identity.

Help me fix my kubectl connection to EKS.

Cost reminder

Your EKS cluster is now incurring charges at $0.10 per hour for the control plane. Complete the project and delete the cluster in the Before You Go section to stop charges.

Your cluster is live and kubectl is connected. Next, you will write Kubernetes manifests to deploy your backend service onto this cluster.

Deploy the Backend Service

Your EKS cluster is up and running. Now it's time to deploy your first workload.

Kubernetes uses manifest files to describe the desired state of your application. In this step, you'll write two manifests: a Deployment that runs your backend containers, and a Service that makes them reachable inside the cluster.

In this step, get ready to:
  • Write and apply a backend Deployment manifest with 3 replicas.
  • Create a ClusterIP Service to route internal traffic to the backend pods.
  • Verify DNS-based service discovery works from inside the cluster.
Write the backend Deployment manifest

A Deployment tells Kubernetes how many copies (replicas) of a container to run and how to keep them healthy. If a replica crashes, the Deployment automatically replaces it.

  • Create a new file called backend-deployment.yaml and add the following content:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  labels:
    app: multi-app
    tier: backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: multi-app
      tier: backend
  template:
    metadata:
      labels:
        app: multi-app
        tier: backend
    spec:
      containers:
        - name: backend
          image: public.ecr.aws/l6m2t8p7/docker-2048:latest
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "250m"
              memory: "256Mi"

What does this manifest do?

  • replicas: 3 tells Kubernetes to run three identical copies of the backend container at all times.
  • selector.matchLabels connects the Deployment to the pods it manages using the labels app: multi-app and tier: backend.
  • The image field points to a public 2048 game container hosted on Amazon ECR. This acts as a simple HTTP server for testing.
  • The resources section sets CPU and memory requests (what the pod needs to start) and limits (the maximum it can consume).
  • Save backend-deployment.yaml.
  • Confirm the file exists by running:
ls backend-deployment.yaml

You should see backend-deployment.yaml listed in the output.

File not found?

Make sure you saved the file in your current working directory. Run pwd to check which directory you're in, then confirm the file is there.

help me find where my file was saved

✔️ Awesome, I've got everything!

Great. Make sure the file is saved before moving on.

ⓧ I'd like to double check the full code

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  labels:
    app: multi-app
    tier: backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: multi-app
      tier: backend
  template:
    metadata:
      labels:
        app: multi-app
        tier: backend
    spec:
      containers:
        - name: backend
          image: public.ecr.aws/l6m2t8p7/docker-2048:latest
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "250m"
              memory: "256Mi"
Write the ClusterIP Service and apply both manifests

A ClusterIP Service gives your backend pods a single, stable internal address. Other workloads in the cluster can reach the backend using this address instead of tracking individual pod IPs.

  • Create a new file called backend-service.yaml and add the following content:
apiVersion: v1
kind: Service
metadata:
  name: backend
  labels:
    app: multi-app
    tier: backend
spec:
  type: ClusterIP
  selector:
    app: multi-app
    tier: backend
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP

What does this Service do?

The selector field matches the same labels (app: multi-app, tier: backend) you defined on the Deployment's pods. This is how the Service knows which pods to route traffic to.

type: ClusterIP means this Service is only accessible from inside the cluster. External traffic cannot reach it directly.

  • Save backend-service.yaml.

✔️ Awesome, I've got everything!

Great. Make sure the file is saved before applying.

ⓧ I'd like to double check the full code

apiVersion: v1
kind: Service
metadata:
  name: backend
  labels:
    app: multi-app
    tier: backend
spec:
  type: ClusterIP
  selector:
    app: multi-app
    tier: backend
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP

Now apply both manifests to your cluster.

  • Apply the backend Deployment by running:
kubectl apply -f backend-deployment.yaml
  • Apply the backend Service by running:
kubectl apply -f backend-service.yaml
  • Verify the backend pods are running:
kubectl get pods -l tier=backend

You should see 3 pods with a Running status. It may take a minute for all pods to start, especially while EKS Auto Mode provisions a node.

  • Verify the Service was created by running:
kubectl get svc backend

You should see a Service named backend with type ClusterIP and an internal IP address assigned.

Pods stuck in Pending?

EKS Auto Mode needs to provision a node before your pods can run. Wait 1-2 minutes and run kubectl get pods -l tier=backend again. If pods stay in Pending for more than 3 minutes, check for errors with kubectl describe pod followed by a pod name from the list.

help me debug why my backend pods are stuck in Pending

Test DNS-based service discovery

Kubernetes automatically creates a DNS entry for every Service. This means any pod in the cluster can reach your backend by simply using the Service name backend as a hostname.

  • Test the backend Service from inside the cluster by running a temporary pod:
kubectl run tmp-test -it --rm --restart=Never --image=busybox -- wget -qO- http://backend

What does this command do?

  • kubectl run tmp-test creates a temporary pod named tmp-test.
  • -it --rm --restart=Never makes it interactive, deletes the pod when done, and ensures it runs only once.
  • wget -qO- http://backend fetches the response from your backend Service using its DNS name. Kubernetes DNS resolves backend to the ClusterIP address automatically.

You should see HTML content returned in your terminal. This confirms that pods inside the cluster can discover and communicate with the backend Service using just its name.

Seeing a connection refused or timeout?

Make sure all 3 backend pods are in Running status before testing. Run kubectl get pods -l tier=backend to check. If pods are running but the connection still fails, verify the Service selector matches the pod labels by running kubectl describe svc backend.

help me troubleshoot my backend Service connection

Your backend is live and discoverable inside the cluster. Next up, you'll deploy a frontend web server and connect it to this backend.

Deploy the Frontend Service

Your backend API is running and reachable via Kubernetes DNS. The next piece is the frontend web server that users will eventually interact with.

In this step, you will deploy a frontend Deployment using an nginx container image, expose it with a NodePort Service, and confirm the frontend can reach the backend through internal service discovery.

In this step, get ready to:
  • Write and apply Kubernetes manifests for a frontend Deployment and NodePort Service.
  • Verify the frontend pods are running and the Service has a NodePort assigned.
  • Confirm the frontend can reach the backend through Kubernetes DNS.
Write the frontend Deployment manifest

The frontend Deployment follows the same structure as the backend. It uses a public nginx image and runs 2 replicas to handle traffic.

  • Create a file called frontend-deployment.yaml and paste the following manifest:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  labels:
    app: multi-app
    tier: frontend
spec:
  # Run 2 copies of the frontend pod
  replicas: 2
  selector:
    matchLabels:
      app: multi-app
      tier: frontend
  template:
    metadata:
      labels:
        app: multi-app
        tier: frontend
    spec:
      containers:
        - name: frontend
          # Official nginx image from Amazon ECR Public Gallery
          image: public.ecr.aws/nginx/nginx:latest
          ports:
            - containerPort: 80
          # Resource requests and limits for scheduling and throttling
          resources:
            requests:
              cpu: "50m"
              memory: "64Mi"
            limits:
              cpu: "200m"
              memory: "128Mi"

What does this manifest do?

This Deployment tells Kubernetes to run 2 replicas of an nginx-based frontend pod. Each pod gets the labels app: multi-app and tier: frontend so the Service can find them.

The resources block tells Kubernetes how much CPU and memory to reserve (requests) and the maximum each pod is allowed to consume (limits). This helps Kubernetes schedule pods efficiently across nodes.

  • Save frontend-deployment.yaml.
  • You will apply this file in the next substep to see it running on the cluster.

Now you need a Service to route traffic to these frontend pods. The backend used a ClusterIP Service (internal only), but the frontend needs a NodePort Service because it will sit behind an Ingress later.

  • Create a file called frontend-service.yaml and paste the following manifest:
apiVersion: v1
kind: Service
metadata:
  name: frontend
  labels:
    app: multi-app
    tier: frontend
spec:
  # NodePort exposes the Service on a high-numbered port on each node
  type: NodePort
  selector:
    app: multi-app
    tier: frontend
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP

Why NodePort instead of ClusterIP?

A ClusterIP Service is only reachable from inside the cluster. A NodePort Service opens a port on every node, making it accessible from outside the cluster.

The Ingress resource you will create in the next step needs a NodePort or LoadBalancer Service as its backend. NodePort is the right fit here because the Application Load Balancer provisioned by the Ingress will forward external traffic to this NodePort.

  • Save frontend-service.yaml.
  • You will apply this file alongside the Deployment in the next substep.

Not sure where to create the files?

Create them in the same directory where your backend-deployment.yaml and backend-service.yaml files live. You can confirm with ls to see all four files listed together.

Help me find the right directory for my manifest files.

✔️ Awesome, I've got everything!

Great. Make sure both files are saved before moving on.

ⓧ I'd like to double check the full code

Here is frontend-deployment.yaml in full:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  labels:
    app: multi-app
    tier: frontend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: multi-app
      tier: frontend
  template:
    metadata:
      labels:
        app: multi-app
        tier: frontend
    spec:
      containers:
        - name: frontend
          image: public.ecr.aws/nginx/nginx:latest
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: "50m"
              memory: "64Mi"
            limits:
              cpu: "200m"
              memory: "128Mi"

And here is frontend-service.yaml in full:

apiVersion: v1
kind: Service
metadata:
  name: frontend
  labels:
    app: multi-app
    tier: frontend
spec:
  type: NodePort
  selector:
    app: multi-app
    tier: frontend
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
Apply the manifests and verify the frontend is running
  • Apply both frontend manifests by running these commands:
kubectl apply -f frontend-deployment.yaml
kubectl apply -f frontend-service.yaml

What do these commands do?

Each kubectl apply command sends the manifest to the Kubernetes API server. The API server creates the Deployment (which spins up 2 frontend pods) and the NodePort Service (which assigns a high-numbered port on every node for external access).

  • Check that the frontend pods are running by running:
kubectl get pods -l tier=frontend

You should see 2 pods with STATUS: Running. Their names will start with frontend- followed by a random suffix.

  • Confirm the Service has a NodePort assigned by running:
kubectl get service frontend

You should see TYPE: NodePort and a port mapping like 80:<high-port>/TCP in the output. The high port number (e.g. 31234) is the NodePort that Kubernetes assigned automatically.

Pods stuck in Pending or ContainerCreating?

EKS Auto Mode may take a moment to provision a node for the new pods. Wait 30-60 seconds and run kubectl get pods -l tier=frontend again.

If pods stay in Pending for more than 2 minutes, check events with kubectl describe pod <pod-name> to see if there is a scheduling or resource issue.

Help me diagnose why my frontend pods are stuck.

Verify frontend-to-backend connectivity

The frontend pods share the same cluster network as the backend pods. This means they can reach the backend ClusterIP Service by its DNS name backend without any extra configuration.

  • Test connectivity from inside the cluster by running a temporary pod:
kubectl run frontend-test --image=busybox --rm -it --restart=Never -- wget -qO- http://backend

What does this command do?

This spins up a temporary busybox pod that runs wget to fetch http://backend. Kubernetes DNS resolves backend to the ClusterIP of your backend Service. The --rm flag deletes the pod automatically after the command finishes.

This proves that any pod in the cluster (including your frontend pods) can reach the backend by name. The frontend will use the same DNS name to send API requests.

You should see HTML output from the backend container (the 2048 game page). This confirms the DNS name backend resolves correctly and traffic reaches the backend pods.

Seeing 'wget: bad address' or no output?

If you see a DNS resolution error, wait a moment for CoreDNS to propagate and try again. The backend Service must be in the same namespace (default) for the short name backend to resolve.

If the command hangs, press Ctrl+C to cancel. Then verify your backend pods are still running with kubectl get pods -l tier=backend.

Help me debug frontend-to-backend connectivity.

Your frontend and backend are both running and connected through Kubernetes service discovery. Next up, you will expose the frontend to the internet by creating an Ingress that provisions an AWS Application Load Balancer.

Expose the App with an ALB Ingress

Your backend and frontend services are deployed and communicating inside the cluster. The missing piece is getting internet traffic into the cluster so users can actually reach your application.

Kubernetes uses an Ingress resource to route external HTTP traffic to internal Services. On EKS Auto Mode, creating an Ingress automatically provisions an AWS Application Load Balancer that handles the internet-facing traffic for you.

In this step, get ready to:
  • Create IngressClassParams and IngressClass resources to configure ALB behavior.
  • Create an Ingress resource to route traffic to the frontend.
  • Retrieve the ALB hostname and access the application in a browser.
Create the IngressClassParams and IngressClass resources

Before you can create an Ingress, Kubernetes needs to know which controller should handle it and how to configure the load balancer. You define this with two resources: an IngressClassParams (the ALB configuration) and an IngressClass (the controller binding).

  • Create a file called alb-ingressclassparams.yaml with the following manifest:
# Configures the ALB scheme as internet-facing
apiVersion: eks.amazonaws.com/v1
kind: IngressClassParams
metadata:
  name: alb
spec:
  scheme: internet-facing

What does this manifest do?

This resource tells EKS Auto Mode's built-in ALB controller how to configure load balancers. The scheme: internet-facing setting means the ALB will get a public IP address that anyone on the internet can reach.

The API version eks.amazonaws.com/v1 is specific to EKS Auto Mode. It differs from the older self-managed AWS Load Balancer Controller.

  • Save the file, then apply it to your cluster by running:
kubectl apply -f alb-ingressclassparams.yaml
  • You should see a confirmation message indicating the IngressClassParams resource was created.

Seeing an error about the API version?

If you see an error mentioning that eks.amazonaws.com/v1 is not recognized, confirm your cluster was created with Auto Mode enabled. The eks.amazonaws.com API group is only available on EKS Auto Mode clusters.

Also check for YAML indentation errors. YAML is whitespace-sensitive, so every level must be exactly two spaces.

help me debug my IngressClassParams apply error

✔️ Awesome, I've got everything!

Great. Make sure the file is saved and the resource was created successfully.

ⓧ I'd like to double check the full code

apiVersion: eks.amazonaws.com/v1
kind: IngressClassParams
metadata:
  name: alb
spec:
  scheme: internet-facing
  • Now create a file called alb-ingressclass.yaml with the following manifest:
# Binds the ALB controller to this IngressClass and sets it as default
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: alb
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: eks.amazonaws.com/alb
  parameters:
    apiGroup: eks.amazonaws.com
    kind: IngressClassParams
    name: alb

What does this manifest do?

The IngressClass connects Ingress resources to a specific controller. Here, controller: eks.amazonaws.com/alb tells Kubernetes to use EKS Auto Mode's built-in ALB controller.

The parameters section links back to the IngressClassParams you just created. The annotation ingressclass.kubernetes.io/is-default-class: "true" makes this the default IngressClass for any Ingress in the cluster.

  • Save the file, then apply it by running:
kubectl apply -f alb-ingressclass.yaml
  • Verify both resources exist by running:
kubectl get ingressclass
  • You should see the alb IngressClass listed with the controller eks.amazonaws.com/alb.

Don't see the IngressClass?

Make sure you applied both files. The IngressClass depends on the IngressClassParams being created first. Run kubectl get ingressclassparams to confirm the params resource exists.

Check for typos in the parameters section. The apiGroup, kind, and name fields must match the IngressClassParams resource exactly.

help me troubleshoot my IngressClass creation

✔️ Awesome, I've got everything!

Great. Make sure the file is saved and the IngressClass appears in the output.

ⓧ I'd like to double check the full code

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: alb
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: eks.amazonaws.com/alb
  parameters:
    apiGroup: eks.amazonaws.com
    kind: IngressClassParams
    name: alb
Create the Ingress resource

With the IngressClass in place, you can now create the actual Ingress resource. This is the resource that triggers ALB provisioning and defines how traffic reaches your frontend.

  • Create a file called alb-ingress.yaml with the following manifest:
# Routes all HTTP traffic to the frontend Service
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: multi-app-ingress
spec:
  ingressClassName: alb
  rules:
    - http:
        paths:
          - path: /*
            pathType: ImplementationSpecific
            backend:
              service:
                name: frontend
                port:
                  number: 80

What does this manifest do?

The Ingress tells Kubernetes to route incoming HTTP requests to a backend Service. The ingressClassName: alb field binds this Ingress to the IngressClass you just created.

The path: /* with pathType: ImplementationSpecific catches all requests and forwards them to the frontend Service on port 80. This is the EKS Auto Mode path syntax for ALB-based Ingresses.

  • Save the file, then apply it by running:
kubectl apply -f alb-ingress.yaml
  • Verify the Ingress was created by running:
kubectl get ingress
  • You should see multi-app-ingress listed. The ADDRESS column may be empty for now. The ALB takes a few minutes to provision.

Ingress not appearing?

Check that ingressClassName in your Ingress manifest matches the IngressClass name exactly (alb). A mismatch means no controller picks up the Ingress.

Also verify the frontend Service exists and is type NodePort. The ALB controller requires a NodePort Service to route traffic to your pods.

help me debug why my Ingress resource isn't working

✔️ Awesome, I've got everything!

Great. Make sure the file is saved and the Ingress appears when you run kubectl get ingress.

ⓧ I'd like to double check the full code

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: multi-app-ingress
spec:
  ingressClassName: alb
  rules:
    - http:
        paths:
          - path: /*
            pathType: ImplementationSpecific
            backend:
              service:
                name: frontend
                port:
                  number: 80
Retrieve the ALB hostname and access the application

EKS Auto Mode's ALB controller is now provisioning a real AWS Application Load Balancer in your account. This takes a few minutes to complete.

Why does the ALB take a few minutes?

AWS is creating an internet-facing load balancer, configuring target groups, registering your nodes, and performing health checks. This real-world provisioning is what makes your application production-ready.

  • Wait 2-3 minutes, then retrieve the ALB hostname by running:
kubectl get ingress multi-app-ingress -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
  • You should see a long hostname ending in .elb.amazonaws.com. If the output is empty, wait another minute and try again.
  • Copy the hostname and paste it into your browser's address bar.
  • You should see the application loaded in your browser. This confirms traffic is flowing from the internet through the ALB into your frontend pods.

Bookmark the hostname

The ALB hostname is how you access your application from anywhere. You can also run kubectl get ingress to see the hostname in the ADDRESS column once provisioning is complete.

Hostname still empty after 5 minutes?

Run kubectl describe ingress multi-app-ingress and check the Events section at the bottom. Look for error messages from the ALB controller.

Common causes include missing subnet tags (these are created automatically by eksctl, so if you created the cluster a different way, the tags may be missing) or IAM permission issues.

help me figure out why my ALB isn't provisioning

Secret mission

Scale Your Backend with a Horizontal Pod Autoscaler

Your multi-container app is running, but what happens when traffic spikes? In this secret mission, you will configure a Horizontal Pod Autoscaler that automatically scales your backend pods based on CPU utilization, then load-test it to watch Kubernetes respond to demand in real time.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project uses paid AWS resources that incur charges every hour they remain active.

Cost warning

Your EKS cluster costs $0.10 per hour for the control plane alone, plus EC2 instance and ALB charges on top. That adds up to at least $2.40/day even with no traffic. If you are done with this project, delete the cluster now to avoid unexpected charges.

Resources you used:

  • EKS cluster named eks-multi-app (control plane at $0.10/hr).
  • EC2 instances provisioned by EKS Auto Mode for worker nodes.
  • Application Load Balancer provisioned by the Ingress resource.
  • VPC, subnets, NAT gateways, and associated networking resources.
  • Local YAML manifest files (backend-deployment.yaml, backend-service.yaml, frontend-deployment.yaml, frontend-service.yaml, alb-ingressclassparams.yaml, alb-ingressclass.yaml, alb-ingress.yaml, backend-hpa.yaml).

Keep everything running

No action needed. Choose this if you are still actively experimenting with your EKS cluster or plan to continue working within the next hour or two.

  • Your cluster will continue to incur charges. The control plane alone costs $0.10/hr ($2.40/day), with EC2 and ALB costs on top.
  • Your application remains accessible at the ALB hostname.
  • Your local YAML manifest files remain on disk.

Pause - I'll come back to this later

Remove the ALB and scale down workloads to minimize costs while keeping the cluster control plane intact. You will still pay $0.10/hr for the control plane, but EC2 and ALB charges stop.

  • Delete the Ingress and ALB resources to stop load balancer charges by running:
kubectl delete ingress multi-app-ingress
kubectl delete ingressclass alb
kubectl delete ingressclassparams alb
  • Scale your Deployments to zero replicas so EKS Auto Mode removes the worker nodes:
kubectl scale deployment backend --replicas=0
kubectl scale deployment frontend --replicas=0
  • When you come back, scale your Deployments back up and re-apply the Ingress manifests to restore access.

Delete - I don't want to use this again

Remove all project resources and stop all charges. This deletes the EKS cluster, its CloudFormation stacks, all networking resources, and the ALB.

  • Delete the entire EKS cluster and all associated AWS resources by running:
eksctl delete cluster --name=eks-multi-app
  • This command tears down the cluster, EC2 instances, VPC, subnets, NAT gateways, and IAM roles. The ALB is cleaned up automatically when the Ingress is deleted as part of cluster teardown.
  • Cluster deletion takes several minutes. Wait for the command to complete before verifying.
  • Verify in the AWS Console that no orphaned resources remain. Check the EC2, VPC, and CloudFormation services in the region you created the cluster.
  • Delete your local manifest files by running:
rm backend-deployment.yaml backend-service.yaml frontend-deployment.yaml frontend-service.yaml alb-ingressclassparams.yaml alb-ingressclass.yaml alb-ingress.yaml backend-hpa.yaml
  • Confirm the files are removed by running:
ls *.yaml
  • You should see a message indicating no matching files were found.

That's a Wrap!

That's a Wrap!

Nice work! You just deployed a multi-container application on Amazon EKS, connected services through Kubernetes DNS, and exposed everything to the internet through an Application Load Balancer.

You've learned how to:

  • Create an EKS Auto Mode cluster from the command line using eksctl and verify connectivity with kubectl.
  • Write and apply Kubernetes Deployment manifests for a backend and frontend service, connecting them with a ClusterIP Service and DNS-based service discovery.
  • Expose your application to the internet using an IngressClass and Ingress resource that provisions an AWS Application Load Balancer through EKS Auto Mode.
  • (Secret Mission) Configured a Horizontal Pod Autoscaler and observed automatic scaling under load.

Ready to quiz yourself?