Imagine you’re working on a growing Kubernetes platform. You need to expose services securely, manage traffic efficiently, and scale without complexity. Historically, Kubernetes users turned to Ingress resources to expose HTTP and HTTPS applications. But as requirements evolve, think traffic shaping, multi-team environments, advanced routing. The limitations of Ingress become painfully real.
This is where the Gateway API comes in. It is a set of Kubernetes Resources that provide a modern, role-oriented, and extensible approach to traffic routing that fixes Ingress’s shortcomings and paves the way for flexible, scalable architecture.
In this guide, we’ll explore how to deploy a simple web service exposed through the Cilium powered Gateway Resource via HTTPS along with the controllers found in many Kubernetes deployment such as cert-manager utilizing Let’s Encrypt and external-dns.
Please go through the previous tutorial Kubernetes Gateway API: Replace Ingress with Cilium Gateway for HTTP Traffic to better understand the Gateway resource, the value it brings and how to do a simple deployment supporting HTTP traffic.
In this tutorial, we’ll explore how to securely expose an application using HTTPS and the Gateway API, backed by cert-manager and Let’s Encrypt.
Note: Replace example domains (like dolearn.xyz
) with your own domain throughout the tutorial.
This tutorial leverages cert-manager to dynamically request and manage production-ready TLS certificates from Let’s Encrypt.
In this tutorial we will utilize an HTTP-01 challenge as part of the certification request process. This challenge is used by cert-manager and Let’s Encrypt to validate control of the FQDN for certificate issuance. This tutorial references the HTTP-01 challenge throughout, and understanding how it works will be helpful.
Note that in domain dolearn.xyz is used in the examples, be sure to replace those references with your own domain.
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.11/samples/bookinfo/platform/kube/bookinfo.yaml
Applying the bookinfo.yaml
manifest spins up the four Bookinfo microservices—productpage, details, reviews, and ratings—with the core
Before you can automatically provision TLS certificates for your applications, you need to install cert-manager. cert-manager is a Kubernetes add-on that automates the management and issuance of TLS certificates from various sources, including Let’s Encrypt. In this tutorial, cert-manager will be responsible for requesting and renewing certificates for your domains, which is essential for enabling secure HTTPS traffic through the Gateway API.
We’ll use Helm, the Kubernetes package manager, to install cert-manager. This approach ensures you get the latest stable version and makes future upgrades easier. The following commands will add the official Jetstack Helm repository, update your local chart information, and install cert-manager with support for the Gateway API enabled.
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--set crds.enabled=true \
--create-namespace \
--set config.apiVersion="controller.config.cert-manager.io/v1alpha1" \
--set config.kind="ControllerConfiguration" \
--set config.enableGatewayAPI=true --wait
When you create a Gateway, you also decide how it will be shared:
For our use case, let’s create a Dedicated Gateway which defines listeners for:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: tls-gateway
spec:
gatewayClassName: cilium
listeners:
- name: https-1
protocol: HTTPS
port: 443
hostname: "bookinfo.dolearn.xyz"
tls:
certificateRefs:
- kind: Secret
name: gateway-tls-secret
- name: https-2
protocol: HTTPS
port: 443
hostname: "hipstershop.dolearn.xyz"
tls:
certificateRefs:
- kind: Secret
name: gateway-tls-secret
- name: http # created for the purpose of solving HTTP-01 challenges
protocol: HTTP
port: 80
hostname: "bookinfo.dolearn.xyz"
allowedRoutes:
namespaces:
from: Same # Any HTTPRoute in same namespace can bind to this Gateway listener.
- name: http-2 # created for the purpose of solving HTTP-01 challenges
protocol: HTTP
port: 80
hostname: "hipstershop.dolearn.xyz"
allowedRoutes:
namespaces:
from: Same # Any HTTPRoute in same namespace can bind to this Gateway listener.
The Gateway and the app’s HTTPRoutes live in the same namespace. Only the sample-app team can attach routes to it, which keeps routing isolated and prevents other teams/namespaces from using this entry point.
Once the above manifest is applied, two resources will be created. The Gateway and the Service of type LoadBalancer. Note it can take a few minutes for the load balancer to be created and for its IP address to be displayed.
kubectl get gateway
NAME CLASS ADDRESS PROGRAMMED AGE
tls-gateway cilium True 7s
kubectl get svc -o wide | grep LoadBalancer
cilium-gateway-tls-gateway LoadBalancer 10.108.88.49 167.172.14.164 443:32269/TCP,80:30298/TCP 21s <none>
Once you have a Gateway in place, the next step is to define HTTPRoutes—these tell the Gateway how to direct incoming requests to your application services.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: https-app-route-1
spec:
parentRefs:
- name: tls-gateway
sectionName: https-1
hostnames:
- "bookinfo.dolearn.xyz"
rules:
- matches:
- path:
type: PathPrefix
value: /details
backendRefs:
- name: details
port: 9080
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: https-app-route-2
spec:
parentRefs:
- name: tls-gateway
sectionName: https-2
hostnames:
- "hipstershop.dolearn.xyz"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: productpage
port: 9080
parentRefs
points to the Gateway tls-gateway
in the same
namespace.bookinfo.dolearn.xyz
and hipstershop.dolearn.xyz
will be matched by this HTTPRoute./details
go to the details
service on port 9080.
5. Second rule:
- Matches requests where the path starts with / (i.e., all paths).
- Routes these requests to the productpage service on port 9080.ExternalDNS automates the management of DNS records in your provider (in this case, DigitalOcean) based on Kubernetes resources such as Services, Ingresses, and Gateway API routes.
You’ll need a Personal Access Token with write access to manage DNS records.
Create a Kubernetes Secret that ExternalDNS will use to authenticate with DigitalOcean:
kubectl create secret generic digitalocean-dns \
--from-literal=access-token=<YOUR_DIGITALOCEAN_TOKEN>
helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
helm repo update
env: # Passes the DigitalOcean token from the secret
- name: DO_TOKEN
valueFrom:
secretKeyRef:
name: digitalocean-dns
key: access-token
provider: # ExternalDNS provider configuration
name: digitalocean
policy: sync # Ensures ExternalDNS continuously reconciles DNS records
txtOwnerId: gateway-api # Ensures ExternalDNS does not conflict others using this domain
sources: # Kubernetes resources to watch and create DNS records for
- service
- ingress
- gateway-grpcroute
- gateway-httproute
Deploy ExternalDNS using Helm
helm install external-dns external-dns/external-dns -f values.yaml
After deployment, ExternalDNS should automatically create DNS records for your Services/Ingresses.
You can verify with:
$ dig +short hipstershop.dolearn.xyz
167.172.14.164
$ dig +short bookinfo.dolearn.xyz
167.172.14.164
Reference: https://kubernetes-sigs.github.io/external-dns/latest/
Alternatively, you can manually add the DNS records: https://docs.digitalocean.com/products/networking/dns/how-to/manage-records/
An Issuer in cert-manager defines where and how to obtain certificates. In this example, we’ll use Let’s Encrypt with the HTTP-01 challenge.
The HTTP-01 challenge works by temporarily creating an HTTP endpoint under your domain (e.g. http://example.com/.well-known/acme-challenge/…).
Let’s Encrypt then checks this endpoint to confirm that you own the domain. Once verified, it issues a certificate.
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: letsencrypt-gateway
spec:
acme:
email: your-email@example.com # Replace this with your email address
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-gateway-account-key
solvers:
# Use the HTTP-01 challenge provider
- http01:
gatewayHTTPRoute:
parentRefs:
- name: tls-gateway
kind: Gateway
Now, we’ll create a Certificate resource. This tells cert-manager which domain we want a TLS cert for, and which Issuer should handle it.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: letsencrypt-certificate
spec:
secretName: gateway-tls-secret
dnsNames:
- bookinfo.dolearn.xyz
- hipstershop.dolearn.xyz
issuerRef:
name: letsencrypt-gateway
kind: Issuer
kubectl get certificate
NAME READY SECRET AGE
letsencrypt-certificate True gateway-tls-secret 3h32m
When creating a Gateway and requesting a certificate using the HTTP-01 challenge, cert-manager performs a self-check by making an HTTP request to the challenge endpoint to verify it’s publicly accessible.
There can be a delay before the DNS record is created by ExternalDNS and fully propagated. During this time, HTTP-01 challenge propagation might take some time, causing temporary failures in the self-check.
Cert-manager handles this gracefully by retrying the self-check periodically. Once the DNS record becomes available and resolves correctly, the self-check succeeds, allowing the HTTP-01 challenge to complete and the certificate to be issued.
Now, let’s test this by making HTTPS requests to the app using the service.
curl https://bookinfo.dolearn.xyz/details/1
{"id":1,"author":"William Shakespeare","year":1595,"type":"paperback","pages":200,"publisher":"PublisherA","language":"English","ISBN-10":"1234567890","ISBN-13":"123-1234567890"}%
$ curl https://hipstershop.dolearn.xyz/
<!DOCTYPE html>
<html>
<head>
<title>Simple Bookstore App</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="static/bootstrap/css/bootstrap.min.css">
<!-- Optional theme -->
<link rel="stylesheet" href="static/bootstrap/css/bootstrap-theme.min.css">
</head>
<body>
<p>
<h3>Hello! This is a simple bookstore application consisting of three services as shown below</h3>
</p>
<table class="table table-condensed table-bordered table-hover"><tr><th>name</th><td>http://details:9080</td></tr><tr><th>endpoint</th><td>details</td></tr><tr><th>children</th><td><table class="table table-condensed table-bordered table-hover"><tr><th>name</th><th>endpoint</th><th>children</th></tr><tr><td>http://details:9080</td><td>details</td><td></td></tr><tr><td>http://reviews:9080</td><td>reviews</td><td><table class="table table-condensed table-bordered table-hover"><tr><th>name</th><th>endpoint</th><th>children</th></tr><tr><td>http://ratings:9080</td><td>ratings</td><td></td></tr></table></td></tr></table></td></tr></table>
<p>
<h4>Click on one of the links below to auto generate a request to the backend as a real user or a tester
</h4>
</p>
<p><a href="/productpage?u=normal">Normal user</a></p>
<p><a href="/productpage?u=test">Test user</a></p>
<!-- Latest compiled and minified JavaScript -->
<script src="static/jquery.min.js"></script>
<!-- Latest compiled and minified JavaScript -->
<script src="static/bootstrap/js/bootstrap.min.js"></script>
</body>
</html>
With the configuration so far if someone was to try and access the URLs using HTTP they would receive a 404. We need to create HTTPRoute objects that will redirect HTTP requests to the secure HTTPS URLs. This can be done by applying the following to your cluster:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: https-1-redirect
spec:
hostnames:
- bookinfo.dolearn.xyz
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: tls-gateway
sectionName: http
rules:
- filters:
- requestRedirect:
port: 443
scheme: https
statusCode: 301
type: RequestRedirect
matches:
- path:
type: PathPrefix
value: /
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: https-2-redirect
spec:
hostnames:
- hipstershop.dolearn.xyz
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: tls-gateway
sectionName: http-2
rules:
- filters:
- requestRedirect:
port: 443
scheme: https
statusCode: 301
type: RequestRedirect
matches:
- path:
type: PathPrefix
value: /
Now when you curl the http url you can see the redirect and then using -L will follow that redirect to the https URL.
https$ curl -Lv http://bookinfo.dolearn.xyz/details/1
* Trying 167.172.14.164:80...
* Connected to bookinfo.dolearn.xyz (167.172.14.164) port 80 (#0)
> GET /details/1 HTTP/1.1
> Host: bookinfo.dolearn.xyz
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< location: https://bookinfo.dolearn.xyz:443/details/1
< date: Thu, 11 Sep 2025 18:20:19 GMT
< server: envoy
< content-length: 0
<
* Connection #0 to host bookinfo.dolearn.xyz left intact
* Clear auth, redirects to port from 80 to 443
* Issue another request to this URL: 'https://bookinfo.dolearn.xyz:443/details/1'
* Trying 167.172.14.164:443...
* Connected to bookinfo.dolearn.xyz (167.172.14.164) port 443 (#1)
<SNIP>
The Gateway API is the modern successor to Ingress, offering several advantages:
Cilium provides:
Cert-manager integrates with Gateway API through:
ExternalDNS and cert-manager handle delays gracefully:
Yes, you can configure multiple hostnames in a single Gateway:
Common troubleshooting steps:
kubectl get certificate
kubectl logs -n cert-manager deployment/cert-manager
dig your-domain.com
http://your-domain.com/.well-known/acme-challenge/
kubectl describe gateway your-gateway
This tutorial provides a production-ready foundation with:
By following this tutorial, you’ve deployed a sample application, exposed it through a Gateway, and secured it with HTTPS using cert-manager. With the Gateway API, routing, TLS termination, and certificate management become more flexible and Kubernetes-native compared to traditional Ingress.
This foundation can be extended further, whether it’s adding canary traffic splitting, cross-namespace routing, or advanced policies like header matching and rate limiting. With HTTPS in place, your workloads are ready for production traffic, and you now have a modern, scalable way to manage networking in Kubernetes.
Ready to take your Kubernetes networking to the next level? Deploy your applications on DigitalOcean Kubernetes and experience the power of modern traffic routing with Gateway API and Cilium.
Now that you have a solid Gateway API foundation, consider these advanced implementations:
Kubernetes Gateway API Tutorial: Replace Ingress with Cilium Gateway for HTTP Traffic
DOKS Operational Readiness, Part 2: Enable HTTPS Configure OAuth authentication with external providers Set up Prometheus monitoring for Gateway metrics
Explore our comprehensive Kubernetes tutorial library covering everything from basics to enterprise patterns.
Join the DigitalOcean Community to share your Gateway API implementations and learn from other developers.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
A Senior Solutions Architect at DigitalOcean focusing on Cloud Architecture, Kubernetes, Automation and Infrastructure-as-Code.
I help Businesses scale with AI x SEO x (authentic) Content that revives traffic and keeps leads flowing | 3,000,000+ Average monthly readers on Medium | Sr Technical Writer @ DigitalOcean | Ex-Cloud Consultant @ AMEX | Ex-Site Reliability Engineer(DevOps)@Nutanix
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.
Full documentation for every DigitalOcean product.
The Wave has everything you need to know about building a business, from raising funding to marketing your product.
Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.
New accounts only. By submitting your email you agree to our Privacy Policy
Scale up as you grow — whether you're running one virtual machine or ten thousand.
Sign up and get $200 in credit for your first 60 days with DigitalOcean.*
*This promotional offer applies to new accounts only.