The ultimate goal of application development is, of course, to expose apps on the Internet. For a developer, Kubernetes simplifies this process to a degree by providing the Ingress controller as the mechanism for routing requests to the application. But not everything is as self‑service as you probably would like: you still need a record in the Domain Name System (DNS) to map the domain name for the app to the Ingress controller’s IP address and a TLS certificate to secure connections using HTTPS. In most organizations, you don’t own DNS or TLS yourself and so have to coordinate with the operational group (or groups!) that do.
Things aren’t necessarily any easier for operators. In most organizations the need to update DNS records is rare enough that procedures – both business rules and the actual technical steps – tend to be sparse or non‑existent. This means that when you need to add a DNS record you first need to find the documentation, ask a colleague, or (in a worst case) figure it out. You also need to ensure you’re in compliance with any corporate security rules and make sure that the ingress is tagged properly for the firewalls.
Fortunately, there is a way to make life easier for both developers and operators. In this post, we show how operators can configure a Kubernetes deployment to enable self‑service for developers to update DNS records and generate TLS certificates in a Kubernetes environment. By building out the infrastructure ahead of time, you can assure that all necessary business and technical requirements are being satisfied.
Overview and Prerequisites
With the solution in place, all a developer needs to do to expose an application to the Internet is create an Ingress controller following a supplied template that includes a fully qualified domain name (FQDN) within a domain managed by the Kubernetes installation. Kubernetes uses the template to allocate an IP address for the Ingress controller, create the DNS A record to map the FQDN to the IP address, and generate TLS certificates for the FQDN and add them to the Ingress controller. Cleanup is just as easy: when the Ingress is removed, the DNS records are cleaned up.
The solution leverages the following technologies (we provide installation and configuration instructions below):
- Kubernetes.
- ExternalDNS.
- cert-manager.
- Let’s Encrypt.
- NGINX Ingress Controller from F5 NGINX, based on either NGINX Open Source or NGINX Plus. The solution doesn’t work with the NGINX Ingress Controller maintained by the Kubernetes community. For more details on the differences between the two projects, see our blog.
Before configuring the solution, you need:
- A Kubernetes cloud installation with an egress (
LoadBalancer) object. The solution uses Linode, but other cloud providers also work. - A domain name hosted with Cloudflare, which we chose because it’s one of the supported DNS providers for cert-manager and supports ExternalDNS (in beta as of the time of writing). We strongly recommend that the domain not be used for production or any other critical purpose.
- Access to the Cloudflare API, which is included in the free tier.
- Helm for installing and deploying Kubernetes.
kubectlas the command‑line interface for Kubernetes.- Optionally, K9s, a well‑constructed tangible user interface (TUI) that provides a more structured way to interact with Kubernetes.
We also assume you have a basic understanding of Kubernetes (how to apply a manifest, use a Helm chart, and issue kubectl commands to view output and troubleshoot). Understanding the basic concepts of Let’s Encrypt is helpful but not required; for an overview, see our blog. You also don’t need to know how cert-manager works, but if you’re interested how it (and certificates in general) work with NGINX Ingress Controller, see my recent post, Automating Certificate Management in a Kubernetes Environment.
We have tested the solution on both macOS and Linux. We haven’t tested on Windows Subsystem for Linux version 2 (WSL2), but don’t foresee any issues.
Note: The solution is intended as a sample proof of concept, and not for production use. In particular, it does not incorporate all best practices for operation and security. For information on those topics, see the cert-manager and ExternalDNS documentation.
Deploying the Solution
Follow the steps in these sections to deploy the solution:
- Download Software
- Deploy NGINX Ingress Controller
- Deploy cert-manager
- Deploy ExternalDNS
- Deploy the Sample Application
- Validate the Solution
Download Software
- Download your Cloudflare API Token.
- Clone the NGINX Ingress Controller repository:
- Verify that you can connect to the Kubernetes cluster.
Deploy NGINX Ingress Controller
- Using Helm, deploy NGINX Ingress Controller. Note that we are adding three non‑standard configuration options:
controller.enableCustomResources– Instructs Helm to install the custom resource definitions (CRDs) used to create the NGINX VirtualServer and VirtualServerRoute custom resources.controller.enableCertManager– Configures NGINX Ingress Controller to communicate with cert-manager components.controller.enableExternalDNS– Configures the Ingress Controller to communicate with ExternalDNS components.
- Verify that NGINX Ingress Controller is running and note the value in the
EXTERNAL-IPfield – it’s the IP address for NGINX Ingress Controller (here,www.xxx.yyy.zzz). The output is spread across two lines for legibility.
Deploy cert-manager
In the solution, cert-manager uses the DNS-01 challenge type when obtaining a TLS certificate, which requires the Cloudflare API token be provided during creation of the ClusterIssuer resource. In the solution, the API token is provided as a Kubernetes Secret.
- Using Helm, deploy cert-manager:
- Deploy the Cloudflare API token as a Kubernetes Secret, substituting it for
<your-API-token>: - Create a ClusterIssuer object, specifying
Cloudflare-api-token-secret(defined in the previous step) as the place to retrieve the token. If you wish, you can replaceexample-issuerin themetadata.namefield (andexample-issuer-account-keyin thespec.acme.privateKeySecretRef.namefield) with a different name. - Verify that the ClusterIssuer is deployed and ready (the value in the
READYfield isTrue).
Deploy ExternalDNS
Like cert-manager, the ExternalDNS project requires a Cloudflare API Token to manage DNS. The same token can be used for both projects, but that is not required.
- Create the ExternalDNS CRDs for NGINX Ingress Controller to enable integration between the projects.
- Create the External DNS service (
external-dns). Because the manifest is rather long, here we break it into two parts. The first part configures accounts, roles, and permissions:The second part of the manifest creates the ExternalDNS deployment:- Creates a ServiceAccount object called
external-dnsto manage all write and update operations for managing DNS. - Creates a ClusterRole object (also called
external-dns) that defines the required permissions. - Binds the ClusterRole to the ServiceAccount.
- Creates a domain filter, which limits the scope of possible damage done by ExternalDNS as it manages domains. For example, you might specify the domain names of staging environments to prevent changes to production environments. In this example, we set
domain-filtertoexample.com. - Sets the
CF_API_TOKENenvironment variable to your Cloudflare API Token. For<your-API-token>, substitute either the actual token or a Secret containing the token. In the latter case, you also need to project the Secret into the container using an environment variable. - Sets the
FREE_TIERenvironment variable to"true"(appropriate unless you have a paid Cloudflare subscription).
- Creates a ServiceAccount object called
Deploy the Sample Application
Use the standard NGINX Ingress Controller sample application called Cafe for testing purposes.
- Deploy the Cafe application.
- Deploy NGINX Ingress Controller for the Cafe application. Note the following settings:Note that the time it takes for this step to complete is highly dependent on the DNS provider, as Kubernetes is interacting with the provider’s DNS API.
kind: VirtualServer– We are using the NGINX VirtualServer custom resource, not the standard Kubernetes Ingress resource.spec.host– Replacecafe.example.comwith the name of the host you are deploying. The host must be within the domain being managed with ExternalDNS.spec.tls.cert-manager.cluster-issuer– If you’ve been using the values specified in this post, this isexample-issuer. If necessary, substitute the name you chose in Step 3 of Deploy cert‑manager.spec.externalDNS.enable– The valuetruetells ExternalDNS to create a DNSArecord.
Validate the Solution
- Verify the DNS
Arecord – in particular that in theANSWERSECTIONblock the FQDN (here,cafe.example.com) is mapped to the correct IP address (www.xxx.yyy.zzz). - Check that the certificate is valid (the value in the
READYfield isTrue). - Verify that you can reach the application.
What Happens When a Developer Deploys NGINX Ingress Controller
A lot happens under the covers once the solution is in place. The diagram shows what happens when a developer deploys the NGINX Ingress Controller with an NGINX VirtualServer custom resource. Note that some operational details are omitted.

