Expose a service with a custom domain
Your app runs on the Bunker Kubernetes cluster. You want it reachable over HTTPS, on a real domain name. This tutorial covers two cases: the default *.apps.france-nuage.fr subdomain and your own domain.
What you need
- A namespace on your Bunker Kubernetes cluster
- A Kubernetes Service exposing your app on port 80
- (Optional) A domain name whose DNS you control
How it works
Bunker uses Kingress as Ingress controller and cert-manager for TLS certificates. You declare an Ingress, cert-manager gets the Let's Encrypt certificate, Kingress routes the traffic. So no ops tickets, no manual steps, welcome to the modern world!
Step 1: check your Service
Kingress requires the backend to listen on port 80. If your app uses a different port (3000, 8080, etc.), the Service must translate:
apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
selector:
app: my-app
ports:
- port: 80 # Kingress requires port 80 here
targetPort: 3000 # your app's actual port
If the Service port is not 80, Kingress returns a silent 503. This is the most common error. Your app can listen on any port, but the Kubernetes Service must expose port 80.
Step 2: create the Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- my-app.apps.france-nuage.fr
secretName: my-app-tls
rules:
- host: my-app.apps.france-nuage.fr
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: my-app
port:
number: 80
Three things then happen automatically:
- cert-manager reads the
cert-manager.io/cluster-issuer: "letsencrypt-prod"annotation and requests a certificate from Let's Encrypt - The certificate is stored in the
my-app-tlsSecret, inside your Kubernetes namespace - Kingress terminates TLS and routes HTTP traffic to your Service
After running kubectl apply, wait 30 to 90 seconds for the certificate to be issued.
You can then track progress using the following command:
kubectl get certificate -n my-namespace
When READY shows True, you're good :).
Step 3: use your own domain name
You're not limited to *.apps.france-nuage.fr. To use app.your-domain.com:
1. Configure your DNS
Create a CNAME record (or A record via a DNS proxy) pointing to:
app.your-domain.com → zero-trust.france-nuage.fr
2. Update the Ingress
Replace the host field with your domain name:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- app.your-domain.com
secretName: my-app-tls
rules:
- host: app.your-domain.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: my-app
port:
number: 80
cert-manager handles the certificate for your domain the same way. The CNAME to zero-trust.france-nuage.fr routes traffic to the cluster's Ingress, and Kingress matches it using the Host header.
Troubleshooting
Silent 503 error
Your Service doesn't expose port 80. Check with:
kubectl get svc my-app -n my-namespace -o yaml
The ports[0].port field must be 80. If your app listens on 8080, use targetPort: 8080 with port: 80.
Certificate stays "Not Ready"
kubectl describe certificate my-app-tls -n my-namespace
kubectl describe certificaterequest -n my-namespace
kubectl describe order -n my-namespace
This usually means the DNS doesn't point to the cluster yet (propagation), or the Ingress host doesn't match the DNS record.
"Connection refused" on custom domain
"Connection refused" means the CNAME hasn't propagated yet. Check propagation using the following command:
dig app.your-domain.com CNAME
The response from dig should contain zero-trust.france-nuage.fr. DNS propagation takes anywhere from a few seconds to 48 hours depending on your registrar.
Full YAML example
Here is a complete Kubernetes deployment with Service and Ingress on a custom domain:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: registry.gitlab.com/my-org/my-app:latest
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 3000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- app.your-domain.com
secretName: my-app-tls
rules:
- host: app.your-domain.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: my-app
port:
number: 80
Going further
- Deploy to Kubernetes from GitLab CI - automate deployment with a pipeline
- cert-manager - official docs