Deploying a MongoDB + Mongo-Express stack on a local Kubernetes cluster (Minikube) using Deployments, Services, ConfigMap, Secret, and Ingress — end to end.
Before jumping into the real deployment, I wanted a hands-on feel of every Kubernetes building block. The idea is simple:
- MongoDB runs inside the cluster and is reachable only internally (no external IP).
- Mongo-Express (the web UI) talks to MongoDB using an internal service, pulls the DB host from a ConfigMap, and credentials from a Secret.
- Mongo-Express is exposed to the outside world via an External Service and finally through an Ingress using a domain name.
Kubernetes hierarchy I kept in mind throughout:
Deployment → ReplicaSet → Pod → Container
Request flow I kept in mind throughout:
Client → Ingress → Service → Pod → Container
Before the real project I played around with plain Nginx and MySQL on Minikube to internalize the workflow.
minikube start
kubectl create deployment nginx-deployment --image=nginx
kubectl get deployment nginx-deployment -o yamlThen the same dance with MySQL — setting env variables, shelling into the Pod and exploring the DB:
kubectl exec -it mysql-deployment-5b4d884d47-ng5xz -- /bin/bash
mysql -u root -pFrom there I switched to declarative YAML files, tuned replicas, and watched Kubernetes reconcile everything automatically. That muscle memory is what made the actual project below feel natural.
You can find the practice files in this repo: nginx-deployment.yaml and nginx-service.yaml.
Credentials should never sit in plain YAML. A Secret holds the Mongo root username & password (base64 encoded) and both the MongoDB Pod and Mongo-Express Pod consume them.
File: mongo-secret.yaml
kubectl apply -f mongo-secret.yamlA single-replica Deployment running mongo:latest. The root credentials come straight from the Secret via secretKeyRef, so no hardcoding.
File: mongo.yaml — contains both the Deployment and its Service.
MongoDB must stay inside the cluster. Its Service is a plain ClusterIP (the default) — no nodePort, no LoadBalancer, no public exposure. Only other Pods in the cluster can reach it via mongodb-service:27017.
The URL of the MongoDB service is not a secret; it’s just configuration. A ConfigMap stores it so Mongo-Express can read ME_CONFIG_MONGODB_SERVER from it at runtime.
File: mongo-configmap.yaml
Mongo-Express is the web-based UI for MongoDB. Its Pod pulls:
- the DB host from the ConfigMap
- the DB username & password from the Secret
File: mongo-express.yaml
This is the one service that should be reachable from outside the cluster — so we turn it into a LoadBalancer (or NodePort in Minikube) to give it an External IP.
Key point: External IPs are only assigned to external services. Internal services (like MongoDB’s) never get one — and that’s exactly what we want.
After applying everything, a quick kubectl get all shows the full picture: two Deployments, two ReplicaSets, two Pods, two Services, plus the ConfigMap, Secret and Ingress.
Creating a database directly from the UI — proving the full Mongo-Express → internal service → MongoDB chain works:
Instead of hitting a raw IP and port, we put an Ingress in front of the external service so the app is reachable at http://mongo-express.com. The Ingress rules are handled by the Ingress Controller Pod (Nginx is the default in Kubernetes), which now becomes the single entry point into the cluster.
File: mongo-ingress.yaml
Enable the Nginx ingress addon on Minikube and add a host entry, then visit the domain:
minikube addons enable ingress
kubectl apply -f mongo-ingress.yaml
# Add to /etc/hosts: <minikube-ip> mongo-express.comApply the files in order (secret first, because the Pods reference it):
kubectl apply -f mongo-secret.yaml
kubectl apply -f mongo.yaml
kubectl apply -f mongo-configmap.yaml
kubectl apply -f mongo-express.yaml
kubectl apply -f mongo-ingress.yaml
kubectl get all
minikube service mongo-express-service # opens the UI in the browserTear it all down just as cleanly:
kubectl delete -f mongo-ingress.yaml -f mongo-express.yaml -f mongo-configmap.yaml -f mongo.yaml -f mongo-secret.yaml| File | Purpose |
|---|---|
mongo-secret.yaml |
MongoDB root username & password (base64) |
mongo.yaml |
MongoDB Deployment + internal ClusterIP Service |
mongo-configmap.yaml |
Non-sensitive config (DB service URL for Mongo-Express) |
mongo-express.yaml |
Mongo-Express Deployment + external Service |
mongo-ingress.yaml |
Ingress exposing Mongo-Express at mongo-express.com |
nginx-deployment.yaml / nginx-service.yaml |
Warm-up practice manifests |
assets/ |
Architecture diagrams & screenshots used above |
- Deployment > ReplicaSet > Pod > Container — always the same hierarchy.
- A request never hits a Pod directly — it goes through a Service first.
- ConfigMap for plain config, Secret for credentials — never hardcode either into a Pod spec.
- Only the services that truly need to be public get an External IP. Everything else stays
ClusterIP. - Ingress turns a raw IP:Port into a clean, domain-based entry point — and the Ingress Controller Pod becomes the cluster’s front door.









