In this episode, we'll discuss an important concept in Kubernetes called Namespaces. We'll learn how to use Namespaces to divide cluster resources between multiple users or teams.

Note
If you want to read the previous episode, you can click the Episode 7 thumbnail below
In the previous episode, we learned deeply about the Annotation object in Kubernetes. In episode 8, we'll discuss another fundamental concept in Kubernetes: Namespaces.
Note: Here I'll be using a Kubernetes Cluster installed through K3s.
Namespaces are a way to divide cluster resources between multiple users, teams, or projects. They provide a scope for names and allow you to organize and isolate resources within a single cluster. Understanding Namespaces is crucial for managing multi-tenant clusters and organizing resources effectively.
A Namespace is a virtual cluster within a physical Kubernetes cluster. It provides a way to divide cluster resources between multiple users or teams. Namespaces are intended for use in environments with many users spread across multiple teams or projects.
Think of Namespaces like folders on your computer. Just as folders help you organize files into logical groups, Namespaces help you organize Kubernetes resources into logical groups. For example, you might have separate Namespaces for development, staging, and production environments, or separate Namespaces for different teams or projects.
Namespaces provide:
Key characteristics of Namespaces:
You might wonder why we need Namespaces when we already have Labels for organizing resources. The key differences are:
Consider this scenario: You have a development team and a production team both deploying applications to the same cluster. Without Namespaces, they might accidentally overwrite each other's resources if they use the same names. With Namespaces, each team can have their own isolated environment within the same cluster.
Another important use case is resource quotas. You can set limits on how much CPU, memory, and storage each Namespace can use. This prevents one team or project from consuming all cluster resources.
When you create a Kubernetes cluster, it comes with several default Namespaces:
The default Namespace is where resources are created if you don't specify a Namespace. When you run kubectl get pods without specifying a Namespace, you're querying the default Namespace.
# This queries the default Namespace
sudo kubectl get podsThe kube-system Namespace contains resources created by the Kubernetes system itself. This includes system components like the DNS server, metrics server, and other control plane components.
# View system components
sudo kubectl get pods -n kube-systemWarning
Be careful when working with resources in the kube-system Namespace. Deleting or modifying these resources can break your cluster.
The kube-public Namespace is readable by all users (including unauthenticated users). It's typically used for resources that should be publicly accessible across the cluster.
# View public resources
sudo kubectl get all -n kube-publicThe kube-node-lease Namespace holds lease objects associated with each node. Node leases allow the kubelet to send heartbeats so the control plane can detect node failures.
# View node leases
sudo kubectl get leases -n kube-node-leaseLet's learn how to view Namespaces in your cluster:
To view all Namespaces in your cluster:
sudo kubectl get namespacesOr use the shorthand:
sudo kubectl get nsThe output will look like this:
NAME STATUS AGE
default Active 45d
kube-node-lease Active 45d
kube-public Active 45d
kube-system Active 45dTo see detailed information about a specific Namespace:
sudo kubectl describe namespace defaultThe output will show:
Name: default
Labels: kubernetes.io/metadata.name=default
Annotations: <none>
Status: Active
No resource quota.
No LimitRange resource.To view resources in a specific Namespace, use the -n or --namespace flag:
# View Pods in kube-system Namespace
sudo kubectl get pods -n kube-system
# View all resources in a Namespace
sudo kubectl get all -n kube-systemTo view resources across all Namespaces, use the --all-namespaces or -A flag:
# View all Pods in all Namespaces
sudo kubectl get pods --all-namespaces
# Or use the shorthand
sudo kubectl get pods -AThere are two main ways to create Namespaces:
The quickest way to create a Namespace is using the kubectl create namespace command:
sudo kubectl create namespace developmentOr use the shorthand:
sudo kubectl create ns stagingYou can also create a Namespace using a YAML configuration file:
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
environment: production
team: platformApply the configuration:
sudo kubectl apply -f namespace.ymlYou can add Labels and Annotations to Namespaces for better organization:
apiVersion: v1
kind: Namespace
metadata:
name: backend-team
labels:
team: backend
environment: production
cost-center: engineering
annotations:
description: "Namespace for backend team production workloads"
contact: "backend-team@company.com"
created-by: "platform-team"When creating resources, you can specify the Namespace in two ways:
Method 1: In the YAML configuration
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
namespace: development
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80Method 2: Using kubectl flag
sudo kubectl apply -f pod.yml -n developmentInstead of specifying the Namespace every time, you can set a default Namespace for your kubectl context:
# Set default Namespace to development
sudo kubectl config set-context --current --namespace=developmentNow all kubectl commands will use the development Namespace by default:
# This will query the development Namespace
sudo kubectl get podsTo switch back to the default Namespace:
sudo kubectl config set-context --current --namespace=defaultTo see which Namespace is currently set as default:
sudo kubectl config view --minify | grep namespace:Let's create a practical example where we set up multiple Namespaces for different environments:
Create a file named environments.yml:
apiVersion: v1
kind: Namespace
metadata:
name: development
labels:
environment: development
annotations:
description: "Development environment"
---
apiVersion: v1
kind: Namespace
metadata:
name: staging
labels:
environment: staging
annotations:
description: "Staging environment"
---
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
environment: production
annotations:
description: "Production environment"Apply the configuration:
sudo kubectl apply -f environments.ymlCreate a file named app-deployments.yml:
# Development deployment
apiVersion: v1
kind: Pod
metadata:
name: web-app
namespace: development
labels:
app: web
environment: development
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
---
# Staging deployment
apiVersion: v1
kind: Pod
metadata:
name: web-app
namespace: staging
labels:
app: web
environment: staging
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
---
# Production deployment
apiVersion: v1
kind: Pod
metadata:
name: web-app
namespace: production
labels:
app: web
environment: production
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80Apply the configuration:
sudo kubectl apply -f app-deployments.ymlNow we have the same Pod name (web-app) in three different Namespaces:
# View Pods in development
sudo kubectl get pods -n development
# View Pods in staging
sudo kubectl get pods -n staging
# View Pods in production
sudo kubectl get pods -n production
# View all Pods across all Namespaces
sudo kubectl get pods -A | grep web-appThe output will show:
NAMESPACE NAME READY STATUS RESTARTS AGE
development web-app 1/1 Running 0 2m
staging web-app 1/1 Running 0 2m
production web-app 1/1 Running 0 2mTip
Notice how we can have resources with the same name in different Namespaces. This is one of the key benefits of using Namespaces.
Kubernetes DNS automatically creates DNS records for Services. The DNS name includes the Namespace, allowing Services to communicate across Namespaces.
Services get DNS names in this format:
<service-name>.<namespace>.svc.cluster.localFor example:
web-service.development.svc.cluster.localweb-service.production.svc.cluster.localWithin the same Namespace, you can use the short name:
Tip
To try to access the domain, you can enter the pod/container shell by running the following command:
sudo kubectl exec -it web-app -n development -- bashAfter that, you can install network tools such as dnsutils, iputils-ping, and curl
apt update
apt install dnsutils iputils-ping curl -ySo the result when an http request uses curl is to return the HTML content from the nginx page.
# Method 1
curl http://web-service
# Method 2
curl http://web-service.production
# Method 3
curl http://web-service.production.svc.cluster.localcurl http://web-serviceAcross Namespaces, you need to use the full DNS name:
curl http://web-service.production.svc.cluster.localCreate Services in different Namespaces:
apiVersion: v1
kind: Service
metadata:
name: web-service
namespace: development
spec:
selector:
app: web
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: web-service
namespace: production
spec:
selector:
app: web
ports:
- protocol: TCP
port: 80
targetPort: 80Now a Pod in the development Namespace can access the Service in the production Namespace:
# From a Pod in development Namespace
curl http://web-service.production.svc.cluster.localOne of the most powerful features of Namespaces is the ability to set resource quotas. This prevents any single Namespace from consuming all cluster resources.
Create a file named resource-quota.yml:
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: development
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
pods: "10"This quota limits the development Namespace to:
Apply the quota:
sudo kubectl apply -f resource-quota.ymlTo view resource quotas in a Namespace:
sudo kubectl get resourcequota -n developmentFor detailed information:
sudo kubectl describe resourcequota compute-quota -n developmentThe output will show:
Name: compute-quota
Namespace: development
Resource Used Hard
-------- ---- ----
limits.cpu 0 8
limits.memory 0 16Gi
pods 1 10
requests.cpu 0 4
requests.memory 0 8GiWhen you try to create a Pod that exceeds the quota, it will be rejected:
apiVersion: v1
kind: Pod
metadata:
name: large-pod
namespace: development
spec:
containers:
- name: app
image: nginx
resources:
requests:
cpu: "5"
memory: 10Gi
limits:
cpu: "10"
memory: 20GiApplying this will fail:
sudo kubectl apply -f large-pod.ymlError message:
Error from server (Forbidden): error when creating "large-pod.yml": pods "large-pod" is forbidden: exceeded quota: compute-quota, requested: requests.cpu=5,requests.memory=10Gi, used: requests.cpu=0,requests.memory=0, limited: requests.cpu=4,requests.memory=8GiLimitRange allows you to set default resource limits for containers in a Namespace. This is useful when developers forget to specify resource limits.
Create a file named limit-range.yml:
apiVersion: v1
kind: LimitRange
metadata:
name: resource-limits
namespace: development
spec:
limits:
- default:
cpu: "500m"
memory: 512Mi
defaultRequest:
cpu: "250m"
memory: 256Mi
max:
cpu: "2"
memory: 2Gi
min:
cpu: "100m"
memory: 128Mi
type: ContainerThis LimitRange sets:
Apply the LimitRange:
sudo kubectl apply -f limit-range.ymlNow any Pod created in the development Namespace without resource specifications will automatically get these defaults.
To delete a Namespace:
sudo kubectl delete namespace developmentWarning
Deleting a Namespace will delete ALL resources within it. This action cannot be undone. Always double-check before deleting a Namespace.
You can also delete using a YAML file:
sudo kubectl delete -f namespace.ymlWhen you delete a Namespace:
Terminating stateYou can check the status:
sudo kubectl get namespace developmentOutput during deletion:
NAME STATUS AGE
development Terminating 5mForgetting to specify the Namespace when working with resources can lead to confusion.
Problem: You create a resource but can't find it because it's in a different Namespace.
Solution: Always specify the Namespace explicitly or set a default Namespace for your context.
Namespaces provide logical isolation, not network isolation. Pods in different Namespaces can still communicate by default.
Solution: Use NetworkPolicies for actual network isolation between Namespaces.
Creating too many Namespaces can make cluster management complex.
Solution: Use Namespaces for major divisions (teams, environments) and use Labels for finer-grained organization.
Without resource quotas, one Namespace can consume all cluster resources.
Solution: Always set ResourceQuotas for production Namespaces.
Hardcoding Namespace names in application code makes it difficult to deploy to different environments.
Solution: Use environment variables or configuration files to specify Namespace names.
Create separate Namespaces for different environments:
sudo kubectl create namespace dev
sudo kubectl create namespace staging
sudo kubectl create namespace prodCreate separate Namespaces for different teams:
sudo kubectl create namespace team-backend
sudo kubectl create namespace team-frontend
sudo kubectl create namespace team-dataSet resource quotas for every Namespace to prevent resource exhaustion:
apiVersion: v1
kind: ResourceQuota
metadata:
name: namespace-quota
namespace: <namespace-name>
spec:
hard:
requests.cpu: "10"
requests.memory: 20Gi
limits.cpu: "20"
limits.memory: 40Gi
pods: "50"
services: "20"
persistentvolumeclaims: "10"Add Labels to Namespaces for better organization:
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
environment: production
team: platform
cost-center: engineering
compliance: pci-dssUse Annotations to document the purpose of each Namespace:
apiVersion: v1
kind: Namespace
metadata:
name: production
annotations:
description: "Production environment for customer-facing applications"
contact: "platform-team@company.com"
oncall: "https://oncall.company.com/platform"
runbook: "https://runbook.company.com/production"Establish consistent naming conventions for Namespaces:
<team>-<environment> (e.g., backend-prod, frontend-dev)<project>-<environment> (e.g., web-app-staging, api-prod)<environment> (e.g., development, staging, production)Use Role-Based Access Control (RBAC) to control who can access each Namespace:
apiVersion: v1
kind: Namespace
metadata:
name: team-backend
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
namespace: team-backend
rules:
- apiGroups: ["", "apps"]
resources: ["pods", "deployments", "services"]
verbs: ["get", "list", "create", "update", "delete"]While Namespaces are useful, there are cases where you shouldn't use them:
In episode 8, we've explored the Namespace concept in Kubernetes in depth. We've learned what Namespaces are, why they're important, how to create and manage them, and best practices for using them effectively.
Namespaces are a fundamental feature for organizing and isolating resources in Kubernetes clusters. They enable multi-tenancy, resource quotas, and logical separation of environments. By understanding Namespaces, you can manage complex clusters with multiple teams, projects, and environments more effectively.
Key takeaways:
Are you getting a clearer understanding of Namespaces in Kubernetes? In the next episode 9, we'll discuss Pod Deletion.
Note
If you want to continue reading, you can click the Episode 9 thumbnail below