[{"data":1,"prerenderedAt":532},["ShallowReactive",2],{"notice":3,"post-docker-containers-images-and-stacks-explained":30},{"id":4,"title":5,"body":6,"description":22,"extension":23,"meta":24,"navigation":25,"path":26,"seo":27,"stem":28,"__hash__":29},"page\u002Fnotice.md","Site relaunch",{"type":7,"value":8,"toc":18},"minimark",[9],[10,11,12,13,17],"p",{},"🎉 ",[14,15,16],"strong",{},"Welcome to the all-new Red ALT Key site",", rebuilt from scratch! Video Notes are back, as well as a proper accessibility panel in the header. Still polishing corners — thanks for bearing with us.",{"title":19,"searchDepth":20,"depth":20,"links":21},"",2,[],"🎉 Welcome to the all-new Red ALT Key site, rebuilt from scratch! Video Notes are back, as well as a proper accessibility panel in the header. Still polishing corners — thanks for bearing with us.","md",{},true,"\u002Fnotice",{"title":5,"description":22},"notice","VKMJnU1ySGlgFLHqcmtMB3LTbmKa7RxzFMeX2jhSEc4",{"id":31,"title":32,"body":33,"date":520,"description":521,"extension":23,"image":522,"meta":523,"navigation":25,"path":524,"readingTime":525,"seo":526,"stem":527,"tags":528,"videoUrl":522,"__hash__":531},"posts\u002Fposts\u002Fdocker-containers-images-and-stacks-explained.md","Docker Deep Dive: Containers, Images, and Stacks Explained",{"type":7,"value":34,"toc":514},[35,38,43,55,87,108,149,164,168,175,242,249,256,283,287,302,442,480,491,495,501,510],[10,36,37],{},"\"Just Dockerize it\" gets thrown around a lot, but the mental model underneath — images, containers, and stacks — is where most beginners actually get stuck. Once it clicks, everything else about Docker gets easier.",[39,40,42],"h2",{"id":41},"images-the-blueprint","Images: The Blueprint",[10,44,45,46,49,50,54],{},"An ",[14,47,48],{},"image"," is a read-only template: a filesystem snapshot plus instructions for what to run when a container starts from it. You don't run an image directly — you run a container ",[51,52,53],"em",{},"from"," it.",[56,57,61],"pre",{"className":58,"code":59,"language":60,"meta":19,"style":19},"language-bash shiki shiki-themes github-light github-dark","docker pull nginx:latest\ndocker images\n","bash",[62,63,64,80],"code",{"__ignoreMap":19},[65,66,69,73,77],"span",{"class":67,"line":68},"line",1,[65,70,72],{"class":71},"sScJk","docker",[65,74,76],{"class":75},"sZZnC"," pull",[65,78,79],{"class":75}," nginx:latest\n",[65,81,82,84],{"class":67,"line":20},[65,83,72],{"class":71},[65,85,86],{"class":75}," images\n",[10,88,89,90,93,94,97,98,97,101,104,105,107],{},"Images are built in layers, and that's the whole reason Docker is fast. Each instruction in a ",[62,91,92],{},"Dockerfile"," (",[62,95,96],{},"FROM",", ",[62,99,100],{},"RUN",[62,102,103],{},"COPY",", ...) creates a new layer, and Docker caches layers that haven't changed. Change one line near the bottom of your ",[62,106,92],{}," and only that layer (and everything after it) needs to rebuild.",[56,109,113],{"className":110,"code":111,"language":112,"meta":19,"style":19},"language-dockerfile shiki shiki-themes github-light github-dark","FROM node:20-alpine\nWORKDIR \u002Fapp\nCOPY package*.json .\u002F\nRUN npm install\nCOPY . .\nCMD [\"node\", \"server.js\"]\n","dockerfile",[62,114,115,120,125,131,137,143],{"__ignoreMap":19},[65,116,117],{"class":67,"line":68},[65,118,119],{},"FROM node:20-alpine\n",[65,121,122],{"class":67,"line":20},[65,123,124],{},"WORKDIR \u002Fapp\n",[65,126,128],{"class":67,"line":127},3,[65,129,130],{},"COPY package*.json .\u002F\n",[65,132,134],{"class":67,"line":133},4,[65,135,136],{},"RUN npm install\n",[65,138,140],{"class":67,"line":139},5,[65,141,142],{},"COPY . .\n",[65,144,146],{"class":67,"line":145},6,[65,147,148],{},"CMD [\"node\", \"server.js\"]\n",[10,150,151,152,155,156,159,160,163],{},"Notice ",[62,153,154],{},"package*.json"," is copied and installed ",[51,157,158],{},"before"," the rest of the source code. That ordering means ",[62,161,162],{},"npm install"," only re-runs when your dependencies actually change, not on every code edit.",[39,165,167],{"id":166},"containers-the-running-instance","Containers: The Running Instance",[10,169,170,171,174],{},"A ",[14,172,173],{},"container"," is a running (or stopped) instance of an image — an isolated process with its own filesystem, network namespace, and resource limits, but sharing the host's kernel.",[56,176,178],{"className":58,"code":177,"language":60,"meta":19,"style":19},"docker run -d --name my-nginx -p 8080:80 nginx:latest\ndocker ps\ndocker logs my-nginx\ndocker stop my-nginx && docker rm my-nginx\n",[62,179,180,205,212,222],{"__ignoreMap":19},[65,181,182,184,187,191,194,197,200,203],{"class":67,"line":68},[65,183,72],{"class":71},[65,185,186],{"class":75}," run",[65,188,190],{"class":189},"sj4cs"," -d",[65,192,193],{"class":189}," --name",[65,195,196],{"class":75}," my-nginx",[65,198,199],{"class":189}," -p",[65,201,202],{"class":75}," 8080:80",[65,204,79],{"class":75},[65,206,207,209],{"class":67,"line":20},[65,208,72],{"class":71},[65,210,211],{"class":75}," ps\n",[65,213,214,216,219],{"class":67,"line":127},[65,215,72],{"class":71},[65,217,218],{"class":75}," logs",[65,220,221],{"class":75}," my-nginx\n",[65,223,224,226,229,231,235,237,240],{"class":67,"line":133},[65,225,72],{"class":71},[65,227,228],{"class":75}," stop",[65,230,196],{"class":75},[65,232,234],{"class":233},"sVt8B"," && ",[65,236,72],{"class":71},[65,238,239],{"class":75}," rm",[65,241,221],{"class":75},[10,243,244,245,248],{},"The relationship is one-to-many: one image, unlimited containers. Spin up five containers from the same ",[62,246,247],{},"nginx:latest"," image and you get five independent, isolated instances — this is the whole reason containers are cheap and disposable compared to VMs.",[10,250,251,252,255],{},"A container's writable layer disappears when the container is removed. If you need data to survive that, you need a ",[14,253,254],{},"volume",":",[56,257,259],{"className":58,"code":258,"language":60,"meta":19,"style":19},"docker run -d -v my-data:\u002Fvar\u002Flib\u002Fdata --name my-app my-image\n",[62,260,261],{"__ignoreMap":19},[65,262,263,265,267,269,272,275,277,280],{"class":67,"line":68},[65,264,72],{"class":71},[65,266,186],{"class":75},[65,268,190],{"class":189},[65,270,271],{"class":189}," -v",[65,273,274],{"class":75}," my-data:\u002Fvar\u002Flib\u002Fdata",[65,276,193],{"class":189},[65,278,279],{"class":75}," my-app",[65,281,282],{"class":75}," my-image\n",[39,284,286],{"id":285},"stacks-multiple-containers-one-definition","Stacks: Multiple Containers, One Definition",[10,288,289,290,293,294,297,298,301],{},"Almost nothing real runs as a single container. A typical self-hosted app is a web server, a database, and maybe a cache or reverse proxy — each is its own container, wired together. That's a ",[14,291,292],{},"stack",", and ",[62,295,296],{},"docker-compose.yml"," is how you define it declaratively instead of typing five long ",[62,299,300],{},"docker run"," commands from memory:",[56,303,307],{"className":304,"code":305,"language":306,"meta":19,"style":19},"language-yaml shiki shiki-themes github-light github-dark","services:\n  app:\n    build: .\n    ports:\n      - \"3000:3000\"\n    depends_on:\n      - db\n  db:\n    image: postgres:16\n    environment:\n      POSTGRES_PASSWORD: example\n    volumes:\n      - db-data:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n\nvolumes:\n  db-data:\n","yaml",[62,308,309,318,325,336,343,351,358,366,374,385,393,404,412,420,426,434],{"__ignoreMap":19},[65,310,311,315],{"class":67,"line":68},[65,312,314],{"class":313},"s9eBZ","services",[65,316,317],{"class":233},":\n",[65,319,320,323],{"class":67,"line":20},[65,321,322],{"class":313},"  app",[65,324,317],{"class":233},[65,326,327,330,333],{"class":67,"line":127},[65,328,329],{"class":313},"    build",[65,331,332],{"class":233},": ",[65,334,335],{"class":189},".\n",[65,337,338,341],{"class":67,"line":133},[65,339,340],{"class":313},"    ports",[65,342,317],{"class":233},[65,344,345,348],{"class":67,"line":139},[65,346,347],{"class":233},"      - ",[65,349,350],{"class":75},"\"3000:3000\"\n",[65,352,353,356],{"class":67,"line":145},[65,354,355],{"class":313},"    depends_on",[65,357,317],{"class":233},[65,359,361,363],{"class":67,"line":360},7,[65,362,347],{"class":233},[65,364,365],{"class":75},"db\n",[65,367,369,372],{"class":67,"line":368},8,[65,370,371],{"class":313},"  db",[65,373,317],{"class":233},[65,375,377,380,382],{"class":67,"line":376},9,[65,378,379],{"class":313},"    image",[65,381,332],{"class":233},[65,383,384],{"class":75},"postgres:16\n",[65,386,388,391],{"class":67,"line":387},10,[65,389,390],{"class":313},"    environment",[65,392,317],{"class":233},[65,394,396,399,401],{"class":67,"line":395},11,[65,397,398],{"class":313},"      POSTGRES_PASSWORD",[65,400,332],{"class":233},[65,402,403],{"class":75},"example\n",[65,405,407,410],{"class":67,"line":406},12,[65,408,409],{"class":313},"    volumes",[65,411,317],{"class":233},[65,413,415,417],{"class":67,"line":414},13,[65,416,347],{"class":233},[65,418,419],{"class":75},"db-data:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n",[65,421,423],{"class":67,"line":422},14,[65,424,425],{"emptyLinePlaceholder":25},"\n",[65,427,429,432],{"class":67,"line":428},15,[65,430,431],{"class":313},"volumes",[65,433,317],{"class":233},[65,435,437,440],{"class":67,"line":436},16,[65,438,439],{"class":313},"  db-data",[65,441,317],{"class":233},[56,443,445],{"className":58,"code":444,"language":60,"meta":19,"style":19},"docker compose up -d\ndocker compose logs -f\ndocker compose down\n",[62,446,447,460,471],{"__ignoreMap":19},[65,448,449,451,454,457],{"class":67,"line":68},[65,450,72],{"class":71},[65,452,453],{"class":75}," compose",[65,455,456],{"class":75}," up",[65,458,459],{"class":189}," -d\n",[65,461,462,464,466,468],{"class":67,"line":20},[65,463,72],{"class":71},[65,465,453],{"class":75},[65,467,218],{"class":75},[65,469,470],{"class":189}," -f\n",[65,472,473,475,477],{"class":67,"line":127},[65,474,72],{"class":71},[65,476,453],{"class":75},[65,478,479],{"class":75}," down\n",[10,481,482,483,486,487,490],{},"Compose also creates a private network for the stack automatically, so ",[62,484,485],{},"app"," can reach ",[62,488,489],{},"db"," by service name — no manual networking required.",[39,492,494],{"id":493},"putting-it-together","Putting It Together",[10,496,497,498,500],{},"Image → container → stack is the whole progression: you build or pull a blueprint, run one or more instances of it, and wire multiple instances together into something that behaves like a single application. Once that ordering is intuitive, reading someone else's ",[62,499,296],{}," stops being intimidating — it's just a list of images, how they run, and how they talk to each other.",[10,502,503,504,509],{},"If you haven't already, the ",[505,506,508],"a",{"href":507},"\u002Fposts\u002Fgetting-started-with-a-home-lab","home lab getting-started guide"," is a good next stop for where to actually run this stuff.",[511,512,513],"style",{},"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);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":19,"searchDepth":20,"depth":20,"links":515},[516,517,518,519],{"id":41,"depth":20,"text":42},{"id":166,"depth":20,"text":167},{"id":285,"depth":20,"text":286},{"id":493,"depth":20,"text":494},"2026-09-06","The three Docker concepts that trip people up most, explained the way I wish someone had explained them to me: images, containers, and multi-service stacks.",null,{},"\u002Fposts\u002Fdocker-containers-images-and-stacks-explained","7 min read",{"title":32,"description":521},"posts\u002Fdocker-containers-images-and-stacks-explained",[529,530],"Docker","Home Lab","QMOTBn5CRq6BjT96TzBXdJk37CslwXadqB4rzs63G2I",1788843378948]