Day: November 23, 2025
How to Deploy a Kubernetes Application with a Clean Namespace Structure
How to Deploy a Kubernetes Application with a Clean Namespace Structure
When you deploy an application to Kubernetes in production, you shouldn’t throw everything into the default namespace or a single giant YAML file. A proper setup uses:
- A dedicated namespace for the app
- A ServiceAccount and RBAC for security
- ConfigMap and Secret for configuration
- Deployment, Service, and Ingress for runtime and traffic
- HPA, PDB, and NetworkPolicies for reliability and security
HPA vs PDB (Summary)
========================
Feature HPA PDB Scales pods based on load ✔ YES ❌ NO Ensures minimum pods stay up ❌ NO ✔ YES Helps with traffic spikes ✔ YES ❌ NO Protects during upgrades/drains ❌ NO ✔ YES Operates on load (CPU/metrics) ✔ YES ❌ NO Operates on disruptions ❌ NO ✔ YES Controls min/max replicas ✔ YES ❌ NO Controls disruption limits ❌ NO ✔ YES
In this post, we’ll walk through a clean, real-world Kubernetes namespace structure, show the YAML for each section, and explain what it does. You can drop all of these files into a directory and apply them in one go with:
kubectl apply -f k8s/
1. Directory Structure
Create a folder for your Kubernetes manifests, for example:
k8s/
namespace.yaml
serviceaccount.yaml
rbac.yaml
configmap.yaml
secret.yaml
deployment.yaml
service.yaml
ingress.yaml
hpa.yaml
pdb.yaml
networkpolicy-default-deny.yaml
networkpolicy-allow-ingress.yaml
Kubernetes will treat all of these files as one desired state when you run kubectl apply -f k8s/, similar to how Terraform reads multiple .tf files in one directory.
2. Namespace – Isolating the Application
A namespace is a logical boundary in the cluster. Think of it as a dedicated “folder” for your application’s resources.
apiVersion: v1
kind: Namespace
metadata:
name: prod-app
labels:
name: prod-app
pod-security.kubernetes.io/enforce: "restricted"
pod-security.kubernetes.io/enforce-version: "latest"
What this does:
- Creates a namespace called
prod-app. - Applies Pod Security labels to enforce restricted policies.
- Gives you a clean way to separate dev, staging, and prod environments.
3. ServiceAccount – Identity for the Pods
A ServiceAccount represents the identity your pods use inside Kubernetes. Instead of relying on the default ServiceAccount, you create a dedicated one for your app.
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: prod-app
What this does:
- Creates a ServiceAccount named
app-sain theprod-appnamespace. - Your Deployment will run pods using this identity, not the insecure default.
4. RBAC – Roles and RoleBindings
RBAC (Role-Based Access Control) defines what your application is allowed to do inside the namespace. You don’t want your app to have full cluster access; you give it just enough permissions.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-read-config
namespace: prod-app
rules:
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-read-config-binding
namespace: prod-app
subjects:
- kind: ServiceAccount
name: app-sa
namespace: prod-app
roleRef:
kind: Role
name: app-read-config
apiGroup: rbac.authorization.k8s.io
What this does:
Roleapp-read-config:- Allows reading (
get,list) ConfigMaps and Secrets in this namespace.
- Allows reading (
RoleBinding:- Attaches that Role to the
app-saServiceAccount. - Any pod running as
app-sacan now read ConfigMaps and Secrets inprod-app.
- Attaches that Role to the
5. ConfigMap – Non-Sensitive Configuration
A ConfigMap holds non-secret configuration such as runtime flags, modes, switches, or log levels.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: prod-app
data:
APP_ENV: "production"
APP_LOG_LEVEL: "info"
What this does:
- Stores plain-text configuration for your application.
- Lets you change behavior without rebuilding the container image.
6. Secret – Sensitive Configuration
Secrets hold confidential settings such as database URLs, API keys, and credentials.
apiVersion: v1
kind: Secret
metadata:
name: app-secret
namespace: prod-app
type: Opaque
stringData:
DATABASE_URL: "postgres://user:password@db.prod:5432/app"
API_KEY_EXTERNAL_SERVICE: "replace-me"
What this does:
- Stores sensitive data separately from code.
- Works with RBAC so only the right ServiceAccount can read it.
7. Deployment – The Application Workload
The Deployment defines how your containers run: image, replicas, health checks, resources, and security context. This is the core of your application’s runtime behavior.
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
namespace: prod-app
labels:
app: my-app
spec:
replicas: 3
revisionHistoryLimit: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
serviceAccountName: app-sa
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
containers:
- name: app
image: your-registry/your-image:TAG
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: http
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secret
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
readinessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /livez
port: http
initialDelaySeconds: 15
periodSeconds: 20
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
terminationGracePeriodSeconds: 30
What this does:
- Runs 3 replicas of your application for availability.
- Uses a safe rolling update strategy with zero downtime (
maxUnavailable: 0,maxSurge: 1). - Runs pods under the
app-saServiceAccount, inheriting its RBAC permissions. - Injects configuration from
ConfigMapandSecret. - Defines health checks (
readinessProbe,livenessProbe) so Kubernetes knows when to route traffic and when to restart pods. - Applies strict security settings (non-root user, no privilege escalation, read-only root filesystem).
8. Service – Internal Load Balancer
A Service provides a stable, cluster-internal endpoint to reach your pods.
apiVersion: v1
kind: Service
metadata:
name: app-service
namespace: prod-app
labels:
app: my-app
spec:
type: ClusterIP
selector:
app: my-app
ports:
- name: http
port: 80
targetPort: http
What this does:
- Maps port 80 on the Service to port 8080 on the pods (via the named port
http). - Provides stable DNS:
app-service.prod-app.svc.cluster.local. - Load balances traffic across all healthy pods with
app: my-app.
9. Ingress – External HTTP/HTTPS Access
Ingress exposes your Service to the outside world using a hostname and optional TLS.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
namespace: prod-app
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- app.your-domain.com
secretName: app-tls-secret
rules:
- host: app.your-domain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-service
port:
number: 80
What this does:
- Routes traffic from
https://app.your-domain.comtoapp-serviceon port 80. - Uses
app-tls-secretfor TLS termination (usually created by cert-manager). - Relies on an Ingress controller (e.g., NGINX) running in the cluster.
10. Horizontal Pod Autoscaler (HPA) – Scaling on Load
The HPA automatically adjusts the number of replicas based on metrics like CPU usage.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: app-hpa
namespace: prod-app
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: app-deployment
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
What this does:
- Keeps at least 3 pods running, and can scale up to 10.
- Targets the
app-deploymentDeployment. - Scales based on CPU usage (e.g., above 60% average).
11. PodDisruptionBudget (PDB) – Protecting Availability
A PodDisruptionBudget ensures that voluntary disruptions (node drains, upgrades) don’t take down too many pods at once.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: app-pdb
namespace: prod-app
spec:
minAvailable: 2
selector:
matchLabels:
app: my-app
What this does:
- Guarantees that at least 2 pods are always available.
- Protects your app during maintenance and cluster upgrades.
12. Network Policies – Zero-Trust Networking
By default, Kubernetes allows every pod to talk to every other pod. NetworkPolicies let you move to a zero-trust model.
Default Deny Policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: prod-app
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
What this does:
- Blocks all ingress and egress traffic for all pods in the namespace, unless explicitly allowed.
Allow Traffic from Ingress Controller to the App
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-ingress
namespace: prod-app
spec:
podSelector:
matchLabels:
app: my-app
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
ports:
- protocol: TCP
port: 80
egress:
- to:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 5432
- protocol: TCP
port: 443
What this does:
- Allows only the Ingress controller namespace (e.g.
ingress-nginx) to send HTTP traffic to the app. - Restricts egress traffic to specific ports (e.g., PostgreSQL and HTTPS).
Applying Everything
Once you have all these files in your k8s/ directory, you deploy the entire application stack with:
kubectl apply -f k8s/
Kubernetes reads all files, builds the state internally, and creates or updates resources in the correct order, very similar to how Terraform applies all .tf files in a directory.
Conclusion
A production-ready Kubernetes deployment is not just a Deployment and a Service. It is a structured set of manifests that cover identity, security, configuration, scaling, networking, and reliability.
- Namespace – isolates your application.
- ServiceAccount + RBAC – define identity and permissions.
- ConfigMap + Secret – handle configuration and sensitive data.
- Deployment + Service + Ingress – run the app and expose it.
- HPA + PDB – keep it scalable and resilient.
- NetworkPolicies – secure communication with a zero-trust model.
With this structure in place, you have a clean, repeatable Kubernetes deployment that fits naturally into Git, CI/CD, and GitOps workflows.
