Part of the series Kubernetes basics
Kubernetes ConfigMaps, NGINX, and TLS: A simple guide
A ConfigMap stores the settings. Kubernetes delivers them. NGINX must read them. Follow the whole journey with an end-to-end diagram and a small hands-on example.
Imagine a restaurant. The kitchen is your application. The recipe tells the kitchen how to prepare a meal.
Changing the recipe on the noticeboard does not mean the cook has read it yet.
That is the main idea behind Kubernetes ConfigMaps: saving new settings and using new settings are two different steps.
The pieces in plain language
| Piece | Think of it as… | What it does |
|---|---|---|
| Container image | The kitchen equipment | Packages the application and what it needs to run. |
| ConfigMap | The recipe sheet | Stores settings that are safe to share. |
| Secret | A restricted drawer | Holds sensitive values, such as passwords and TLS private keys. |
| Pod | A running kitchen | Runs one or more containers. |
| Deployment | The kitchen manager | Keeps the desired number of Pods running through ReplicaSets. |
| Service | A stable front door | Directs traffic to the application’s ready Pod endpoints. |
| NGINX | The server at the counter | Serves web content or forwards requests to another application. |
| TLS | A protected conversation | Encrypts the connection used by HTTPS. |
A ConfigMap can hold a port number, a log level, or an entire configuration file. Keep passwords and private keys in a Secret instead. A Secret still needs access controls; base64 encoding is not encryption.
Follow the end-to-end flow
The diagram separates configuration delivery from website traffic. Read the top path first, then the bottom path. On a phone, swipe sideways inside the diagram to see the full flow.
Open the full end-to-end diagram.
In the top path, you save settings in a ConfigMap. Kubernetes makes them available as files inside the Pod. NGINX reads those files when it starts or reloads.
In the bottom path, a browser connects to an externally reachable Service address. Traffic reaches NGINX in a Pod, where TLS is handled in this example. NGINX sends the response back over the connection.
A ConfigMap is not a network stop. Browser requests do not pass through it. Also, TLS may end at an Ingress controller or load balancer in other designs. Change TLS settings on the component that actually handles that connection.
Try a small ConfigMap example
Start with plain HTTP so you can learn configuration delivery without also setting up certificates. You need kubectl connected to a Kubernetes cluster and permission to create resources in a namespace named demo.
Save this as nginx-demo.yaml, or download the complete example.
apiVersion: v1
kind: Namespace
metadata:
name: demo
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-settings
namespace: demo
data:
default.conf: |
server {
listen 8080;
location / {
default_type text/plain;
return 200 "Hello from ConfigMap v1\n";
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-web
namespace: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo-web
template:
metadata:
labels:
app: demo-web
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /
port: 8080
volumeMounts:
- name: settings
mountPath: /etc/nginx/conf.d
readOnly: true
volumes:
- name: settings
configMap:
name: nginx-settings
---
apiVersion: v1
kind: Service
metadata:
name: demo-web
namespace: demo
spec:
selector:
app: demo-web
ports:
- port: 80
targetPort: 8080
The ConfigMap key default.conf becomes a file at /etc/nginx/conf.d/default.conf. NGINX loads files from that directory. Mounting a directory hides the image’s original files at that location, so supply all the files you need there.
The Service receives traffic on port 80 and targets the Pod on port 8080. This example’s Service is internal; it does not create a public address.
Apply it, wait for the rollout, and open a local tunnel:
kubectl apply -f nginx-demo.yaml
kubectl rollout status deployment/demo-web -n demo
kubectl port-forward service/demo-web 8080:80 -n demo
Leave that terminal running. In another terminal:
curl http://localhost:8080
You should see Hello from ConfigMap v1. This checks the application through a local port-forward; it does not test public exposure or the normal Service network path.
The floating nginx:stable tag keeps this learning example short. For a repeatable release, choose and pin a tested image version or digest.
What happens when you change the ConfigMap?
In your YAML file, change Hello from ConfigMap v1 to Hello from ConfigMap v2, then apply it again:
kubectl apply -f nginx-demo.yaml
Kubernetes saves the new value. With this normal directory mount, the file inside the Pod updates eventually. NGINX continues using its loaded settings until it reloads or restarts.
Check whether the new text has reached the mounted file:
kubectl exec -n demo deployment/demo-web -- \
cat /etc/nginx/conf.d/default.conf
Wait until that file shows v2. Then validate the files and ask NGINX to reload:
kubectl exec -n demo deployment/demo-web -- nginx -t
# Continue only if the syntax check succeeds.
kubectl exec -n demo deployment/demo-web -- nginx -s reload
curl http://localhost:8080
The response should now contain v2. These commands target one Pod; that is enough for this one-replica example. With multiple replicas, each NGINX process needs the new configuration and a reload, or you can use a controlled rollout.
A reload keeps the Pod and asks NGINX to reread its files. A rolling restart replaces the Deployment’s Pods:
kubectl rollout restart deployment/demo-web -n demo
kubectl rollout status deployment/demo-web -n demo
If you use a restart, reconnect the port-forward after the old Pod exits. Availability during a rollout depends on readiness, capacity, replica count, and the update strategy.
Three update rules to remember
| How the setting reaches the container | What a ConfigMap edit does |
|---|---|
| Environment variable | Does not update the running process’s environment. Replace the Pod to pick it up. |
| Normal ConfigMap volume | Files update eventually. The application may need a reload. |
A file mounted with subPath |
The existing mount does not receive ConfigMap updates. Replace the Pod. |
Updating a ConfigMap alone does not trigger a Deployment rollout. These differences are documented in the Kubernetes ConfigMap guide.
Where HTTPS and TLS fit
HTTP carries web requests and responses. HTTPS carries HTTP over a connection protected by TLS.
For a usual HTTPS website, a certificate helps the browser check the server’s identity. The matching private key stays on the server. TLS also protects data from being read or changed in transit.
Here is a small NGINX HTTPS configuration fragment. It is separate from the HTTP lab above:
server {
listen 443 ssl;
server_name shop.example.com;
ssl_certificate /etc/nginx/tls/tls.crt;
ssl_certificate_key /etc/nginx/tls/tls.key;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
return 200 "Hello over HTTPS\n";
}
}
The ssl_protocols line allows TLS 1.2 and TLS 1.3. The client and server need a version and other connection settings they both support.
The fragment alone will not enable HTTPS in the lab. You also need a valid certificate for your hostname, its matching private key mounted from a Secret, and a Service port pointing to NGINX’s port 443. Public access needs a reachable entry point and DNS as well. See the NGINX SSL module documentation.
To check an actual HTTPS endpoint, replace the example hostname with yours:
curl -v --tlsv1.2 --tls-max 1.2 https://shop.example.com/
curl -v --tlsv1.3 --tls-max 1.3 https://shop.example.com/
These force an exact TLS version. Your local curl must support the requested version, and certificate verification must succeed. A failed request can also mean DNS, networking, or certificate trouble; inspect the error before blaming the protocol setting.
nginx -t checks the configuration files. It does not prove clients can reach the website. Test the actual endpoint after a reload, and remember that a proxy in front may handle TLS instead of your NGINX Pod.
Immutable means the settings are locked
An immutable ConfigMap blocks changes to its data and binaryData. Set immutable: true at the top level:
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-settings-v1
namespace: demo
immutable: true
data:
LOG_LEVEL: "info"
This is a separate example of the lock. NGINX does not automatically use this LOG_LEVEL key; an application must explicitly read a setting for it to have an effect.
You cannot turn the lock off or edit that object’s data afterward. An authorized user can still delete it, and immutability does not encrypt it.
For the next release, create a new ConfigMap, such as nginx-settings-v2, containing the full new configuration. Change the Deployment’s volumes[].configMap.name to that name and apply the Deployment. Changing the Pod template triggers a rollout.
Keep the previous ConfigMap while you may still need to roll back. A Deployment rollback does not restore ConfigMap data that you edited or deleted. Versioned names make it easier to keep each release’s settings together.
A simple troubleshooting order
Follow the same direction as the diagram:
- Stored settings: inspect
kubectl get configmap nginx-settings -n demo -o yaml. - Delivered file: read the mounted
default.confinside the Pod. - Application: run
nginx -t, reload if needed, and inspectkubectl logs -n demo deployment/demo-web. - Traffic: check
kubectl get pods,service,endpointslices -n demo, then test the response. - HTTPS: identify where TLS ends, then check DNS, certificate validity, and the negotiated version.
The question at each step is small: Is this layer doing its job?
When you finish the HTTP lab, stop the port-forward with Ctrl+C. To remove only the lab workload resources:
kubectl delete service/demo-web deployment/demo-web configmap/nginx-settings -n demo
Remember the recipe sheet: store the settings → deliver the file → let NGINX read it → test the result. For a release you want to keep fixed, use a versioned immutable ConfigMap.