- Developer deploys a VirtualServer resource using the NGINX CRD
- Kubernetes creates the VirtualServer using NGINX Ingress Controller
- NGINX Ingress Controller calls ExternalDNS to create a DNS
Arecord - ExternalDNS creates the
Arecord in DNS - NGINX Ingress Controller calls cert-manager to request a TLS certificate
- cert-manager adds a DNS record for use during the DNS-01 challenge
- cert-manager contacts Let’s Encrypt to complete the challenge
- Let’s Encrypt validates the challenge against DNS
- Let’s Encrypt issues the TLS certificate
- cert-manager provides the TLS certificate to NGINX Ingress Controller
- NGINX Ingress Controller routes TLS‑secured external requests to the application pods
Troubleshooting
Given the complexity of Kubernetes along with the components we are using, it is difficult to provide a comprehensive troubleshooting guide. That said, there are some basic suggestions to help you determine the problem.
- Use the
kubectlgetandkubectldescribecommands to validate the configuration of deployed objects. - Use the
kubectllogs<component>command to view log files for the various deployed components. - Use K9s to inspect the installation; the software highlights problems in yellow or red (depending on severity) and provides an interface to access logs and details about objects.
If you are still having issues, please find us on the NGINXCommunity Slack and ask for help! We have a vibrant community and are always happy to work through issues.
To try the NGINX Ingress Controller based on NGINX Plus, start your 30-day free trial today or contact us to discuss your use cases.
About the Author
Related Blog Posts
Secure Your API Gateway with NGINX App Protect WAF
As monoliths move to microservices, applications are developed faster than ever. Speed is necessary to stay competitive and APIs sit at the front of these rapid modernization efforts. But the popularity of APIs for application modernization has significant implications for app security.
How Do I Choose? API Gateway vs. Ingress Controller vs. Service Mesh
When you need an API gateway in Kubernetes, how do you choose among API gateway vs. Ingress controller vs. service mesh? We guide you through the decision, with sample scenarios for north-south and east-west API traffic, plus use cases where an API gateway is the right tool.
Deploying NGINX as an API Gateway, Part 2: Protecting Backend Services
In the second post in our API gateway series, Liam shows you how to batten down the hatches on your API services. You can use rate limiting, access restrictions, request size limits, and request body validation to frustrate illegitimate or overly burdensome requests.
New Joomla Exploit CVE-2015-8562
Read about the new zero day exploit in Joomla and see the NGINX configuration for how to apply a fix in NGINX or NGINX Plus.
Why Do I See “Welcome to nginx!” on My Favorite Website?
The ‘Welcome to NGINX!’ page is presented when NGINX web server software is installed on a computer but has not finished configuring