{"id":"l-dk-1-4","courseId":"docker-kubernetes","lang":"en","kind":"lesson","summary":"Run notes-api in the background with ports, environment variables and resource limits, read its logs, get a shell inside it, and make it shut down cleanly on SIGTERM.","html":"<p>Building an image is half the job; the other half is running it the way production will. Last year a Skylane service dropped a few hundred requests on every deploy. Nothing was wrong with the code. The process ignored the polite “please stop” signal and was killed mid-request ten seconds later. This lesson covers the runtime side, ending with that shutdown.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Run it in the background</h2>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"0\"><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\"> run</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -d</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --name</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  -p</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 8080:3000</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  -e</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> PORT=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3000</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  -e</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> DATABASE_URL=postgres://notes:notes@host.docker.internal:5432/notes</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  --memory</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 512m</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --cpus</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  notes-api:dev</span></span></code></pre></div>\n<ul>\n<li><code dir=\"ltr\">-d</code> detaches, so the container runs in the background and Docker prints its ID.</li>\n<li><code dir=\"ltr\">--name notes</code> gives it a name you can type instead of the ID.</li>\n<li><code dir=\"ltr\">-p 8080:3000</code> maps <strong>host</strong> port 8080 to <strong>container</strong> port 3000. Read it as “outside:inside”. Two containers can both listen on 3000 inside, as long as they’re published on different host ports.</li>\n<li><code dir=\"ltr\">-e</code> sets environment variables. Use <code dir=\"ltr\">--env-file .env.local</code> when there are many.</li>\n<li><code dir=\"ltr\">--memory</code> and <code dir=\"ltr\">--cpus</code> set cgroup limits. If the process needs more memory than that (plus whatever swap Docker allows it on hosts that have swap), the kernel’s OOM killer ends it.</li>\n</ul>\n<p><code dir=\"ltr\">host.docker.internal</code> resolves to your machine from inside a container on Docker Desktop; on Linux add <code dir=\"ltr\">--add-host=host.docker.internal:host-gateway</code>. You’ll replace this with a proper Compose network in section 3.</p>\n<aside class=\"callout callout-why\" 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\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><circle cx=\"12\" cy=\"12\" r=\"6\"/><circle cx=\"12\" cy=\"12\" r=\"2\"/></svg><span><span class=\"callout-kind\">Why this matters</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>One image, every environment</span></p>\n<p>Configuration comes in at run time, never baked into the image. The image you tested in staging is the same bytes you run in production, with different <code dir=\"ltr\">-e</code> values. If you rebuild per environment, you’re shipping something you never tested.</p>\n</aside>\n<h2 id=\"sec-2\" tabindex=\"-1\">See what’s running and what it’s saying</h2>\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\"> ps</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">                      # running containers</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> logs</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -f</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">           # follow stdout/stderr</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> logs</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --since</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 10m</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">  # recent output only</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> inspect</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --format</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> '{{.State.Status}} {{.NetworkSettings.Ports}}'</span></span></code></pre></div>\n<p><code dir=\"ltr\">docker logs</code> shows whatever the process wrote to stdout and stderr. That’s the contract for containers: <strong>log to the standard streams</strong>, one line per event, and let the platform collect them. Writing to a log file inside the container hides logs from every tool and loses them when the container goes.</p>\n<p>To look around inside:</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\"> exec</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -it</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> sh</span></span></code></pre></div>\n<p><code dir=\"ltr\">exec</code> starts an extra process in the running container; <code dir=\"ltr\">-it</code> gives you an interactive terminal. It’s for looking, not for fixing. In section 2 you’ll move to a base image with no shell at all, and you’ll learn other ways in.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">Stopping: SIGTERM, then SIGKILL</h2>\n<p><code dir=\"ltr\">docker stop notes</code> sends <code dir=\"ltr\">SIGTERM</code> to PID 1 and waits 10 seconds (change it with <code dir=\"ltr\">-t</code>). If the process is still alive, Docker sends <code dir=\"ltr\">SIGKILL</code>, which can’t be caught. Exit codes tell you which happened:</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\"><table><thead>\n<tr>\n<th>Exit code</th>\n<th>Meaning</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>The app exited cleanly by itself</td>\n</tr>\n<tr>\n<td>143</td>\n<td>128 + 15: the process died from SIGTERM without handling it</td>\n</tr>\n<tr>\n<td>137</td>\n<td>128 + 9: killed by SIGKILL (stop timeout or out of memory)</td>\n</tr>\n</tbody>\n</table></div><p>Linux treats PID 1 specially: a signal the process hasn’t registered a handler for is ignored, instead of killing it. Node.js registers no <code dir=\"ltr\">SIGTERM</code> handler by default, so <code dir=\"ltr\">node dist/server.js</code> as PID 1 ignores <code dir=\"ltr\">docker stop</code> and gets killed after 10 seconds. In-flight requests die with it.</p>\n<p>Handle the signal: stop accepting connections, finish what’s in flight, close the database pool, exit.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"ts\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">src/server.ts</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:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> server</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> app.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">listen</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(port, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'0.0.0.0'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, () </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">`notes-api listening on ${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">port</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">));</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">process.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">on</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'SIGTERM'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, () </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'SIGTERM received, draining'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  server.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">close</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">async</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> () </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    await</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pool.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">end</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    process.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">exit</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  });</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">  // Safety net: don't hang forever on a stuck keep-alive connection</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  setTimeout</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> process.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">exit</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">), </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">8000</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">unref</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">});</span></span></code></pre></div>\n<p>Now <code dir=\"ltr\">docker stop</code> returns in well under a second and the container exits with 0. This handler matters even more in Kubernetes, which sends the same signal on every deploy, scale-down and node drain.</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>Relying on --init and skipping the handler</span></p>\n<p><code dir=\"ltr\">docker run --init</code> puts a tiny init process at PID 1 that forwards signals and reaps zombie processes. That’s useful, but forwarding SIGTERM to an app with no handler only makes it die instantly instead of after 10 seconds. Requests still drop. The handler in your code is what makes shutdown graceful.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">Restart policies and log growth</h2>\n<p>On a single server without an orchestrator, <code dir=\"ltr\">--restart unless-stopped</code> tells Docker to bring the container back after a crash or a host reboot, unless you stopped it yourself. Kubernetes and Compose have their own restart settings, so you’ll rarely set this on a laptop, but it’s the first thing to check on that one old VM nobody wants to touch.</p>\n<p>On long-lived Linux hosts, also watch log volume. The default <code dir=\"ltr\">json-file</code> logging driver keeps everything a container prints unless you configure <code dir=\"ltr\">max-size</code> and <code dir=\"ltr\">max-file</code> in the daemon settings. A chatty container can fill a disk in a weekend; I’ve been paged for exactly that.</p>\n<h2 id=\"sec-5\" tabindex=\"-1\">Clean up</h2>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"4\"><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\"> rm</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -f</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">          # stop and remove</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> container</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> prune</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">      # remove all stopped containers</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> image</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> prune</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">          # remove dangling images</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> system</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> df</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">            # what's using disk</span></span></code></pre></div>\n<p>You can now build, run, observe and stop a container properly. Section 2 turns this image into one you’d be comfortable shipping: smaller, non-root, versioned and scanned.</p>\n","toc":[{"id":"sec-1","text":"Run it in the background","level":2},{"id":"sec-2","text":"See what’s running and what it’s saying","level":2},{"id":"sec-3","text":"Stopping: SIGTERM, then SIGKILL","level":2},{"id":"sec-4","text":"Restart policies and log growth","level":2},{"id":"sec-5","text":"Clean up","level":2}],"takeaways":["<code dir=\"ltr\">-p 8080:3000</code> maps host port 8080 to container port 3000; the left side is your machine, the right side is the app.","Pass configuration with <code dir=\"ltr\">-e</code> or <code dir=\"ltr\">--env-file</code> at run time so one image serves every environment.","Containers should log to stdout and stderr; <code dir=\"ltr\">docker logs</code> and every orchestrator collect from there.","<code dir=\"ltr\">docker stop</code> sends SIGTERM, waits 10 seconds, then sends SIGKILL; exit code 137 means the process was killed.","A Node.js process at PID 1 needs its own SIGTERM handler, or it ignores the signal and gets killed mid-request."],"further":[{"title":"docker container run","url":"https://docs.docker.com/reference/cli/docker/container/run/"},{"title":"Publishing and exposing ports","url":"https://docs.docker.com/get-started/docker-concepts/running-containers/publishing-ports/"},{"title":"Process signal events in Node.js","url":"https://nodejs.org/api/process.html#signal-events"}],"quiz":[{"q":"<p>You run <code dir=\"ltr\">docker run -d -p 8080:3000 notes-api:dev</code>. Which URL reaches the API from your laptop?</p>","options":[{"text":"<code dir=\"ltr\">http://localhost:8080</code>","why":"Correct. The left side of <code dir=\"ltr\">-p</code> is the host port; Docker forwards it to port 3000 inside the container."},{"text":"<code dir=\"ltr\">http://localhost:3000</code>","why":"3000 is the container’s port. Nothing on the host listens there unless you publish it as the host side."},{"text":"<code dir=\"ltr\">http://notes-api:8080</code>","why":"Container names resolve on Docker networks, not on your laptop’s DNS, and 8080 isn’t the container port anyway."},{"text":"Either port works, because Docker maps both directions.","why":"Port mapping is one rule from one host port to one container port."}],"answer":0},{"q":"<p><code dir=\"ltr\">docker stop notes</code> takes exactly ten seconds and <code dir=\"ltr\">docker ps -a</code> shows <code dir=\"ltr\">Exited (137)</code>. What happened?</p>","options":[{"text":"The app crashed with an unhandled exception during shutdown.","why":"An unhandled exception normally exits with code 1. 137 is 128 + 9, the signal number of SIGKILL."},{"text":"Docker ran out of memory while stopping the container.","why":"137 can come from the OOM killer, but the exact 10-second delay on <code dir=\"ltr\">docker stop</code> points to the stop timeout."},{"text":"The app ignored SIGTERM, so after the 10-second grace period Docker sent SIGKILL.","why":"Correct. 137 = 128 + 9 (SIGKILL). A Node process at PID 1 with no SIGTERM handler ignores the signal and waits to be killed."},{"text":"The container exited cleanly; 137 is Docker’s normal stop code.","why":"A clean exit is 0, or 143 if the process died from an unhandled SIGTERM. 137 means it was killed."}],"answer":2},{"q":"<p>Where should notes-api write its request logs inside a container?</p>","options":[{"text":"To <code dir=\"ltr\">/var/log/notes-api.log</code>, so they survive restarts.","why":"They don’t survive. The writable layer is discarded with the container. And nobody collects files inside containers by default."},{"text":"To stdout and stderr, where <code dir=\"ltr\">docker logs</code> and Kubernetes collect them.","why":"Correct. The runtime captures the standard streams; log shippers read them from there."},{"text":"To a file in a volume, rotated by a cron job in the container.","why":"That’s a second process and a second job inside your container. Write to stdout and let the platform handle storage."},{"text":"Directly to the logging service over HTTP from the app.","why":"It couples the app to one vendor and loses logs when the network blips. Stdout is the contract every platform supports."}],"answer":1},{"q":"<p>A teammate wants to fix a typo in a config file inside the running production container with <code dir=\"ltr\">docker exec</code>. What’s the problem?</p>","options":[{"text":"<code dir=\"ltr\">docker exec</code> only works on stopped containers.","why":"It’s the opposite. <code dir=\"ltr\">exec</code> runs a new process inside a running container."},{"text":"Files inside containers are always read-only.","why":"The writable layer accepts changes, unless you’ve set <code dir=\"ltr\">--read-only</code>. The change would work, briefly."},{"text":"<code dir=\"ltr\">docker exec</code> restarts the container, causing downtime.","why":"<code dir=\"ltr\">exec</code> starts an extra process; it doesn’t restart anything."},{"text":"The change lives in that container’s writable layer and disappears on the next deploy or restart.","why":"Correct. Fix the image or the configuration passed in, then redeploy. Hand edits are invisible to everyone else."}],"answer":3}],"exercise":{"kind":"fill","language":"bash","template":"# Start notes-api in the background, reachable at localhost:8080\ndocker run {{0}} --name notes -p {{1}}:3000 notes-api:dev\n\n# Follow its output\ndocker {{2}} -f notes\n\n# Open a shell inside the running container\ndocker {{3}} -it notes sh\n\n# Stop it, allowing 30 seconds to drain\ndocker stop {{4}} 30 notes","blanks":[{"answers":["-d","--detach"]},{"answers":["8080"]},{"answers":["logs","container logs"]},{"answers":["exec","container exec"]},{"answers":["-t","--time","--timeout"]}],"title":"Day-to-day container commands","prompt":"<p>Fill in the blanks to run notes-api in the background on host port <strong>8080</strong>, follow its logs, open a shell inside it, and stop it with a 30-second grace period.</p>\n","hints":["One flag detaches the container; the host port is the left side of <code dir=\"ltr\">-p</code>.","The subcommands are the ones you’d use to read output and to run an extra process in a running container.","<code dir=\"ltr\">docker stop</code> takes a flag for the number of seconds to wait before SIGKILL."],"explanation":"<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"0\"><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\"> run</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -d</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --name</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -p</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 8080:3000</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes-api:dev</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> logs</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -f</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> exec</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -it</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> sh</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> stop</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -t</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 30</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes</span></span></code></pre></div>\n<p><code dir=\"ltr\">-d</code> detaches. In <code dir=\"ltr\">-p 8080:3000</code> the host side comes first. <code dir=\"ltr\">logs -f</code> follows stdout and stderr. <code dir=\"ltr\">exec -it</code> runs an interactive process inside the running container. <code dir=\"ltr\">stop -t 30</code> (long form <code dir=\"ltr\">--timeout</code>) waits 30 seconds after SIGTERM before sending SIGKILL, which gives a draining app time to finish in-flight requests.</p>\n","checkLabels":{}},"words":720,"readMinutes":3}