[{"data":1,"prerenderedAt":619},["ShallowReactive",2],{"\u002Fblog\u002F20260709":3},{"id":4,"title":5,"body":6,"date":612,"description":42,"extension":613,"meta":614,"navigation":59,"path":615,"seo":616,"stem":617,"__hash__":618},"blog\u002Fblog\u002F20260709.md","docker composeにおけるvolumeの挙動",{"type":7,"value":8,"toc":609},"minimark",[9,13,17,33,36,125,128,145,148,201,204,402,405,454,457,472,475,520,523,577,596,605],[10,11,5],"h2",{"id":12},"docker-composeにおけるvolumeの挙動",[14,15,16],"p",{},"対象は以下の４パターン",[18,19,20,24,27,30],"ol",{},[21,22,23],"li",{},"bind mountでホスト側が空の場合",[21,25,26],{},"bind mountでホスト側が空ではない場合",[21,28,29],{},"local bindなnamed volumeのvolume mountでホスト側が空の場合",[21,31,32],{},"local bindなnamed volumeのvolume mountでホスト側が空ではない場合",[14,34,35],{},"Dockerfileはこちら",[37,38,43],"pre",{"className":39,"code":40,"language":41,"meta":42,"style":42},"language-Dockerfile shiki shiki-themes github-dark","FROM alpine:3.20\n\nRUN mkdir -p \u002Fapp\u002Fbind-1 \\\n    && mkdir -p \u002Fapp\u002Fbind-2\\\n    && mkdir -p \u002Fapp\u002Fnamed-bind-1 \\\n    && mkdir -p \u002Fapp\u002Fnamed-bind-2\n\nRUN echo \"file in bind1\" > \u002Fapp\u002Fbind-1\u002Ffile.txt \\\n    && echo \"file in bind2\" > \u002Fapp\u002Fbind-2\u002Ffile.txt \\\n    && echo \"file in named-bind-1\" > \u002Fapp\u002Fnamed-bind-1\u002Ffile.txt \\\n    && echo \"file in named-bind-2\" > \u002Fapp\u002Fnamed-bind-2\u002Ffile.txt\n\nCMD [\"sh\", \"-c\", \"sleep infinity\"]\n","Dockerfile","",[44,45,46,54,61,67,73,79,85,90,96,102,108,114,119],"code",{"__ignoreMap":42},[47,48,51],"span",{"class":49,"line":50},"line",1,[47,52,53],{},"FROM alpine:3.20\n",[47,55,57],{"class":49,"line":56},2,[47,58,60],{"emptyLinePlaceholder":59},true,"\n",[47,62,64],{"class":49,"line":63},3,[47,65,66],{},"RUN mkdir -p \u002Fapp\u002Fbind-1 \\\n",[47,68,70],{"class":49,"line":69},4,[47,71,72],{},"    && mkdir -p \u002Fapp\u002Fbind-2\\\n",[47,74,76],{"class":49,"line":75},5,[47,77,78],{},"    && mkdir -p \u002Fapp\u002Fnamed-bind-1 \\\n",[47,80,82],{"class":49,"line":81},6,[47,83,84],{},"    && mkdir -p \u002Fapp\u002Fnamed-bind-2\n",[47,86,88],{"class":49,"line":87},7,[47,89,60],{"emptyLinePlaceholder":59},[47,91,93],{"class":49,"line":92},8,[47,94,95],{},"RUN echo \"file in bind1\" > \u002Fapp\u002Fbind-1\u002Ffile.txt \\\n",[47,97,99],{"class":49,"line":98},9,[47,100,101],{},"    && echo \"file in bind2\" > \u002Fapp\u002Fbind-2\u002Ffile.txt \\\n",[47,103,105],{"class":49,"line":104},10,[47,106,107],{},"    && echo \"file in named-bind-1\" > \u002Fapp\u002Fnamed-bind-1\u002Ffile.txt \\\n",[47,109,111],{"class":49,"line":110},11,[47,112,113],{},"    && echo \"file in named-bind-2\" > \u002Fapp\u002Fnamed-bind-2\u002Ffile.txt\n",[47,115,117],{"class":49,"line":116},12,[47,118,60],{"emptyLinePlaceholder":59},[47,120,122],{"class":49,"line":121},13,[47,123,124],{},"CMD [\"sh\", \"-c\", \"sleep infinity\"]\n",[14,126,127],{},"これをmountなしで起動する。",[37,129,133],{"className":130,"code":131,"language":132,"meta":42,"style":42},"language-sh shiki shiki-themes github-dark","docker build -t volume-test-app .\ndocker run --rm -it volume-test-app sh\n","sh",[44,134,135,140],{"__ignoreMap":42},[47,136,137],{"class":49,"line":50},[47,138,139],{},"docker build -t volume-test-app .\n",[47,141,142],{"class":49,"line":56},[47,143,144],{},"docker run --rm -it volume-test-app sh\n",[14,146,147],{},"当然こうなる。",[37,149,151],{"className":130,"code":150,"language":132,"meta":42,"style":42},"\u002F # tree app\napp\n├── bind-1\n│   └── file.txt\n├── bind-2\n│   └── file.txt\n├── named-bind-1\n│   └── file.txt\n└── named-bind-2\n    └── file.txt\n",[44,152,153,158,163,168,173,178,182,187,191,196],{"__ignoreMap":42},[47,154,155],{"class":49,"line":50},[47,156,157],{},"\u002F # tree app\n",[47,159,160],{"class":49,"line":56},[47,161,162],{},"app\n",[47,164,165],{"class":49,"line":63},[47,166,167],{},"├── bind-1\n",[47,169,170],{"class":49,"line":69},[47,171,172],{},"│   └── file.txt\n",[47,174,175],{"class":49,"line":75},[47,176,177],{},"├── bind-2\n",[47,179,180],{"class":49,"line":81},[47,181,172],{},[47,183,184],{"class":49,"line":87},[47,185,186],{},"├── named-bind-1\n",[47,188,189],{"class":49,"line":92},[47,190,172],{},[47,192,193],{"class":49,"line":98},[47,194,195],{},"└── named-bind-2\n",[47,197,198],{"class":49,"line":104},[47,199,200],{},"    └── file.txt\n",[14,202,203],{},"次に、docker composeで、先ほどあげたパターンでmountする。",[37,205,209],{"className":206,"code":207,"language":208,"meta":42,"style":42},"language-yml shiki shiki-themes github-dark","services:\n  app:\n    build: .\n    volumes:\n      - .\u002Fhost-bind-empty:\u002Fapp\u002Fbind-1\n      - .\u002Fhost-bind-non-empty:\u002Fapp\u002Fbind-2\n      - named_bind_volume:\u002Fapp\u002Fnamed-bind-1\n      - named_bind_volume2:\u002Fapp\u002Fnamed-bind-2\n\nvolumes:\n  named_bind_volume:\n    driver: local\n    driver_opts:\n      type: none\n      o: bind\n      device: .\u002Fhost-named-bind-empty\n\n  named_bind_volume2:\n    driver: local\n    driver_opts:\n      type: none\n      o: bind\n      device: .\u002Fhost-named-bind-non-empty\n","yml",[44,210,211,221,228,240,247,256,263,270,277,281,288,295,305,312,323,334,345,350,358,367,374,383,392],{"__ignoreMap":42},[47,212,213,217],{"class":49,"line":50},[47,214,216],{"class":215},"s4JwU","services",[47,218,220],{"class":219},"s95oV",":\n",[47,222,223,226],{"class":49,"line":56},[47,224,225],{"class":215},"  app",[47,227,220],{"class":219},[47,229,230,233,236],{"class":49,"line":63},[47,231,232],{"class":215},"    build",[47,234,235],{"class":219},": ",[47,237,239],{"class":238},"sDLfK",".\n",[47,241,242,245],{"class":49,"line":69},[47,243,244],{"class":215},"    volumes",[47,246,220],{"class":219},[47,248,249,252],{"class":49,"line":75},[47,250,251],{"class":219},"      - ",[47,253,255],{"class":254},"sU2Wk",".\u002Fhost-bind-empty:\u002Fapp\u002Fbind-1\n",[47,257,258,260],{"class":49,"line":81},[47,259,251],{"class":219},[47,261,262],{"class":254},".\u002Fhost-bind-non-empty:\u002Fapp\u002Fbind-2\n",[47,264,265,267],{"class":49,"line":87},[47,266,251],{"class":219},[47,268,269],{"class":254},"named_bind_volume:\u002Fapp\u002Fnamed-bind-1\n",[47,271,272,274],{"class":49,"line":92},[47,273,251],{"class":219},[47,275,276],{"class":254},"named_bind_volume2:\u002Fapp\u002Fnamed-bind-2\n",[47,278,279],{"class":49,"line":98},[47,280,60],{"emptyLinePlaceholder":59},[47,282,283,286],{"class":49,"line":104},[47,284,285],{"class":215},"volumes",[47,287,220],{"class":219},[47,289,290,293],{"class":49,"line":110},[47,291,292],{"class":215},"  named_bind_volume",[47,294,220],{"class":219},[47,296,297,300,302],{"class":49,"line":116},[47,298,299],{"class":215},"    driver",[47,301,235],{"class":219},[47,303,304],{"class":254},"local\n",[47,306,307,310],{"class":49,"line":121},[47,308,309],{"class":215},"    driver_opts",[47,311,220],{"class":219},[47,313,315,318,320],{"class":49,"line":314},14,[47,316,317],{"class":215},"      type",[47,319,235],{"class":219},[47,321,322],{"class":254},"none\n",[47,324,326,329,331],{"class":49,"line":325},15,[47,327,328],{"class":215},"      o",[47,330,235],{"class":219},[47,332,333],{"class":254},"bind\n",[47,335,337,340,342],{"class":49,"line":336},16,[47,338,339],{"class":215},"      device",[47,341,235],{"class":219},[47,343,344],{"class":254},".\u002Fhost-named-bind-empty\n",[47,346,348],{"class":49,"line":347},17,[47,349,60],{"emptyLinePlaceholder":59},[47,351,353,356],{"class":49,"line":352},18,[47,354,355],{"class":215},"  named_bind_volume2",[47,357,220],{"class":219},[47,359,361,363,365],{"class":49,"line":360},19,[47,362,299],{"class":215},[47,364,235],{"class":219},[47,366,304],{"class":254},[47,368,370,372],{"class":49,"line":369},20,[47,371,309],{"class":215},[47,373,220],{"class":219},[47,375,377,379,381],{"class":49,"line":376},21,[47,378,317],{"class":215},[47,380,235],{"class":219},[47,382,322],{"class":254},[47,384,386,388,390],{"class":49,"line":385},22,[47,387,328],{"class":215},[47,389,235],{"class":219},[47,391,333],{"class":254},[47,393,395,397,399],{"class":49,"line":394},23,[47,396,339],{"class":215},[47,398,235],{"class":219},[47,400,401],{"class":254},".\u002Fhost-named-bind-non-empty\n",[14,403,404],{},"host側はこんな感じ。",[37,406,408],{"className":130,"code":407,"language":132,"meta":42,"style":42},".\n├── Dockerfile\n├── docker-compose.yml\n├── host-bind-empty #1\n├── host-bind-non-empty #2\n│   └── host-file.txt\n├── host-named-bind-empty #3\n└── host-named-bind-non-empty #4\n    └── host-named-bind-file.txt\n",[44,409,410,414,419,424,429,434,439,444,449],{"__ignoreMap":42},[47,411,412],{"class":49,"line":50},[47,413,239],{},[47,415,416],{"class":49,"line":56},[47,417,418],{},"├── Dockerfile\n",[47,420,421],{"class":49,"line":63},[47,422,423],{},"├── docker-compose.yml\n",[47,425,426],{"class":49,"line":69},[47,427,428],{},"├── host-bind-empty #1\n",[47,430,431],{"class":49,"line":75},[47,432,433],{},"├── host-bind-non-empty #2\n",[47,435,436],{"class":49,"line":81},[47,437,438],{},"│   └── host-file.txt\n",[47,440,441],{"class":49,"line":87},[47,442,443],{},"├── host-named-bind-empty #3\n",[47,445,446],{"class":49,"line":92},[47,447,448],{},"└── host-named-bind-non-empty #4\n",[47,450,451],{"class":49,"line":98},[47,452,453],{},"    └── host-named-bind-file.txt\n",[14,455,456],{},"実行。",[37,458,460],{"className":130,"code":459,"language":132,"meta":42,"style":42},"docker compose build app\ndocker compose run --rm --no-deps app sh\n",[44,461,462,467],{"__ignoreMap":42},[47,463,464],{"class":49,"line":50},[47,465,466],{},"docker compose build app\n",[47,468,469],{"class":49,"line":56},[47,470,471],{},"docker compose run --rm --no-deps app sh\n",[14,473,474],{},"コンテナ内を見るとこうなっている。",[37,476,478],{"className":130,"code":477,"language":132,"meta":42,"style":42},"\u002F # tree app\napp\n├── bind-1 # イメージ内に存在したファイルが消えている。\n├── bind-2 # ホスト側のファイルのみが存在する。\n│   └── host-file.txt\n├── named-bind-1 # イメージ内に存在したファイルがそのまま存在する。\n│   └── file.txt\n└── named-bind-2 # ホスト側のファイルのみが存在する。\n    └── host-named-bind-file.txt\n",[44,479,480,484,488,493,498,502,507,511,516],{"__ignoreMap":42},[47,481,482],{"class":49,"line":50},[47,483,157],{},[47,485,486],{"class":49,"line":56},[47,487,162],{},[47,489,490],{"class":49,"line":63},[47,491,492],{},"├── bind-1 # イメージ内に存在したファイルが消えている。\n",[47,494,495],{"class":49,"line":69},[47,496,497],{},"├── bind-2 # ホスト側のファイルのみが存在する。\n",[47,499,500],{"class":49,"line":75},[47,501,438],{},[47,503,504],{"class":49,"line":81},[47,505,506],{},"├── named-bind-1 # イメージ内に存在したファイルがそのまま存在する。\n",[47,508,509],{"class":49,"line":87},[47,510,172],{},[47,512,513],{"class":49,"line":92},[47,514,515],{},"└── named-bind-2 # ホスト側のファイルのみが存在する。\n",[47,517,518],{"class":49,"line":98},[47,519,453],{},[14,521,522],{},"ホスト側はこう。",[37,524,526],{"className":130,"code":525,"language":132,"meta":42,"style":42}," tree\n.\n├── Dockerfile\n├── docker-compose.yml\n├── host-bind-empty # 空のまま。\n├── host-bind-non-empty # 元のファイルがそのまま存在する。\n│   └── host-file.txt\n├── host-named-bind-empty # イメージ側のファイルが存在する。\n│   └── file.txt\n└── host-named-bind-non-empty # ホスト側のファイルがそのまま存在する。\n    └── host-named-bind-file.txt\n",[44,527,528,533,537,541,545,550,555,559,564,568,573],{"__ignoreMap":42},[47,529,530],{"class":49,"line":50},[47,531,532],{}," tree\n",[47,534,535],{"class":49,"line":56},[47,536,239],{},[47,538,539],{"class":49,"line":63},[47,540,418],{},[47,542,543],{"class":49,"line":69},[47,544,423],{},[47,546,547],{"class":49,"line":75},[47,548,549],{},"├── host-bind-empty # 空のまま。\n",[47,551,552],{"class":49,"line":81},[47,553,554],{},"├── host-bind-non-empty # 元のファイルがそのまま存在する。\n",[47,556,557],{"class":49,"line":87},[47,558,438],{},[47,560,561],{"class":49,"line":92},[47,562,563],{},"├── host-named-bind-empty # イメージ側のファイルが存在する。\n",[47,565,566],{"class":49,"line":98},[47,567,172],{},[47,569,570],{"class":49,"line":104},[47,571,572],{},"└── host-named-bind-non-empty # ホスト側のファイルがそのまま存在する。\n",[47,574,575],{"class":49,"line":110},[47,576,453],{},[14,578,579,580,583,589,590,592],{},"bind mountの場合、コンテナ内にもともと存在するファイルやディレクトリはホスト側によって上書きされる。",[581,582],"br",{},[584,585,586],"a",{"href":586,"rel":587},"https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Fstorage\u002Fbind-mounts\u002F?utm_source=chatgpt.com#bind-mounting-over-existing-data",[588],"nofollow","\nvolume mountの場合、non-emptyなvolumeでマウントする場合は、bind mountと同様にホスト側によって上書きされる。しかし、emptyなvolumeでマウントする場合、コンテナ内に存在するファイルやディレクトリがコピーされる。",[581,591],{},[584,593,594],{"href":594,"rel":595},"https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Fstorage\u002Fvolumes\u002F?utm_source=chatgpt.com#mounting-a-volume-over-existing-data",[588],[14,597,598,599,601,602,604],{},"この挙動を利用すると、local bindなnamed volumeを定義して、そのvolumeでvolume mountをすることで、イメージ内にもともと存在するファイルをホスト側にコピーさせることが可能となる。",[581,600],{},"\nたとえばイメージビルド時にnpm installした内容をnode_modulesディレクトリを通じてホストと共有することできる。",[581,603],{},"\nその状態でコンテナ内で新たにnpm installすれば、ホスト側にもそれが共有される。",[606,607,608],"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 pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}",{"title":42,"searchDepth":56,"depth":56,"links":610},[611],{"id":12,"depth":56,"text":5},"2026-07-09","md",{},"\u002Fblog\u002F20260709",{"title":5,"description":42},"blog\u002F20260709","Yzb8oeZfFqLTM6DRKzs4fgjQJve3h35Igc649Dl4lio",1783609083901]