In this learn guide we will obtain a free TLS certificate from LetsEncrypt for an application we have running on our Kubernetes cluster. The sample application will be written in Node.js using the popular microservices framework Express.js.
- A Kubernetes cluster you control. This can be an existing, pre-configured cluster, or if you don't have one yet here are a couple of options for using k3s, a light-weight Kubernetes distribution from Rancher:
- If you are part of the #KUBE100 beta program on Civo, you can use the dashboard or command line to create a cluster in minutes. You can apply for the beta here.
- If you are not in the beta, or use another cloud provider, you can use k3sup.
- Once you have a virtual machine instance running, download
k3sup install --ip $IP --user $USERwhich will use
sshto log into the remote computer and install Kubernetes. You can use a Civo Instance if you like, just make sure you add your SSH key when setting up.
- Once you have a virtual machine instance running, download
- A working
kubectlsetup. Once you have a new or existing cluster, get your
kubectl command-line to point at it and verify that with
kubectl config get-contexts. See also: Setup and install
- A domain name. You'll need a domain and you can register one for as little as $2 from namecheap.com. For the examples below I used Google Domains and a .dev address. These cost between £10-15 at time of writing.
Pictured: Conceptual diagram
First let's install
cert-manager, and then a sample application written in Node.js. Once that's in place, we can explore the steps necessary to configure the TLS certificate.
cert-manager will then obtain, manage, and renew the certificate for us on an on-going basis.
If you're using Civo's k3s service, then when you create your cluster you can add an "app" from the marketplace. If you're doing this after the fact you can run the following using the
$civo k8s ls graph-block $ civo applications add cert-manager --cluster graph-block Added cert-manager v0.11.0 to Kubernetes cluster graph-block
This finds your cluster by name and installs the
cert-manager app. You can see that in my case, the cluster was called
If you're not using Civo's k3s service then run the following instead:
k3sup app install cert-manager
The sample application we will install is an Express.js microservice built to show how to get started. It includes the following:
- a Dockerfile which can be used to rebuilt the code at any time
- a Deployment YAML file
- a Service YAML file
- the index.js and package.json used by Node.js
The sample comes with three endpoints which you can edit at a later time:
/- serves a HTML page
/links- serves a JSON response of links
/health- serves a health endpoint giving a HTTP 200 OK status.
Let's clone the repository onto your local machine:
$ git clone https://github.com/alexellis/expressjs-k8s $ cd expressjs-k8s
Deployment which will create a
Pod in your cluster.
$ kubectl apply -f ./yaml/dep.yaml
Now apply the
Service, which is of type
ClusterIP. This type of service is not accessible outside of your cluster.
$ kubectl apply -f ./yaml/svc.yaml
Forward the port for the app to your local computer:
kubectl port-forward svc/expressjs 8080:8080
In a browser open http://127.0.0.1:8080
If you're new to writing Kubernetes YAML files then take a few moments to read over the two YAML files,
We now need to create two additional objects in Kubernetes to get our certificate:
- Ingress - a routing definition for Traefik (the built-in k3s IngressController)
- Issuer - a definition for cert-manager to use for obtaining certificates In addition to that, we must also create a record in our DNS system for the domain name.
If you're using k3s then Traefik is installed by default and exposes port 80 and 443 on each node. Simply find the public IP of one of your nodes and create a DNS entry for it in your DNS provider.
If you are on the Civo managed Kubernetes service, you will find the DNS entry in your dashboard:
Use this DNS entry to create a CNAME record with the DNS entry in your domain name registrar, such as
If you are using antoher service with a public IP address, create an A record with the public IP, i.e.
Now create an Issuer. Issuers are used by cert-manager to identify or authenticate your user. We'll use a HTTP01 Issuer which is only used to identify you and share your email address.
apiVersion: cert-manager.io/v1alpha2 kind: Issuer metadata: name: letsencrypt-prod spec: acme: email: firstname.lastname@example.org server: https://acme-v02.api.letsencrypt.org/directory privateKeySecretRef: name: expressjs-secret-issuer-account-key solvers: - http01: ingress: class: traefik
Save the above file as
issuer.yaml, changing the email address to yours.
Apply the file:
$ kubectl apply -f issuer.yaml
Now let's create the Ingress record. Save the following as
ingress.yaml, and edit
expressjs.example.com, replacing that with your own domain.
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: expressjs annotations: cert-manager.io/issuer: letsencrypt-prod kubernetes.io/ingress.class: "traefik" spec: tls: - hosts: - expressjs.example.com secretName: expressjs-secret rules: - host: expressjs.example.com http: paths: - backend: serviceName: expressjs servicePort: 8080 path: /
Now, apply this file:
$ kubectl apply -f ingress.yaml
Access your site at the domain you set up in the previous steps, such as https://expressjs.example.com. You should see the following page:
You can also access a JSON API via
If you don't see the certificate at once, check that you can
ping your domain and that it gives the correct result. DNS configuration can take a few minutes to propagate throughout the Internet.
You now have a Node.js website deployed with a free TLS certificate from LetsEncrypt. It's over to you to extend the functionality and to customise it as you see fit. Build your own API and/or webpage and share it with me on Twitter @alexellisuk or the @CivoCloud community. We would love to hear what you end up making!
You can customise the example using this workflow:
- Edit the Node.js code in
- Build a new container image under your username (
docker build -t yourname/image:version.)
- Push the image to the Docker Hub or a similar registry (
docker push yourname/image:version)
- Update the
kubectl apply -f dep.yamlThe TLS certificate, Ingress and Issuer all stay the same between iterations.
Find out more about the projects we used today: