The limitation on attaching ACM certificates for an Application Load Balancer (ALB) is over! Now, let’s deploy multi-tenant applications using NGINX Ingress Controller, Cert-Manager, and Let’s Encrypt issuer integration.

Kemila De Silva
6 min readFeb 24, 2024

In this article, I’m going to explain how we deployed a Multi-tenant app by using ALB together with ACM certificates previously, and how we discovered the limitations of that approach and subsequently migrated to cert-manager integration.

Let’s take a look at how we deployed a Multi-tenant app together with ALB and ACM certificates prior to implementing the cert-manager integration. Then, you can get an idea of the limitations.

To do that integration, we had to add multiple ACM certificates for a single Application Load Balancer. Allow me to explain that deployment process using the diagram below:

When implementing this solution, we encountered a significant limitation that directly impacts our Multi-tenant deployment strategy.

The limitation arises when attaching ACM certificates to a single Application Load Balancer (ALB), which initially allows only 25 certificates. Although this limit can be increased to 100 certificates by submitting a support ticket, it still imposes constraints on our ability to deploy merchants within our multi-tenant application. While one workaround involves creating multiple ALB groups and attaching 100 certificates per ALB, but when it’s comes to multiple deployments; it’s going to be a huge hassle.

To address these challenges comprehensively, we devised a solution by integrating cert-manager and NGINX Ingress Controller with a classic load balancer, allowing us to create unlimited certificates using Let’s Encrypt issuer.

Let’s explore this solution further through the diagram below.

In this solution, we will utilize the following major resources:

  • Cert-Manager: A Kubernetes add-on to automate the management and issuance of TLS certificates.
  • Let’s Encrypt issuer: A component of Cert-Manager that interfaces with Let’s Encrypt, a free and automated certificate authority, to issue TLS certificates.
  • NGINX Ingress Controller: An open-source solution for managing ingress traffic into Kubernetes clusters.
  • Classic LoadBalancer: The traditional AWS load balancer used to distribute incoming application traffic across multiple targets.

To implement the above solution, we don’t want to separately add an nginx-ingress controller; since we already implemented AWS LoadBalancer Controller configurations to our cluster, However, if you wish to include NGINX Ingress Controller, you can do so using the following YAML configuration and command.

kubectl apply -f deploy.yaml

Once you apply the above nginx-ingress controller deployment, you will receive the relevant resources for the nginx-ingress controller as below;

Then you need to install cert-manager deployment to your Cluster to manage your let’s encrypt certificates.

To install cert-manager you can run this command;

helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.11.0 \
--set installCRDs=true

For the cert-manager deployment, it will take few minutes to setup; to verify your deployment you can run this command;

kubectl get pods -n cert-manager

The output should be similar like this;

Once the cert-manager deployment done, we can move on to the Cluster-Issuer deployment. Cluster Issuer deployment will issue let’s encrypt certificates to our multi-tenant applications and in the below yaml you should need to update your email address.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: <PUT-YOUR-EMAIL-ADDRESS-HERE>
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
kubectl apply -f cluster-issuer.yaml

To check whether the Cluster issuer is deployed successfully put this command you will see similar response like this;

If it shows as the status as “True”; you’re good to go!

Then let’s deploy a sample application by using below yamls.

apiVersion: apps/v1
kind: Deployment

metadata:
name: deployment-2048
spec:
selector:
matchLabels:
app.kubernetes.io/name: app-2048
replicas: 5
template:
metadata:
labels:
app.kubernetes.io/name: app-2048
spec:
containers:
- image: alexwhen/docker-2048
imagePullPolicy: Always
name: app-2048
ports:
- containerPort: 80

This is a sample application, which provides you to a simple game provided by AWS. And to expose this deployment you should need a service as well.

apiVersion: v1
kind: Service
metadata:
name: service-2048
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
type: NodePort
selector:
app.kubernetes.io/name: app-2048

You can apply both files by using below commands.

kubectl create ns sample-app

kubectl apply -f deployment.yaml service.yaml -n sample-app

Now you should need to have a domain to point out your application. Once you find it, you can point that nginx-ingress deployment loadbalancer endpoint to your custom domain as an example below;

CNAME     app.abc.com      a123456789-XXXXXXXXX.us-east-2.elb.amazonaws.com

To propagate your DNS record will take around 10 mins — 24 Hrs according to your domain registrar and it’s better to put your DNS entry TTL value as 60 if possible.

You can check your DNS propagation status as following example;

dig app.abc.com

Once your propagation is done; and if you browse your custom endpoint, you will receive 5xx or 4xx error code with an nginx page; which means your domain is successfully pointed to your loadbalancer.

Let’s create an ingress to your application and it will give privilege to add multiple host + certificates.

In the following ingress.yaml file, I’ll show you; how to add multiple host names and certificates into a single ingress by using cert-manager and let’s encrypt issuer.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-2048-ingress
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
rules:
- host: app.abc.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service-2048
port:
number: 80
- host: sample-app.abc.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service-2048
port:
number: 80
- host: sample-app.test.net
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service-2048
port:
number: 80
tls:
- hosts:
- app.abc.com
secretName: tls-cert
- hosts:
- sample-app.abc.com
secretName: tls-cert2
- hosts:
- sample-app.test.net
secretName: tls-cert3
kubectl apply -f ingress.yaml

According to our deployment strategy, you should need to add nginx and let’s encrypt issuer annotations in your ingress and you can add unlimited host name according to your project requirement and finally you can add a relevant name for your hostname pointed secret.

Once you apply the above ingress to your cluster, it will configure all the host names into a nginx-ingress classic loadbalancer through nginx-ingress controller and create multiple secrets as we mentioned above and binds let’s encrypt certificates.

Need to mention, SSL certificate creation time will take few minutes and you can check your all the dns endpoints through the browser to verify the let’s encrypt certificate. Also the renewal of those certificates will be automatically managed by cert-manager.

Once the SSL certificate is applied; you will see the browser response as below;

Based on this strategy you can deploy multi-tenant applications without any hassle of AWS ACM certificates limitation issue.

If you have any questions; please drop them in the comments section.

--

--

Kemila De Silva

Senior DevOps Engineer @aeturnuminc • CNCF Ambassador • AWS Community Builder • AWS Certified • Community Organizer • || kemilad.bio.link