Namespaces partition a single Kubernetes cluster into multiple virtual clusters. Combined with Kubernetes DNS-based service discovery, namespaces enable multi-tenant workloads, resource isolation, and clear service communication patterns.
Namespaces
A namespace provides a scope for names, resource quotas, and access control. Resources in one namespace are isolated from resources in another (except at the network level, by default).
Default Namespaces
Every GKE cluster starts with four namespaces:
| Namespace | Purpose |
|---|---|
default | For resources with no namespace specified |
kube-system | Kubernetes system components (kube-dns, kube-proxy, metrics-server) |
kube-public | Public, readable data (cluster config) |
kube-node-lease | Node heartbeat lease objects |
# List all namespaces
kubectl get namespaces
# View resources in a specific namespace
kubectl get pods -n kube-systemCreating Namespaces
# Create a namespace
kubectl create namespace staging
# Create from YAML
kubectl apply -f - <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: staging
labels:
environment: staging
team: backend
EOFWorking with Namespaces
# Set default namespace for current context
kubectl config set-context --current --namespace=staging
# Specify namespace per command
kubectl get pods -n staging
kubectl apply -f deployment.yaml -n staging
# List all pods across all namespaces
kubectl get pods -ATip: Set your default namespace to avoid accidentally deploying to
default. Usekubectl config set-context --current --namespace=staging.
When to Use Namespaces
| Use Case | Example |
|---|---|
| Multiple environments | dev, staging, production |
| Multi-tenant applications | tenant-a, tenant-b |
| Team isolation | backend, frontend, data-team |
| Resource quotas | Limit CPU/memory per environment |
| Access control | Restrict teams to their namespaces via RBAC |
When NOT to Use Namespaces
- Different clusters are needed (different security requirements, different cloud regions)
- The overhead of namespace management outweighs the benefit (small teams, simple apps)
- You need hard security isolation (namespaces are a logical boundary, not a security boundary)
Warning: Namespaces do NOT provide network isolation by default. Pods in any namespace can communicate with pods in any other namespace. Use Network Policies for network-level isolation.
Resource Quotas
Resource Quotas limit the total resources a namespace can consume:
apiVersion: v1
kind: ResourceQuota
metadata:
name: staging-quota
namespace: staging
spec:
hard:
requests.cpu: "4" # Max 4 vCPU total across all pods
requests.memory: 8Gi # Max 8 GB memory total
limits.cpu: "8"
limits.memory: 16Gi
pods: "20" # Max 20 pods
services: "10" # Max 10 services
persistentvolumeclaims: "5" # Max 5 PVCs# Apply the quota
kubectl apply -f quota.yaml
# Check quota usage
kubectl get resourcequota -n staging
kubectl describe resourcequota staging-quota -n stagingQuota Enforcement
| Scenario | What Happens |
|---|---|
| Pod exceeds quota | Pod creation fails with Forbidden: exceeded quota |
| All quotas consumed | No new pods can be created until existing pods are deleted |
| Quota increased | Pending pods can now be scheduled |
Key Insight: When a ResourceQuota is applied to a namespace, every pod MUST define
resources.requests. Pods without resource requests are rejected.
LimitRanges
LimitRanges set default, minimum, and maximum resource values per container in a namespace:
apiVersion: v1
kind: LimitRange
metadata:
name: staging-limits
namespace: staging
spec:
limits:
- type: Container
default: # Default limits if not specified
cpu: "500m"
memory: "256Mi"
defaultRequest: # Default requests if not specified
cpu: "100m"
memory: "64Mi"
max: # Maximum allowed
cpu: "2"
memory: "2Gi"
min: # Minimum allowed
cpu: "50m"
memory: "32Mi"# Apply and check
kubectl apply -f limitrange.yaml -n staging
kubectl describe limitrange staging-limits -n staging| LimitRange Field | Purpose |
|---|---|
default | Applied as resources.limits when not specified |
defaultRequest | Applied as resources.requests when not specified |
max | Upper bound — any value above is rejected |
min | Lower bound — any value below is rejected |
Network Policies
Network Policies control pod-to-pod communication at the network level. By default, all pods can communicate with all other pods.
Enable Network Policy in GKE
# For Standard clusters
gcloud container clusters update my-cluster \
--zone=us-central1-a \
--enable-network-policy
# Autopilot clusters have network policy available by defaultNetwork Policy Example
# Deny all ingress to the staging namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: staging
spec:
podSelector: {} # Select all pods
policyTypes:
- Ingress # Block all incoming traffic
---
# Allow only frontend pods to talk to backend pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: staging
spec:
podSelector:
matchLabels:
tier: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
tier: frontend
ports:
- port: 8080# Apply and check
kubectl apply -f networkpolicy.yaml -n staging
kubectl get networkpolicies -n staging| Policy Type | Controls |
|---|---|
Ingress | Incoming traffic to selected pods |
Egress | Outgoing traffic from selected pods |
| Both | Full network isolation |
Key Insight: Network Policies are additive. Start with a deny-all policy, then add specific allow rules. This is the “default deny” security model.
Service Discovery (DNS)
Kubernetes provides built-in DNS records for every Service. In GKE, Standard clusters use kube-dns by default, while Autopilot clusters use Cloud DNS by default.
DNS Format
SERVICE_NAME.NAMESPACE.svc.cluster.local| Format | Scope | Example |
|---|---|---|
service | Same namespace | backend |
service.namespace | Different namespace | backend.staging |
service.namespace.svc | Explicit service subdomain | backend.staging.svc |
service.namespace.svc.cluster.local | Full FQDN | backend.staging.svc.cluster.local |
How DNS Resolution Works
flowchart LR Pod["Pod in namespace: production"] -->|curl http://backend| DNS["Cluster DNS"] DNS -->|backend.production.svc.cluster.local| SVC["Service: backend"] SVC --> P1["Pod 10.4.0.12"] SVC --> P2["Pod 10.4.0.23"]
# Test DNS resolution from inside a pod
kubectl exec -it my-pod -- nslookup backend
kubectl exec -it my-pod -- nslookup backend.staging
kubectl exec -it my-pod -- nslookup backend.staging.svc.cluster.localCross-Namespace Communication
By default, pods can reach services in other namespaces using the full DNS name:
# From a pod in the 'production' namespace
curl http://backend.staging:8080/api
curl http://shared-services.shared:3306# Reference a service in another namespace
# In a ConfigMap or environment variable
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
STAGING_API_URL: "http://backend.staging:8080"
SHARED_DB_HOST: "database.shared-services"Tip: Use short names within the same namespace and namespace-qualified names across namespaces. This makes the communication intent clear.
Headless Services and Pod DNS
For StatefulSets with headless services, each pod gets its own DNS name:
POD_NAME.SERVICE_NAME.NAMESPACE.svc.cluster.local# Example: Kafka StatefulSet pods
kafka-0.kafka.default.svc.cluster.local
kafka-1.kafka.default.svc.cluster.local
kafka-2.kafka.default.svc.cluster.localNamespace Strategy for GKE
Strategy 1: Environment-Based Namespaces
default/ # → avoid using
production/ # production workloads
staging/ # pre-production testing
development/ # development and testingStrategy 2: Team-Based Namespaces
default/
frontend-team/ # frontend services
backend-team/ # backend services
data-team/ # data processing services
shared-services/ # databases, message queues, monitoringStrategy 3: Combined (Recommended for Larger Teams)
production/
├── production-frontend/
├── production-backend/
└── production-data/
staging/
├── staging-frontend/
├── staging-backend/
└── staging-data/| Strategy | Pros | Cons |
|---|---|---|
| Environment-based | Simple, clear boundaries | Hard to isolate teams within an environment |
| Team-based | Team autonomy, clear ownership | No environment separation |
| Combined | Full isolation | More namespaces to manage |
RBAC and Namespace Access
Control who can do what in each namespace:
# Role: what can be done in a namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
namespace: staging
rules:
- apiGroups: ["", "apps"]
resources: ["pods", "deployments", "services", "configmaps"]
verbs: ["get", "list", "watch", "create", "update", "delete"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"] # Read-only secrets
---
# RoleBinding: who gets the role
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: developer-binding
namespace: staging
subjects:
- kind: User
name: [email protected]
roleRef:
kind: Role
name: developer
apiGroup: rbac.authorization.k8s.io| RBAC Concept | Scope | Use Case |
|---|---|---|
| Role + RoleBinding | Single namespace | Team member access to their namespace |
| ClusterRole + ClusterRoleBinding | Cluster-wide | Admin access, viewing node info |
| ClusterRole + RoleBinding | RoleBinding restricts to a namespace | Same ClusterRole used in multiple namespaces |
Common Commands
| Command | Purpose |
|---|---|
kubectl get namespaces | List all namespaces |
kubectl create namespace NAME | Create a namespace |
kubectl get pods -n NAME | List pods in a namespace |
kubectl get pods -A | List pods in all namespaces |
kubectl config set-context --current --namespace=NAME | Set default namespace |
kubectl get resourcequota -n NAME | View resource quotas |
kubectl get limitrange -n NAME | View limit ranges |
kubectl get networkpolicies -n NAME | View network policies |
kubectl exec -it POD -- nslookup SERVICE | Test DNS resolution |
kubectl get events -n NAME --sort-by='.lastTimestamp' | View namespace events |
Common Pitfalls
| Pitfall | Consequence | Fix |
|---|---|---|
Deploying everything to default | No isolation, hard to manage | Create dedicated namespaces per environment/team |
| No ResourceQuotas | One team can consume all cluster resources | Apply ResourceQuotas to every namespace |
Forgetting -n flag | Operating on wrong namespace | Set default namespace or always specify -n |
| Assuming namespace = network isolation | All pods can communicate across namespaces | Use Network Policies for network isolation |
| No LimitRange | Pods without resources.requests rejected when quota exists | Apply LimitRange to auto-set defaults |
| Hardcoded namespace in YAML | Manifests not portable across namespaces | Use kubectl apply -f -n TARGET_NS or Kustomize |
TL;DR
- Namespaces partition a cluster for multi-tenancy, environments, and teams
- Use ResourceQuotas to limit resources per namespace and LimitRanges to set defaults
- Namespaces do NOT provide network isolation — use Network Policies for that
- Kubernetes DNS enables automatic service discovery within and across namespaces
- Use short DNS names within a namespace (
backend) and namespace-qualified names across namespaces (backend.staging) - Start with a default-deny Network Policy and add specific allow rules
- Use RBAC to control access to namespaces per team