{"id":"l-dk-3-2","courseId":"docker-kubernetes","lang":"en","kind":"lesson","summary":"Keep Postgres data across restarts with a named volume, seed the database with a bind-mounted init script, and know which Compose commands keep or destroy your data.","html":"<p>The week we added Compose to a Skylane project, a developer ran <code dir=\"ltr\">docker compose down -v</code> to “clean up” before a demo. It cleaned up three days of carefully entered test data. Containers are disposable by design, so anything you want to keep must live outside them, and you need to know exactly which command throws it away.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Named volumes for database files</h2>\n<p>Add a volume to the <code dir=\"ltr\">db</code> service and declare it at the top level:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"yaml\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">compose.yaml</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">services</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">  # api: …as before</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  db</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    image</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">postgres:17</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    environment</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">      POSTGRES_USER</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">notes</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">      POSTGRES_PASSWORD</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">notes</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">      POSTGRES_DB</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">notes</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    volumes</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      - </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">pgdata:/var/lib/postgresql/data</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">volumes</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  pgdata</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span></code></pre></div>\n<p>A <strong>named volume</strong> is storage that Docker creates and manages. Compose prefixes it with the project name, so this one is <code dir=\"ltr\">notes_pgdata</code>. It isn’t part of any container, so removing and recreating <code dir=\"ltr\">db</code> leaves the data in place:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> compose</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> down</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">        # containers and network removed, pgdata kept</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> compose</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> up</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -d</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">       # notes are still there</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> volume</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> ls</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">           # notes_pgdata</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> volume</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> inspect</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes_pgdata</span></span></code></pre></div>\n<p>Named volumes are the right default for database files. Docker sets ownership correctly for the container, you don’t get host filesystem quirks, and on Docker Desktop they’re much faster than bind mounts because they live inside the Linux VM.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>down -v as a cleanup habit</span></p>\n<p><code dir=\"ltr\">docker compose down -v</code> removes the stack <em>and</em> every named volume it declares. People learn it as “the full reset” and type it from muscle memory. Use plain <code dir=\"ltr\">down</code> by default, and keep <code dir=\"ltr\">-v</code> for the moments you really want an empty database. If your dev data is precious, back it up first:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> compose</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> exec</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> db</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> pg_dump</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -U</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> ></span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> backup.sql</span></span></code></pre></div>\n</aside>\n<p>Restoring is the same pipe in the other direction: <code dir=\"ltr\">docker compose exec -T db psql -U notes notes &lt; backup.sql</code>. The <code dir=\"ltr\">-T</code> flag turns off the pseudo-terminal so the file can be streamed in. It’s worth trying once on a quiet afternoon, so the first time you restore isn’t the day you need to.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Bind mounts for files you edit</h2>\n<p>A <strong>bind mount</strong> maps a path on your machine into the container. Edits on either side show up on the other immediately. That’s what you want for files that live in your repository, like a schema script:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"yaml\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">YAML</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  db</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    image</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">postgres:17</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    volumes</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      - </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">pgdata:/var/lib/postgresql/data</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      - </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">./db/init:/docker-entrypoint-initdb.d:ro</span></span></code></pre></div>\n<p>The Postgres image runs every <code dir=\"ltr\">.sql</code> and <code dir=\"ltr\">.sh</code> file in <code dir=\"ltr\">/docker-entrypoint-initdb.d</code>, in name order, <strong>only when the data directory is empty</strong>. On the very first <code dir=\"ltr\">up</code>, <code dir=\"ltr\">db/init/001-schema.sql</code> creates the <code dir=\"ltr\">notes</code> table. On every later start, the folder is ignored, because the volume already holds an initialised database. When you change the schema after that, write a migration that the app runs, or deliberately wipe the dev volume.</p>\n<p>The <code dir=\"ltr\">:ro</code> suffix makes the mount read-only inside the container. Give containers the least access that works.</p>\n<p>Bind mounts have two rough edges. On Linux, files are owned by the host user’s UID, which may not match the user inside the container, so writes fail with <code dir=\"ltr\">EACCES</code>. On macOS and Windows, file access crosses the VM boundary and is slower, which is why heavy folders like <code dir=\"ltr\">node_modules</code> should never be bind-mounted from the host. You’ll use a better tool for live code editing, Compose watch, in lesson 3.4.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">tmpfs for scratch data</h2>\n<p>A <code dir=\"ltr\">tmpfs</code> mount lives in memory and disappears when the container stops. It’s useful for scratch files you never want on disk, or for a throwaway test database where speed matters more than survival:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"yaml\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">YAML</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  db-test</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    image</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">postgres:17</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    tmpfs</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      - </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">/var/lib/postgresql/data</span></span></code></pre></div>\n<h2 id=\"sec-4\" tabindex=\"-1\">Volumes and database versions</h2>\n<p>A volume stores the database’s on-disk format, which belongs to one major version. If you bump <code dir=\"ltr\">postgres:17</code> to <code dir=\"ltr\">postgres:18</code> and keep the same volume, the new server refuses to start on the old files. Postgres 18’s image also moved its default data location: it expects a single mount at <code dir=\"ltr\">/var/lib/postgresql</code> (and keeps data in a version-named folder below it) rather than at <code dir=\"ltr\">.../data</code>. Its entrypoint exits with an error explaining this if it finds data, or a mount, at the old path. Read the image’s release notes before bumping a major version.</p>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Treat major upgrades as migrations</span></p>\n<p>In development: dump, bump the image, remove the old volume, start fresh, restore the dump. In production you’d plan the same thing with <code dir=\"ltr\">pg_upgrade</code> or a managed database’s upgrade tooling, and a rollback path. Either way, it’s never just a tag change.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">Which mount for which job</h2>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\"><table><thead>\n<tr>\n<th>Need</th>\n<th>Use</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Database files that survive restarts</td>\n<td>Named volume</td>\n</tr>\n<tr>\n<td>Seed scripts or config from the repo</td>\n<td>Bind mount, <code dir=\"ltr\">:ro</code></td>\n</tr>\n<tr>\n<td>Fast, disposable scratch space</td>\n<td><code dir=\"ltr\">tmpfs</code></td>\n</tr>\n<tr>\n<td>Your source code during development</td>\n<td>Compose watch (lesson 3.4)</td>\n</tr>\n</tbody>\n</table></div><p>Your data now outlives your containers. But start the stack from cold and you may see the API fail its first few requests: it starts before Postgres is ready to accept connections. Startup order is next.</p>\n","toc":[{"id":"sec-1","text":"Named volumes for database files","level":2},{"id":"sec-2","text":"Bind mounts for files you edit","level":2},{"id":"sec-3","text":"tmpfs for scratch data","level":2},{"id":"sec-4","text":"Volumes and database versions","level":2},{"id":"sec-5","text":"Which mount for which job","level":2}],"takeaways":["Named volumes are managed by Docker and outlive containers; use them for database files.","Bind mounts map a host path into a container; use them for files you edit on the host, such as seed scripts, and mount them read-only when you can.","<code dir=\"ltr\">docker compose down</code> keeps named volumes; <code dir=\"ltr\">docker compose down -v</code> deletes them along with all their data.","Scripts in <code dir=\"ltr\">/docker-entrypoint-initdb.d</code> run only when the Postgres data directory is empty, which means on the very first start.","A database’s on-disk format is tied to its major version; upgrading Postgres across majors needs a dump and restore or <code dir=\"ltr\">pg_upgrade</code>, not a new tag."],"further":[{"title":"Volumes","url":"https://docs.docker.com/engine/storage/volumes/"},{"title":"Bind mounts","url":"https://docs.docker.com/engine/storage/bind-mounts/"},{"title":"Compose volumes reference","url":"https://docs.docker.com/reference/compose-file/volumes/"}],"quiz":[{"q":"<p>You run <code dir=\"ltr\">docker compose down</code> and then <code dir=\"ltr\">docker compose up -d</code>. The <code dir=\"ltr\">db</code> service mounts the named volume <code dir=\"ltr\">pgdata</code>. What happens to your notes?</p>","options":[{"text":"They’re gone, because <code dir=\"ltr\">down</code> removes containers.","why":"The containers are removed, but the named volume is not. Postgres finds its data again on the next start."},{"text":"They’re gone unless you ran <code dir=\"ltr\">docker compose stop</code> first.","why":"<code dir=\"ltr\">stop</code> versus <code dir=\"ltr\">down</code> affects containers, not named volumes. Both leave <code dir=\"ltr\">pgdata</code> alone."},{"text":"They’re restored from the image’s initial snapshot.","why":"Images don’t snapshot data. The volume is what holds it."},{"text":"They’re still there; <code dir=\"ltr\">down</code> doesn’t delete named volumes unless you add <code dir=\"ltr\">-v</code>.","why":"Correct. Only <code dir=\"ltr\">docker compose down -v</code> (or <code dir=\"ltr\">docker volume rm</code>) deletes the volume."}],"answer":3},{"q":"<p>You edit <code dir=\"ltr\">db/init/001-schema.sql</code> to add a column, run <code dir=\"ltr\">docker compose up -d</code>, and the column doesn’t appear. Why?</p>","options":[{"text":"Bind mounts are only read when the image is built.","why":"Bind mounts are live; the container sees your edit immediately. The issue is when Postgres reads the folder."},{"text":"Init scripts only run when the data directory is empty, and your volume already has data.","why":"Correct. The entrypoint skips <code dir=\"ltr\">/docker-entrypoint-initdb.d</code> on an initialised database. Use a migration, or wipe the dev volume on purpose."},{"text":"The file must be named <code dir=\"ltr\">init.sql</code> exactly.","why":"Any <code dir=\"ltr\">.sql</code>, <code dir=\"ltr\">.sql.gz</code> or <code dir=\"ltr\">.sh</code> file in that folder runs, in name order."},{"text":"The <code dir=\"ltr\">:ro</code> flag stops Postgres from reading the file.","why":"<code dir=\"ltr\">:ro</code> stops the container from <em>writing</em>. Reading is fine."}],"answer":1},{"q":"<p>Which mount should hold Postgres’s data in a development stack you want to survive restarts?</p>","options":[{"text":"A named volume such as <code dir=\"ltr\">pgdata:/var/lib/postgresql/data</code>.","why":"Correct. Docker manages it, it outlives containers, and it avoids host file-permission and performance surprises."},{"text":"A <code dir=\"ltr\">tmpfs</code> mount at <code dir=\"ltr\">/var/lib/postgresql/data</code>.","why":"tmpfs lives in memory and vanishes when the container stops. Fine for throwaway tests, wrong here."},{"text":"The container’s writable layer, which is the default.","why":"That layer is deleted with the container, so <code dir=\"ltr\">docker compose down</code> loses everything."},{"text":"A bind mount to your <code dir=\"ltr\">src/</code> folder.","why":"Mixing database files into your source tree invites accidental commits and permission errors."}],"answer":0},{"q":"<p>You change the <code dir=\"ltr\">db</code> image from <code dir=\"ltr\">postgres:17</code> to <code dir=\"ltr\">postgres:18</code> and keep the same <code dir=\"ltr\">pgdata</code> volume. What should you expect?</p>","options":[{"text":"Postgres upgrades the files in place on startup.","why":"Postgres doesn’t convert data directories across major versions on its own."},{"text":"Nothing changes, because volumes are version-independent.","why":"The volume stores Postgres’s on-disk format, which is specific to the major version."},{"text":"The new version refuses the old data directory (and 18’s image also expects a different mount path); you need a dump-and-restore or <code dir=\"ltr\">pg_upgrade</code>.","why":"Correct. Major upgrades are a migration. In development, dump, wipe the volume, restore; in production, plan it."},{"text":"The volume is automatically renamed to <code dir=\"ltr\">pgdata-18</code>.","why":"Compose never renames volumes. It mounts exactly what the file names."}],"answer":2}],"exercise":{"kind":"choice","multiple":true,"options":["pg-named","init-bind","src-volume","pg-tmpfs","tmpfs-scratch"],"correct":["pg-named","init-bind","tmpfs-scratch"],"title":"Pick the right mount","prompt":"<p>You’re reviewing storage choices in the notes-api Compose stack. Select <strong>every</strong> pairing of need and mount type that is correct.</p>\n","hints":["Ask two questions of each: should the data survive the container, and does it come from your repository?","A named volume never reflects edits you make in your editor; a tmpfs never survives a stop.","Three pairings are correct."],"explanation":"<p>Postgres data belongs in a named volume: it survives <code dir=\"ltr\">down</code> and avoids host permission and performance issues. Seed scripts live in the repo, so a read-only bind mount into <code dir=\"ltr\">/docker-entrypoint-initdb.d</code> is right. Disposable scratch space is what tmpfs is for. A named volume for <code dir=\"ltr\">src/</code> would freeze a copy of your code that never sees your edits, and tmpfs for data you want to keep loses it on every stop.</p>\n","checkLabels":{},"optionText":{"pg-named":{"text":"Postgres data that must survive restarts → named volume <code dir=\"ltr\">pgdata:/var/lib/postgresql/data</code>","why":"Correct. Named volumes outlive containers and are Docker-managed."},"init-bind":{"text":"SQL schema scripts from the repository → bind mount <code dir=\"ltr\">./db/init:/docker-entrypoint-initdb.d:ro</code>","why":"Correct. The files live in your repo and the container only needs to read them."},"src-volume":{"text":"Live-editing <code dir=\"ltr\">src/</code> during development → named volume <code dir=\"ltr\">src:/app/src</code>","why":"A named volume is filled once and then independent of your host folder, so edits never reach the container. Use Compose watch or a bind mount."},"pg-tmpfs":{"text":"Postgres data you want to keep between workdays → <code dir=\"ltr\">tmpfs: /var/lib/postgresql/data</code>","why":"tmpfs lives in memory; stopping the container erases it."},"tmpfs-scratch":{"text":"Temporary files for a test run that should vanish afterwards → <code dir=\"ltr\">tmpfs: /tmp/scratch</code>","why":"Correct. Fast, never written to disk, gone when the container stops."}}},"words":732,"readMinutes":3}