[{"data":1,"prerenderedAt":599},["ShallowReactive",2],{"mdc--5d03ls-key":3,"mdc--i0du9r-key":21,"mdc-jajpsn-key":202,"mdc-os7wn8-key":216,"mdc--mamtp3-key":228,"mdc--7amcbv-key":292,"mdc-2usx64-key":412,"mdc-3z4irc-key":422,"mdc-f0bcrd-key":489,"mdc-5bt5my-key":501},{"data":4,"body":5},{},{"type":6,"children":7},"root",[8,16],{"type":9,"tag":10,"props":11,"children":12},"element","p",{},[13],{"type":14,"value":15},"text","A container's writable layer dies with the container. That is the design, not a limitation — it is what makes containers disposable, reproducible, and safe to restart. But real applications have state, and state has to live somewhere else.",{"type":9,"tag":10,"props":17,"children":18},{},[19],{"type":14,"value":20},"Docker gives you three places, and choosing between them is most of what this lesson is.",{"data":22,"body":23},{},{"type":6,"children":24},[25,32,154,166],{"type":9,"tag":26,"props":27,"children":29},"h2",{"id":28},"the-three-kinds-of-mount",[30],{"type":14,"value":31},"The three kinds of mount",{"type":9,"tag":33,"props":34,"children":35},"table",{},[36,65],{"type":9,"tag":37,"props":38,"children":39},"thead",{},[40],{"type":9,"tag":41,"props":42,"children":43},"tr",{},[44,50,55,60],{"type":9,"tag":45,"props":46,"children":47},"th",{},[48],{"type":14,"value":49},"Kind",{"type":9,"tag":45,"props":51,"children":52},{},[53],{"type":14,"value":54},"Managed by",{"type":9,"tag":45,"props":56,"children":57},{},[58],{"type":14,"value":59},"Lives at",{"type":9,"tag":45,"props":61,"children":62},{},[63],{"type":14,"value":64},"Use for",{"type":9,"tag":66,"props":67,"children":68},"tbody",{},[69,102,128],{"type":9,"tag":41,"props":70,"children":71},{},[72,82,87,97],{"type":9,"tag":73,"props":74,"children":75},"td",{},[76],{"type":9,"tag":77,"props":78,"children":79},"strong",{},[80],{"type":14,"value":81},"Named volume",{"type":9,"tag":73,"props":83,"children":84},{},[85],{"type":14,"value":86},"Docker",{"type":9,"tag":73,"props":88,"children":89},{},[90],{"type":9,"tag":91,"props":92,"children":94},"code",{"className":93},[],[95],{"type":14,"value":96},"/var/lib/docker/volumes/…",{"type":9,"tag":73,"props":98,"children":99},{},[100],{"type":14,"value":101},"databases, uploads, anything the app owns",{"type":9,"tag":41,"props":103,"children":104},{},[105,113,118,123],{"type":9,"tag":73,"props":106,"children":107},{},[108],{"type":9,"tag":77,"props":109,"children":110},{},[111],{"type":14,"value":112},"Bind mount",{"type":9,"tag":73,"props":114,"children":115},{},[116],{"type":14,"value":117},"you",{"type":9,"tag":73,"props":119,"children":120},{},[121],{"type":14,"value":122},"any host path you name",{"type":9,"tag":73,"props":124,"children":125},{},[126],{"type":14,"value":127},"source code in development, config files",{"type":9,"tag":41,"props":129,"children":130},{},[131,139,144,149],{"type":9,"tag":73,"props":132,"children":133},{},[134],{"type":9,"tag":77,"props":135,"children":136},{},[137],{"type":14,"value":138},"tmpfs",{"type":9,"tag":73,"props":140,"children":141},{},[142],{"type":14,"value":143},"kernel",{"type":9,"tag":73,"props":145,"children":146},{},[147],{"type":14,"value":148},"RAM only, never disk",{"type":9,"tag":73,"props":150,"children":151},{},[152],{"type":14,"value":153},"secrets and scratch that must not persist",{"type":9,"tag":155,"props":156,"children":160},"pre",{"className":157,"code":159,"language":14},[158],"language-text","docker run -v mydata:/var/lib/postgresql/data postgres:17   # named volume\ndocker run -v \"$PWD:/app\" node:22 npm test                  # bind mount\ndocker run --tmpfs /tmp alpine                              # tmpfs\n",[161],{"type":9,"tag":91,"props":162,"children":164},{"__ignoreMap":163},"",[165],{"type":14,"value":159},{"type":9,"tag":10,"props":167,"children":168},{},[169,171,177,179,185,187,193,195,200],{"type":14,"value":170},"The syntax is confusingly overloaded: ",{"type":9,"tag":91,"props":172,"children":174},{"className":173},[],[175],{"type":14,"value":176},"-v",{"type":14,"value":178}," means a named volume when the left side is a bare name, and a bind mount when it looks like a path. ",{"type":9,"tag":91,"props":180,"children":182},{"className":181},[],[183],{"type":14,"value":184},"./data",{"type":14,"value":186}," binds; ",{"type":9,"tag":91,"props":188,"children":190},{"className":189},[],[191],{"type":14,"value":192},"data",{"type":14,"value":194}," creates a volume called ",{"type":9,"tag":91,"props":196,"children":198},{"className":197},[],[199],{"type":14,"value":192},{"type":14,"value":201},". One character of difference, entirely different behaviour.",{"data":203,"body":204},{},{"type":6,"children":205},[206,211],{"type":9,"tag":207,"props":208,"children":210},"terminal-teaser",{":lines":209},"[{\"cmd\":\"docker volume create pgdata\",\"out\":\"pgdata\"},{\"cmd\":\"docker run -d --name db -v pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=x postgres:17\",\"out\":\"c4d81f9a2e77\"},{\"cmd\":\"docker rm -f db\",\"out\":\"db\"},{\"cmd\":\"docker volume ls\",\"out\":\"DRIVER    VOLUME NAME\\nlocal     pgdata\"}]",[],{"type":9,"tag":10,"props":212,"children":213},{},[214],{"type":14,"value":215},"The container is gone; the data is not. Start a new Postgres against the same volume and the database is exactly as it was.",{"data":217,"body":218},{},{"type":6,"children":219},[220],{"type":9,"tag":221,"props":222,"children":227},"quiz",{":answer":223,":options":224,"explanation":225,"question":226},"0","[\"It is gone — it lived in the removed container's writable layer\",\"It is in the image, and the new container will see it\",\"Docker keeps it for 24 hours before deleting\"]","Every write with no mount underneath goes to the container's own layer, which is deleted with the container. Nothing warns you. This is the most common way people lose a development database.","You run Postgres with no volume, write data, then `docker rm` the container and start a fresh one from the same image. What happened to the data?",[],{"data":229,"body":230},{},{"type":6,"children":231},[232,251,256,265],{"type":9,"tag":26,"props":233,"children":235},{"id":234},"mount-says-what-v-means",[236,242,244,249],{"type":9,"tag":91,"props":237,"children":239},{"className":238},[],[240],{"type":14,"value":241},"--mount",{"type":14,"value":243}," says what ",{"type":9,"tag":91,"props":245,"children":247},{"className":246},[],[248],{"type":14,"value":176},{"type":14,"value":250}," means",{"type":9,"tag":10,"props":252,"children":253},{},[254],{"type":14,"value":255},"The newer syntax is verbose and unambiguous, and worth using in anything that outlives a terminal session:",{"type":9,"tag":155,"props":257,"children":260},{"className":258,"code":259,"language":14},[158],"docker run --mount type=volume,source=pgdata,target=/var/lib/postgresql/data postgres:17\ndocker run --mount type=bind,source=\"$PWD\",target=/app,readonly node:22\ndocker run --mount type=tmpfs,target=/tmp alpine\n",[261],{"type":9,"tag":91,"props":262,"children":263},{"__ignoreMap":163},[264],{"type":14,"value":259},{"type":9,"tag":10,"props":266,"children":267},{},[268,270,275,277,282,284,290],{"type":14,"value":269},"Longer, and it removes two real hazards. It states the type instead of inferring it from punctuation. And it ",{"type":9,"tag":77,"props":271,"children":272},{},[273],{"type":14,"value":274},"fails if a bind source does not exist",{"type":14,"value":276},", where ",{"type":9,"tag":91,"props":278,"children":280},{"className":279},[],[281],{"type":14,"value":176},{"type":14,"value":283}," silently creates an empty directory — the reason a bind-mounted config file sometimes shows up inside the container as an empty ",{"type":9,"tag":285,"props":286,"children":287},"em",{},[288],{"type":14,"value":289},"directory",{"type":14,"value":291}," with the same name.",{"data":293,"body":294},{},{"type":6,"children":295},[296,302,307,316,321,326,393],{"type":9,"tag":26,"props":297,"children":299},{"id":298},"bind-mounts-are-for-development",[300],{"type":14,"value":301},"Bind mounts are for development",{"type":9,"tag":10,"props":303,"children":304},{},[305],{"type":14,"value":306},"The characteristic development loop:",{"type":9,"tag":155,"props":308,"children":311},{"className":309,"code":310,"language":14},[158],"docker run -d -p 3000:3000 -v \"$PWD/src:/app/src\" myapp\n",[312],{"type":9,"tag":91,"props":313,"children":314},{"__ignoreMap":163},[315],{"type":14,"value":310},{"type":9,"tag":10,"props":317,"children":318},{},[319],{"type":14,"value":320},"Edit on the host, the container sees it immediately, the process reloads. No rebuild.",{"type":9,"tag":10,"props":322,"children":323},{},[324],{"type":14,"value":325},"Three things to know before relying on it:",{"type":9,"tag":327,"props":328,"children":329},"ul",{},[330,341,383],{"type":9,"tag":331,"props":332,"children":333},"li",{},[334,339],{"type":9,"tag":77,"props":335,"children":336},{},[337],{"type":14,"value":338},"It is a live window, not a copy.",{"type":14,"value":340}," Deleting a file on the host deletes it in the container.",{"type":9,"tag":331,"props":342,"children":343},{},[344,349,351,357,359,365,367,373,375,381],{"type":9,"tag":77,"props":345,"children":346},{},[347],{"type":14,"value":348},"A mount hides whatever was underneath.",{"type":14,"value":350}," Bind-mounting ",{"type":9,"tag":91,"props":352,"children":354},{"className":353},[],[355],{"type":14,"value":356},"/app",{"type":14,"value":358}," over an image that installed ",{"type":9,"tag":91,"props":360,"children":362},{"className":361},[],[363],{"type":14,"value":364},"node_modules",{"type":14,"value":366}," at ",{"type":9,"tag":91,"props":368,"children":370},{"className":369},[],[371],{"type":14,"value":372},"/app/node_modules",{"type":14,"value":374}," makes those dependencies disappear. The usual fix is an anonymous volume over the subdirectory: ",{"type":9,"tag":91,"props":376,"children":378},{"className":377},[],[379],{"type":14,"value":380},"-v \"$PWD:/app\" -v /app/node_modules",{"type":14,"value":382},".",{"type":9,"tag":331,"props":384,"children":385},{},[386,391],{"type":9,"tag":77,"props":387,"children":388},{},[389],{"type":14,"value":390},"UIDs are numbers, not names.",{"type":14,"value":392}," A container running as UID 1000 writing to a host directory owned by a different UID gets permission denied, and the file it does create is owned by a user your host may not have.",{"type":9,"tag":10,"props":394,"children":395},{},[396,398,404,406,411],{"type":14,"value":397},"Compose has a purpose-built answer to all of this — ",{"type":9,"tag":91,"props":399,"children":401},{"className":400},[],[402],{"type":14,"value":403},"develop.watch",{"type":14,"value":405},", covered in ",{"type":9,"tag":285,"props":407,"children":408},{},[409],{"type":14,"value":410},"Docker in Practice",{"type":14,"value":382},{"data":413,"body":414},{},{"type":6,"children":415},[416],{"type":9,"tag":221,"props":417,"children":421},{":answer":223,":options":418,"explanation":419,"question":420},"[\"The bind mount covers `/app` entirely, hiding everything the image put there\",\"The install failed during the build\",\"Bind mounts delete existing directories\"]","A mount replaces the view at its target, like mounting over a directory anywhere in Unix. The image's contents are still in the layer, just not visible. Mount an anonymous volume at `/app/node_modules` to punch a hole back through.","You bind-mount your project into `/app`, but the container's `node_modules` installed at build time has vanished. Why?",[],{"data":423,"body":424},{},{"type":6,"children":425},[426,432,445,454,459,468],{"type":9,"tag":26,"props":427,"children":429},{"id":428},"backing-a-volume-up",[430],{"type":14,"value":431},"Backing a volume up",{"type":9,"tag":10,"props":433,"children":434},{},[435,437,443],{"type":14,"value":436},"Volumes live under ",{"type":9,"tag":91,"props":438,"children":440},{"className":439},[],[441],{"type":14,"value":442},"/var/lib/docker",{"type":14,"value":444},", which you should not go rummaging in. The portable approach is a throwaway container with both the volume and a host directory attached:",{"type":9,"tag":155,"props":446,"children":449},{"className":447,"code":448,"language":14},[158],"docker run --rm \\\n  -v pgdata:/data:ro \\\n  -v \"$PWD\":/backup \\\n  alpine tar czf /backup/pgdata.tar.gz -C /data .\n",[450],{"type":9,"tag":91,"props":451,"children":452},{"__ignoreMap":163},[453],{"type":14,"value":448},{"type":9,"tag":10,"props":455,"children":456},{},[457],{"type":14,"value":458},"And to restore:",{"type":9,"tag":155,"props":460,"children":463},{"className":461,"code":462,"language":14},[158],"docker run --rm \\\n  -v pgdata:/data \\\n  -v \"$PWD\":/backup \\\n  alpine sh -c \"cd /data && tar xzf /backup/pgdata.tar.gz\"\n",[464],{"type":9,"tag":91,"props":465,"children":466},{"__ignoreMap":163},[467],{"type":14,"value":462},{"type":9,"tag":10,"props":469,"children":470},{},[471,473,479,481,487],{"type":14,"value":472},"For a database, prefer the database's own tool — ",{"type":9,"tag":91,"props":474,"children":476},{"className":475},[],[477],{"type":14,"value":478},"pg_dump",{"type":14,"value":480}," via ",{"type":9,"tag":91,"props":482,"children":484},{"className":483},[],[485],{"type":14,"value":486},"docker exec",{"type":14,"value":488}," gives you a consistent snapshot, where a file-level copy of a running Postgres may not.",{"data":490,"body":491},{},{"type":6,"children":492},[493],{"type":9,"tag":494,"props":495,"children":500},"fill-blank",{":answer":496,"hint":497,"placeholder":498,"prompt":499},"[\"docker run -v pgdata:/var/lib/postgresql/data postgres:17\",\"docker run --volume pgdata:/var/lib/postgresql/data postgres:17\",\"docker run -d -v pgdata:/var/lib/postgresql/data postgres:17\"]","The left side of the colon is a bare name, not a path — that is what makes it a volume.","docker run ...","Run `postgres:17` with a named volume called `pgdata` mounted at `/var/lib/postgresql/data`, using the short flag.",[],{"data":502,"body":503},{},{"type":6,"children":504},[505,594],{"type":9,"tag":506,"props":507,"children":509},"deep-dive",{"title":508},"Anonymous volumes, and the disk that fills up",[510,530,542,547,556,583],{"type":9,"tag":10,"props":511,"children":512},{},[513,515,521,523,528],{"type":14,"value":514},"Some images declare ",{"type":9,"tag":91,"props":516,"children":518},{"className":517},[],[519],{"type":14,"value":520},"VOLUME /var/lib/mysql",{"type":14,"value":522}," in their Dockerfile. If you start such a container without naming a mount, Docker creates an ",{"type":9,"tag":77,"props":524,"children":525},{},[526],{"type":14,"value":527},"anonymous volume",{"type":14,"value":529}," — a real volume with a 64-hex-character name and no other identity.",{"type":9,"tag":10,"props":531,"children":532},{},[533,535,540],{"type":14,"value":534},"It behaves like a named one, except nothing refers to it. Remove the container and it stays. Start the container again and you get a ",{"type":9,"tag":285,"props":536,"children":537},{},[538],{"type":14,"value":539},"new",{"type":14,"value":541}," anonymous volume, so your data appears to have vanished while the old volume sits on disk forever.",{"type":9,"tag":10,"props":543,"children":544},{},[545],{"type":14,"value":546},"That is the usual explanation for a Docker host quietly running out of space:",{"type":9,"tag":155,"props":548,"children":551},{"className":549,"code":550,"language":14},[158],"docker system df                  # where the space actually went\ndocker volume ls -f dangling=true # volumes no container references\ndocker volume prune               # delete them — read the list first\n",[552],{"type":9,"tag":91,"props":553,"children":554},{"__ignoreMap":163},[555],{"type":14,"value":550},{"type":9,"tag":10,"props":557,"children":558},{},[559,565,567,573,575,581],{"type":9,"tag":91,"props":560,"children":562},{"className":561},[],[563],{"type":14,"value":564},"docker volume prune",{"type":14,"value":566}," is the one prune command to be careful with. ",{"type":9,"tag":91,"props":568,"children":570},{"className":569},[],[571],{"type":14,"value":572},"docker container prune",{"type":14,"value":574}," and ",{"type":9,"tag":91,"props":576,"children":578},{"className":577},[],[579],{"type":14,"value":580},"docker image prune",{"type":14,"value":582}," throw away things you can recreate; volumes are the only place your data was.",{"type":9,"tag":10,"props":584,"children":585},{},[586,592],{"type":9,"tag":91,"props":587,"children":589},{"className":588},[],[590],{"type":14,"value":591},"docker run --rm",{"type":14,"value":593}," removes anonymous volumes along with the container, which is why one-shot containers are safe. Long-lived services should be given named volumes, so there is a name to back up, inspect, and reason about.",{"type":9,"tag":10,"props":595,"children":596},{},[597],{"type":14,"value":598},"Next up: networking — how containers reach each other and the outside world.",1787908867640]