You can’t actually isolate Dev and Prod workloads in Rancher by simply putting them in different projects.

Here’s what that looks like in practice:

apiVersion: v1
kind: Pod
metadata:
  name: dev-app-pod
  namespace: dev-ns
  labels:
    app: my-dev-app
spec:
  containers:
  - name: main
    image: nginx:latest
    ports:
    - containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
  name: prod-app-pod
  namespace: prod-ns
  labels:
    app: my-prod-app
spec:
  containers:
  - name: main
    image: nginx:latest
    ports:
    - containerPort: 80

In Rancher, projects are a logical grouping mechanism, not a hard security boundary. They help organize resources and apply RBAC within a cluster. But by default, all projects within the same cluster share the same underlying Kubernetes API server, etcd, and node resources. This means a misconfiguration, a resource exhaustion event, or even a compromised pod in your "dev" project can absolutely impact your "prod" project if they reside on the same cluster.

The core problem is that "isolation" implies a strong boundary, and Rancher’s project abstraction, while useful for organization and access control, doesn’t provide that inherent environmental separation at the cluster level.

So, how do you actually achieve meaningful separation? You need to leverage Kubernetes primitives that create distinct environments. The most straightforward and common way is to use separate Kubernetes clusters.

Method 1: Separate Clusters

This is the gold standard for true isolation. Each cluster is an independent Kubernetes control plane and node pool.

  • Diagnosis: Check your Rancher UI. If your "dev" and "prod" projects are listed under the same cluster, they are not isolated.
  • Fix:
    1. In Rancher, navigate to Cluster Management.
    2. Click Create Cluster.
    3. Choose your infrastructure provider (e.g., RKE1, RKE2, K3s, cloud provider) and configure it for your dev environment.
    4. Repeat steps 2-3 for your prod environment.
    5. Once both clusters are provisioned, create a "dev" project in the dev cluster and a "prod" project in the prod cluster.
  • Why it works: Each cluster has its own distinct API server, etcd, and scheduler. A runaway process or resource leak in the dev cluster cannot affect the prod cluster because they are entirely separate Kubernetes control planes. Node resources are also segregated.

Method 2: Namespaces with Resource Quotas and Network Policies (Less Isolation)

If separate clusters are cost-prohibitive, you can achieve some level of separation within a single cluster using namespaces, combined with strict resource management and network controls. This is not true isolation, but it’s a significant improvement over just using Rancher projects.

  • Diagnosis: You’re using different namespaces for dev and prod, but you don’t have ResourceQuota or NetworkPolicy objects defined for these namespaces. You can also check if pods from dev-ns can curl pods in prod-ns directly.
  • Fix:
    1. Namespaces: Ensure you have distinct namespaces, e.g., dev-ns and prod-ns. Rancher projects map to these namespaces.
    2. Resource Quotas: Apply ResourceQuota objects to each namespace to limit CPU, memory, storage, and object counts.
      • Example for dev-ns:
        apiVersion: v1
        kind: ResourceQuota
        metadata:
          name: dev-quota
          namespace: dev-ns
        spec:
          hard:
            requests.cpu: "10"         # Max 10 CPU cores requested across all pods
            requests.memory: 20Gi      # Max 20Gi RAM requested
            limits.cpu: "20"           # Max 20 CPU cores allowed
            limits.memory: 40Gi        # Max 40Gi RAM allowed
            pods: "50"                 # Max 50 pods
            services: "20"             # Max 20 services
        
      • Example for prod-ns:
        apiVersion: v1
        kind: ResourceQuota
        metadata:
          name: prod-quota
          namespace: prod-ns
        spec:
          hard:
            requests.cpu: "50"         # More resources for prod
            requests.memory: 100Gi
            limits.cpu: "100"
            limits.memory: 200Gi
            pods: "100"
            services: "50"
        
      • Why it works: ResourceQuota prevents a single namespace (and its pods) from consuming all cluster resources, thereby starving other namespaces. It enforces a hard cap on what can be requested and limited.
    3. Network Policies: Implement NetworkPolicy to restrict pod-to-pod communication.
      • Example: Deny all ingress to prod-ns except from specific kube-system pods and prod-ingress-controller:
        apiVersion: networking.k8s.io/v1
        kind: NetworkPolicy
        metadata:
          name: deny-all-ingress-to-prod
          namespace: prod-ns
        spec:
          podSelector: {} # Selects all pods in the namespace
          policyTypes:
          - Ingress
          ingress:
          - from:
            - namespaceSelector:
                matchLabels:
                  kubernetes.io/metadata.name: kube-system # Allow core K8s components
            - podSelector:
                matchLabels:
                  app: prod-ingress-controller # Allow traffic from your prod ingress
        
      • Example: Deny all egress from dev-ns except to specific external IPs or allowed internal services:
        apiVersion: networking.k8s.io/v1
        kind: NetworkPolicy
        metadata:
          name: deny-all-egress-from-dev
          namespace: dev-ns
        spec:
          podSelector: {}
          policyTypes:
          - Egress
          egress:
          - to:
            - ipBlock:
                cidr: 10.0.0.0/8 # Example: Allow access to internal cluster CIDR
            - podSelector:
                matchLabels:
                  app: dev-database # Allow access to dev database
          - to:
            - ipBlock:
                cidr: 8.8.8.8/32 # Example: Allow access to specific external IP (e.g., DNS)
        
      • Why it works: NetworkPolicy acts like a firewall within the cluster, controlling which pods can communicate with each other. By default, if no policies are applied, all pods can talk to all other pods. Applying restrictive policies ensures dev pods cannot accidentally (or maliciously) reach prod services, and vice-versa.
    4. Limit Ranges: While ResourceQuota sets namespace-wide limits, LimitRange can set default resource requests/limits for containers within a namespace and enforce minimums/maximums per pod.
      • Example for dev-ns:
        apiVersion: v1
        kind: LimitRange
        metadata:
          name: dev-limits
          namespace: dev-ns
        spec:
          limits:
          - type: Container
            default:
              cpu: "200m"
              memory: "256Mi"
            defaultRequest:
              cpu: "100m"
              memory: "128Mi"
            max:
              cpu: "1"
              memory: "1Gi"
            min:
              cpu: "50m"
              memory: "64Mi"
        
      • Why it works: This ensures that even if a developer forgets to set resource requests/limits on their pod spec, reasonable defaults are applied, and they can’t request absurd amounts of resources for a single container.

The One Thing Most People Don’t Know

Even with separate clusters, if you’re using Rancher’s multi-cluster management features to view and manage both dev and prod clusters from a single pane of glass, you’re still interacting with a single Rancher API. A compromise of your Rancher instance itself could potentially expose access to both environments. The true isolation is at the Kubernetes cluster level, but the management layer introduces its own potential blast radius.

The next hurdle you’ll face is managing secrets and configurations consistently across these isolated environments without introducing duplication or drift.

Want structured learning?

Take the full Rancher course →