[{"data":1,"prerenderedAt":661},["ShallowReactive",2],{"print-topic-\u002Flearn\u002Fen\u002Fcourses\u002Fpods":3,"lessons-\u002Flearn\u002Fen\u002Fcourses\u002Fpods":44,"i-lucide:printer":657},{"id":4,"title":5,"access":6,"body":7,"description":14,"draft":15,"estimatedMinutes":16,"extension":17,"featured":15,"icon":18,"lang":19,"level":20,"meta":21,"navigation":22,"order":23,"path":24,"prerequisites":25,"resources":26,"seo":31,"sku":32,"stem":33,"subjects":34,"summary":36,"tags":37,"updated":42,"__hash__":43},"courses\u002Flearn\u002Fen\u002Fcourses\u002Fpods\u002Findex.md","Pods and workloads","free",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",3,[],"The smallest deployable unit, and the controllers that manage sets of them.",false,25,"md","lucide:blocks","en","beginner",{},true,1,"\u002Flearn\u002Fen\u002Fcourses\u002Fpods",[],[27],{"label":28,"file":29,"access":6,"size":30},"Workload controller decision tree","k8s-workload-controllers.pdf","120 KB",{"title":5,"description":14},null,"learn\u002Fen\u002Fcourses\u002Fpods\u002Findex",[35],"kubernetes","A pod is one or more containers sharing a network namespace and a lifecycle.\nYou rarely create one directly — Deployments, StatefulSets, DaemonSets and Jobs\neach manage pods with different guarantees about identity, ordering and\nreplacement. Choosing the wrong controller is the root of a surprising number\nof production problems, because the guarantee you assumed was never offered.\n",[38,39,40,41],"pods","deployments","statefulsets","workloads","2026-08-04","m76OXiUC-jLIAp9S_k3i7kuptZfYW5jrdGa40vEITF8",[45,365],{"id":46,"title":47,"access":6,"body":48,"description":359,"extension":17,"lang":19,"meta":360,"navigation":22,"order":23,"partial":15,"path":361,"seo":362,"stem":363,"__hash__":364},"lessons\u002Flearn\u002Fen\u002Fcourses\u002Fpods\u002F01.the-pod.md","The pod",{"type":8,"value":49,"toc":354},[50,54,58,61,66,74,233,240,246,250,253,316,319,323,343,350],[51,52,47],"h1",{"id":53},"the-pod",[55,56,57],"p",{},"Kubernetes does not schedule containers. It schedules pods.",[55,59,60],{},"A pod is a group of containers that share a network namespace, share IPC, and can\nshare volumes. They are always placed on the same node, always started together,\nand always terminated together.",[62,63,65],"h2",{"id":64},"what-sharing-a-network-namespace-means","What sharing a network namespace means",[55,67,68,69,73],{},"Every container in a pod sees the same IP address and the same port space. They\nreach each other over ",[70,71,72],"code",{},"localhost",", and they cannot both bind the same port.",[75,76,80],"pre",{"className":77,"code":78,"language":79,"meta":11,"style":11},"language-yaml shiki shiki-themes github-dark-dimmed github-dark-dimmed","apiVersion: v1\nkind: Pod\nmetadata:\n  name: web\nspec:\n  containers:\n    - name: app\n      image: myapp:1.4\n      ports:\n        - containerPort: 8080\n    - name: metrics-proxy\n      image: proxy:2.0\n      ports:\n        - containerPort: 9090\n","yaml",[70,81,82,98,109,117,128,136,144,158,169,177,192,204,214,221],{"__ignoreMap":11},[83,84,86,90,94],"span",{"class":85,"line":23},"line",[83,87,89],{"class":88},"sza-u","apiVersion",[83,91,93],{"class":92},"sM9_K",": ",[83,95,97],{"class":96},"szYpP","v1\n",[83,99,101,104,106],{"class":85,"line":100},2,[83,102,103],{"class":88},"kind",[83,105,93],{"class":92},[83,107,108],{"class":96},"Pod\n",[83,110,111,114],{"class":85,"line":12},[83,112,113],{"class":88},"metadata",[83,115,116],{"class":92},":\n",[83,118,120,123,125],{"class":85,"line":119},4,[83,121,122],{"class":88},"  name",[83,124,93],{"class":92},[83,126,127],{"class":96},"web\n",[83,129,131,134],{"class":85,"line":130},5,[83,132,133],{"class":88},"spec",[83,135,116],{"class":92},[83,137,139,142],{"class":85,"line":138},6,[83,140,141],{"class":88},"  containers",[83,143,116],{"class":92},[83,145,147,150,153,155],{"class":85,"line":146},7,[83,148,149],{"class":92},"    - ",[83,151,152],{"class":88},"name",[83,154,93],{"class":92},[83,156,157],{"class":96},"app\n",[83,159,161,164,166],{"class":85,"line":160},8,[83,162,163],{"class":88},"      image",[83,165,93],{"class":92},[83,167,168],{"class":96},"myapp:1.4\n",[83,170,172,175],{"class":85,"line":171},9,[83,173,174],{"class":88},"      ports",[83,176,116],{"class":92},[83,178,180,183,186,188],{"class":85,"line":179},10,[83,181,182],{"class":92},"        - ",[83,184,185],{"class":88},"containerPort",[83,187,93],{"class":92},[83,189,191],{"class":190},"sQdni","8080\n",[83,193,195,197,199,201],{"class":85,"line":194},11,[83,196,149],{"class":92},[83,198,152],{"class":88},[83,200,93],{"class":92},[83,202,203],{"class":96},"metrics-proxy\n",[83,205,207,209,211],{"class":85,"line":206},12,[83,208,163],{"class":88},[83,210,93],{"class":92},[83,212,213],{"class":96},"proxy:2.0\n",[83,215,217,219],{"class":85,"line":216},13,[83,218,174],{"class":88},[83,220,116],{"class":92},[83,222,224,226,228,230],{"class":85,"line":223},14,[83,225,182],{"class":92},[83,227,185],{"class":88},[83,229,93],{"class":92},[83,231,232],{"class":190},"9090\n",[55,234,235,236,239],{},"The proxy scrapes ",[70,237,238],{},"http:\u002F\u002Flocalhost:8080"," — no service discovery, no DNS, no\nnetwork hop. This is the sidecar pattern, and the shared namespace is the whole\nreason it works.",[241,242,243],"note",{},[55,244,245],{},"Two containers in one pod is a design decision, not a packaging convenience. The\nright question is whether they must scale together and die together. If either\nanswer is no, they belong in separate pods.",[62,247,249],{"id":248},"init-containers","Init containers",[55,251,252],{},"Init containers run to completion, in order, before any app container starts. If\none fails, the pod restarts according to its policy and the sequence begins again.",[75,254,256],{"className":77,"code":255,"language":79,"meta":11,"style":11},"spec:\n  initContainers:\n    - name: wait-for-db\n      image: busybox:1.36\n      command: ['sh', '-c', 'until nc -z db 5432; do sleep 1; done']\n",[70,257,258,264,271,282,291],{"__ignoreMap":11},[83,259,260,262],{"class":85,"line":23},[83,261,133],{"class":88},[83,263,116],{"class":92},[83,265,266,269],{"class":85,"line":100},[83,267,268],{"class":88},"  initContainers",[83,270,116],{"class":92},[83,272,273,275,277,279],{"class":85,"line":12},[83,274,149],{"class":92},[83,276,152],{"class":88},[83,278,93],{"class":92},[83,280,281],{"class":96},"wait-for-db\n",[83,283,284,286,288],{"class":85,"line":119},[83,285,163],{"class":88},[83,287,93],{"class":92},[83,289,290],{"class":96},"busybox:1.36\n",[83,292,293,296,299,302,305,308,310,313],{"class":85,"line":130},[83,294,295],{"class":88},"      command",[83,297,298],{"class":92},": [",[83,300,301],{"class":96},"'sh'",[83,303,304],{"class":92},", ",[83,306,307],{"class":96},"'-c'",[83,309,304],{"class":92},[83,311,312],{"class":96},"'until nc -z db 5432; do sleep 1; done'",[83,314,315],{"class":92},"]\n",[55,317,318],{},"They are the correct place for migrations, waiting on a dependency, or fetching\nconfiguration — anything that must be finished before the app process begins.",[62,320,322],{"id":321},"restart-policy-is-per-pod-not-per-container","Restart policy is per pod, not per container",[55,324,325,328,329,304,332,335,336,339,340,342],{},[70,326,327],{},"restartPolicy"," applies to the pod as a whole: ",[70,330,331],{},"Always",[70,333,334],{},"OnFailure"," or ",[70,337,338],{},"Never",".\nDeployments require ",[70,341,331],{},". Jobs require one of the other two.",[55,344,345,346,349],{},"A crashing container is restarted in place by the kubelet, with exponential backoff\nup to five minutes. That backoff is what ",[70,347,348],{},"CrashLoopBackOff"," means — the pod is not\nbroken beyond repair, the kubelet is simply waiting before its next attempt.",[351,352,353],"style",{},"html pre.shiki code .sza-u, html code.shiki .sza-u{--shiki-default:#8DDB8C;--shiki-dark:#8DDB8C}html pre.shiki code .sM9_K, html code.shiki .sM9_K{--shiki-default:#ADBAC7;--shiki-dark:#ADBAC7}html pre.shiki code .szYpP, html code.shiki .szYpP{--shiki-default:#96D0FF;--shiki-dark:#96D0FF}html pre.shiki code .sQdni, html code.shiki .sQdni{--shiki-default:#6CB6FF;--shiki-dark:#6CB6FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":11,"searchDepth":12,"depth":12,"links":355},[356,357,358],{"id":64,"depth":100,"text":65},{"id":248,"depth":100,"text":249},{"id":321,"depth":100,"text":322},"Shared network and storage namespaces, and why the unit is not the container.",{},"\u002Flearn\u002Fen\u002Fcourses\u002Fpods\u002Fthe-pod",{"title":47,"description":359},"learn\u002Fen\u002Fcourses\u002Fpods\u002F01.the-pod","MxEnNnEovq7oJ35S9_DH8RQtcRph-8E1rjzvVskgg5U",{"id":366,"title":367,"access":6,"body":368,"description":651,"extension":17,"lang":19,"meta":652,"navigation":22,"order":100,"partial":15,"path":653,"seo":654,"stem":655,"__hash__":656},"lessons\u002Flearn\u002Fen\u002Fcourses\u002Fpods\u002F02.controllers.md","Choosing a controller",{"type":8,"value":369,"toc":646},[370,373,376,464,468,471,474,478,488,491,497,501,617,630,644],[51,371,367],{"id":372},"choosing-a-controller",[55,374,375],{},"Each workload controller manages pods. They differ in what they promise about\nidentity, ordering and placement.",[377,378,379,395],"table",{},[380,381,382],"thead",{},[383,384,385,389,392],"tr",{},[386,387,388],"th",{},"Controller",[386,390,391],{},"Use when",[386,393,394],{},"Guarantee",[396,397,398,412,425,438,451],"tbody",{},[383,399,400,406,409],{},[401,402,403],"td",{},[70,404,405],{},"Deployment",[401,407,408],{},"Stateless replicas",[401,410,411],{},"Interchangeable pods, rolling updates",[383,413,414,419,422],{},[401,415,416],{},[70,417,418],{},"StatefulSet",[401,420,421],{},"Stateful members",[401,423,424],{},"Stable name, stable storage, ordered rollout",[383,426,427,432,435],{},[401,428,429],{},[70,430,431],{},"DaemonSet",[401,433,434],{},"Node-level agents",[401,436,437],{},"One pod per matching node",[383,439,440,445,448],{},[401,441,442],{},[70,443,444],{},"Job",[401,446,447],{},"Run to completion",[401,449,450],{},"Retries until the success count is met",[383,452,453,458,461],{},[401,454,455],{},[70,456,457],{},"CronJob",[401,459,460],{},"Scheduled work",[401,462,463],{},"Creates Jobs on a schedule",[62,465,467],{"id":466},"deployments-give-you-no-identity","Deployments give you no identity",[55,469,470],{},"Deployment pods get random name suffixes and are replaced freely. Nothing about a\ngiven pod persists across a restart — not its name, not its storage, not its\nposition in a cluster.",[55,472,473],{},"This is exactly right for a web server and exactly wrong for a database replica.",[62,475,477],{"id":476},"statefulsets-trade-speed-for-identity","StatefulSets trade speed for identity",[55,479,480,481,304,484,487],{},"A StatefulSet gives each pod an ordinal name (",[70,482,483],{},"db-0",[70,485,486],{},"db-1","), a stable DNS entry\nvia a headless Service, and its own PersistentVolumeClaim that survives rescheduling.",[55,489,490],{},"Rollouts happen one pod at a time, in reverse ordinal order, waiting for each to\nbecome ready before continuing.",[492,493,494],"warning",{},[55,495,496],{},"That serial rollout is a feature, not a limitation — but it means a StatefulSet\nwith fifty replicas and a slow readiness probe can take an hour to roll. Budget\nfor it before choosing StatefulSet for something that did not need identity.",[62,498,500],{"id":499},"jobs-and-the-completion-count","Jobs and the completion count",[75,502,504],{"className":77,"code":503,"language":79,"meta":11,"style":11},"apiVersion: batch\u002Fv1\nkind: Job\nmetadata:\n  name: migrate\nspec:\n  backoffLimit: 3\n  template:\n    spec:\n      restartPolicy: Never\n      containers:\n        - name: migrate\n          image: myapp:1.4\n          command: ['.\u002Fmigrate']\n",[70,505,506,515,524,530,539,545,555,562,569,579,586,596,605],{"__ignoreMap":11},[83,507,508,510,512],{"class":85,"line":23},[83,509,89],{"class":88},[83,511,93],{"class":92},[83,513,514],{"class":96},"batch\u002Fv1\n",[83,516,517,519,521],{"class":85,"line":100},[83,518,103],{"class":88},[83,520,93],{"class":92},[83,522,523],{"class":96},"Job\n",[83,525,526,528],{"class":85,"line":12},[83,527,113],{"class":88},[83,529,116],{"class":92},[83,531,532,534,536],{"class":85,"line":119},[83,533,122],{"class":88},[83,535,93],{"class":92},[83,537,538],{"class":96},"migrate\n",[83,540,541,543],{"class":85,"line":130},[83,542,133],{"class":88},[83,544,116],{"class":92},[83,546,547,550,552],{"class":85,"line":138},[83,548,549],{"class":88},"  backoffLimit",[83,551,93],{"class":92},[83,553,554],{"class":190},"3\n",[83,556,557,560],{"class":85,"line":146},[83,558,559],{"class":88},"  template",[83,561,116],{"class":92},[83,563,564,567],{"class":85,"line":160},[83,565,566],{"class":88},"    spec",[83,568,116],{"class":92},[83,570,571,574,576],{"class":85,"line":171},[83,572,573],{"class":88},"      restartPolicy",[83,575,93],{"class":92},[83,577,578],{"class":96},"Never\n",[83,580,581,584],{"class":85,"line":179},[83,582,583],{"class":88},"      containers",[83,585,116],{"class":92},[83,587,588,590,592,594],{"class":85,"line":194},[83,589,182],{"class":92},[83,591,152],{"class":88},[83,593,93],{"class":92},[83,595,538],{"class":96},[83,597,598,601,603],{"class":85,"line":206},[83,599,600],{"class":88},"          image",[83,602,93],{"class":92},[83,604,168],{"class":96},[83,606,607,610,612,615],{"class":85,"line":216},[83,608,609],{"class":88},"          command",[83,611,298],{"class":92},[83,613,614],{"class":96},"'.\u002Fmigrate'",[83,616,315],{"class":92},[55,618,619,622,623,626,627,629],{},[70,620,621],{},"backoffLimit"," counts failed pods, not failed containers. With\n",[70,624,625],{},"restartPolicy: Never"," a failure creates a new pod; with ",[70,628,334],{}," the container\nrestarts inside the existing pod. The two produce different pod counts for the same\nnumber of failures, which makes debugging confusing if you are not expecting it.",[631,632,633],"tip",{},[55,634,635,636,639,640,643],{},"Set ",[70,637,638],{},"ttlSecondsAfterFinished"," on Jobs. Without it, completed Job pods accumulate\nindefinitely and eventually make ",[70,641,642],{},"kubectl get pods"," unusable.",[351,645,353],{},{"title":11,"searchDepth":12,"depth":12,"links":647},[648,649,650],{"id":466,"depth":100,"text":467},{"id":476,"depth":100,"text":477},{"id":499,"depth":100,"text":500},"Deployment, StatefulSet, DaemonSet and Job — what each one actually guarantees.",{},"\u002Flearn\u002Fen\u002Fcourses\u002Fpods\u002Fcontrollers",{"title":367,"description":651},"learn\u002Fen\u002Fcourses\u002Fpods\u002F02.controllers","VSCelnVYA4VGJd0fBOn99YCc4llCWJ8uDyfJB4DOFKM",{"left":658,"top":658,"width":659,"height":659,"rotate":658,"vFlip":15,"hFlip":15,"body":660},0,24,"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6\"\u002F>\u003Crect width=\"12\" height=\"8\" x=\"6\" y=\"14\" rx=\"1\"\u002F>\u003C\u002Fg>",1787597904899]