{"id":28,"date":"2026-07-15T00:00:00","date_gmt":"2026-07-15T00:00:00","guid":{"rendered":"http:\/\/\/var\/www\/html\/?p=28"},"modified":"2026-07-21T01:02:31","modified_gmt":"2026-07-21T01:02:31","slug":"docker-container-exited-with-code-137-oom-killed-limit-on-alpine-linux","status":"publish","type":"post","link":"https:\/\/butitworkedlocal.com\/docker-container-exited-with-code-137-oom-killed-limit-on-alpine-linux\/","title":{"rendered":"Troubleshooting Docker Container Exited with Code 137: OOM Killed on Alpine Linux"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Resolve Docker containers exiting with code 137 due to OOM kills on Alpine Linux. Learn to identify, diagnose, and fix out-of-memory issues effectively.<\/strong><\/p>\n\n\n<p>When a Docker container abruptly stops functioning, exhibiting an &quot;Exited (137)&quot; status, it&#039;s a strong indicator that the Linux kernel&#039;s Out-Of-Memory (OOM) killer has terminated the process. This guide provides a comprehensive, expert-level approach to diagnose and resolve these critical memory-related issues, specifically in environments utilizing Alpine Linux base images for their Docker containers.<\/p>\n<h3>Symptom &amp; Error Signature<\/h3>\n<p>The most prominent symptom is a container that repeatedly crashes, fails to start, or becomes unresponsive, leading to service disruption. Your application, whether it&#039;s a web server like Nginx, a database, or a custom microservice, will cease to function.<\/p>\n<p>You&#039;ll typically observe the following:<\/p>\n<ol>\n<li><p><strong>Container Status:<\/strong><\/p>\n<pre><code class=\"language-bash\">$ docker ps -a\nCONTAINER ID   IMAGE                 COMMAND                  CREATED         STATUS                     PORTS     NAMES\na1b2c3d4e5f6   my-alpine-app:latest  &quot;node \/app\/index.js&quot;     2 minutes ago   Exited (137) 5 seconds ago   web_app\n<\/code><\/pre>\n<\/li>\n<li><p><strong>Docker Daemon Logs (on the host system):<\/strong>\nThese logs often provide the first explicit mention of an OOM event.<\/p>\n<pre><code class=\"language-bash\">$ sudo journalctl -u docker.service | grep -i &quot;oom|killed process&quot;\nJul 15 10:30:45 hostname dockerd[1234]: containerd\/task\/v2\/sender\/pipe.go:39: &quot;OOM killer killed container_id_a1b2c3d4e5f6&quot;\nJul 15 10:30:45 hostname dockerd[1234]: shim_exit: container_id_a1b2c3d4e5f6 exit status 137\n<\/code><\/pre>\n<\/li>\n<li><p><strong>Kernel Logs (<code>dmesg<\/code> on the host system):<\/strong>\nThe definitive evidence comes from the kernel itself, detailing the OOM event.<\/p>\n<pre><code class=\"language-bash\">$ sudo dmesg -T | grep -i &quot;oom-killer|killed process&quot;\n[Wed Jul 15 10:30:44 2026] node invoked oom-killer: gfp_mask=0x100cca(GFP_HIGHUSER_MOVABLE|__GFP_COMP), order=0, oom_score_adj=0\n[Wed Jul 15 10:30:44 2026] oom-kill:constraint=CONTAINER,nodemask=(null),cpuset=docker\/a1b2c3d4e5f6,mems_allowed=0,global_oom,task_memcg=\/docker\/a1b2c3d4e5f6,task=node,pid=5678,uid=0\n[Wed Jul 15 10:30:44 2026] Memory cgroup out of memory: Killed process 5678 (node) total-vm:450000kB, anon-rss:400000kB, file-rss:0kB, shmem-rss:0kB, UID:0 pgtables:800kB oom_score_adj:0\n[Wed Jul 15 10:30:44 2026] Tasks state (memory and swap accounts):\n[Wed Jul 15 10:30:44 2026] [ pid ]   uid  tgid total_vm      rss nr_ptes nr_pmds swapents oom_score_adj name\n[Wed Jul 15 10:30:44 2026] [ 5678]     0  5678   112500   100000     200       0        0             0 node\n[Wed Jul 10:30:44 2026] oom-killer: Kill process 5678 (node) score 1000 or sacrifice child\n[Wed Jul 10:30:44 2026] Killed process 5678 (node) total-vm:450000kB, anon-rss:400000kB, file-rss:0kB, shmem-rss:0kB\n<\/code><\/pre>\n<p>This output clearly shows <code>oom-killer<\/code> being invoked and targeting <code>process 5678 (node)<\/code> within the specified Docker cgroup.<\/p>\n<\/li>\n<\/ol>\n<h3>Root Cause Analysis<\/h3>\n<p>The &quot;Exited with code 137&quot; error specifically means the container process received a <code>SIGKILL<\/code> signal (signal 9). When this is combined with &quot;OOM killed,&quot; it unambiguously indicates that the Linux kernel&#039;s Out-Of-Memory (OOM) killer decided to terminate the container&#039;s main process.<\/p>\n<p>Here&#039;s a breakdown of the underlying reasons:<\/p>\n<ul>\n<li><p><strong>Linux OOM Killer:<\/strong> This is a critical kernel mechanism designed to maintain system stability. When the system&#039;s memory (RAM + swap) is exhausted, the OOM killer steps in to free up resources by terminating one or more processes. It uses an internal heuristic (<code>oom_score<\/code>) to decide which processes are &quot;least important&quot; to kill.<\/p>\n<\/li>\n<li><p><strong>Docker Memory Limits (cgroups):<\/strong> Docker leverages Linux Control Groups (cgroups) to isolate and limit resource usage for containers.<\/p>\n<ul>\n<li><strong><code>mem_limit<\/code> (or <code>-m<\/code> flag):<\/strong> This sets the maximum amount of RAM a container can use.<\/li>\n<li><strong><code>memswap_limit<\/code> (or <code>--memory-swap<\/code> flag):<\/strong> This defines the combined limit for RAM and swap space.<\/li>\n<li>If a container&#039;s processes attempt to allocate memory beyond its <code>mem_limit<\/code>, the OOM killer is triggered <em>within that container&#039;s cgroup<\/em>, effectively killing the container without affecting other processes on the host or in other containers (unless the host itself is completely out of memory). If <code>memswap_limit<\/code> is hit, and there&#039;s no more swap available, the OOM killer will also be triggered.<\/li>\n<\/ul>\n<\/li>\n<li><p><strong>Application Memory Profile:<\/strong><\/p>\n<ul>\n<li><strong>Memory Leak:<\/strong> The application has a bug that causes it to continuously consume more memory over time without releasing it.<\/li>\n<li><strong>Memory Spike:<\/strong> The application might have a legitimate, but unexpected, memory spike during certain operations (e.g., loading large datasets, complex computations, garbage collection cycles), especially during startup or peak load.<\/li>\n<li><strong>Incorrect Configuration:<\/strong> Application-specific memory settings (e.g., JVM heap size <code>-Xmx<\/code>, Node.js <code>--max-old-space-size<\/code>, PHP <code>memory_limit<\/code>) are misconfigured or too high for the allocated container limits.<\/li>\n<li><strong>Unoptimized Code\/Libraries:<\/strong> Using inefficient data structures or libraries that are memory-hungry.<\/li>\n<\/ul>\n<\/li>\n<li><p><strong>Host System Memory Exhaustion:<\/strong> While less common when cgroup limits are in place, if the host system itself runs out of memory (e.g., too many containers, other demanding host processes, insufficient swap), the OOM killer might target container processes even if they haven&#039;t explicitly hit their Docker limits, or if limits are unset\/very high.<\/p>\n<\/li>\n<li><p><strong>Alpine Linux Specifics:<\/strong> While Alpine&#039;s small base image size (often just a few MB) reduces the <em>baseline<\/em> memory footprint, it doesn&#039;t fundamentally change how the OOM killer or cgroups operate. The application <em>inside<\/em> the Alpine container is still subject to the same memory demands. Often, developers choose Alpine for its small size and then forget to properly account for the application&#039;s actual memory needs, leading to hitting tighter limits more quickly.<\/p>\n<\/li>\n<\/ul>\n<h3>Step-by-Step Resolution<\/h3>\n<p>Resolving OOM issues requires a systematic approach, combining host-level diagnostics, container-level inspection, and application-specific tuning.<\/p>\n<h4>1. Confirm OOM Kill and Identify the Victim Process<\/h4>\n<p>Before making any changes, confirm the container was indeed OOM killed and identify the exact process within the container that was targeted.<\/p>\n<ul>\n<li><p><strong>Check <code>dmesg<\/code> output on the host:<\/strong><\/p>\n<pre><code class=\"language-bash\">sudo dmesg -T | grep -E &quot;oom-killer|killed process&quot;\n<\/code><\/pre>\n<p>Look for entries similar to the &quot;Symptom &amp; Error Signature&quot; section, specifically mentioning <code>Memory cgroup out of memory<\/code> and the container&#039;s cgroup path (e.g., <code>\/docker\/a1b2c3d4e5f6<\/code>). Note the <code>pid<\/code> and <code>name<\/code> of the killed process.<\/p>\n<\/li>\n<li><p><strong>Check Docker daemon logs:<\/strong><\/p>\n<pre><code class=\"language-bash\">sudo journalctl -u docker.service | grep &quot;OOM killer killed container&quot;\n<\/code><\/pre>\n<p>This confirms Docker&#039;s daemon saw the OOM event.<\/p>\n<\/li>\n<\/ul>\n<h4>2. Inspect Container Memory Limits and Usage<\/h4>\n<p>Understand what memory limits, if any, were applied to the container, and review historical usage if possible.<\/p>\n<ul>\n<li><p><strong>Check configured memory limits:<\/strong><\/p>\n<pre><code class=\"language-bash\">docker inspect &lt;container_id_or_name&gt; | grep -E &quot;Memory|Swap&quot;\n<\/code><\/pre>\n<p>Look for <code>Memory<\/code> and <code>MemorySwap<\/code> under <code>HostConfig<\/code>. Values of <code>0<\/code> typically mean unlimited, but this is relative to the host&#039;s physical memory.\nExample output:<\/p>\n<pre><code class=\"language-json\">        &quot;Memory&quot;: 0,\n        &quot;MemorySwap&quot;: 0,\n        &quot;MemoryReservation&quot;: 0,\n        &quot;KernelMemory&quot;: 0,\n        &quot;OomKillDisable&quot;: false,\n        &quot;OomScoreAdj&quot;: 0,\n<\/code><\/pre>\n<p>If <code>Memory<\/code> is <code>0<\/code>, the container can consume all available host memory, making the host&#039;s overall memory the limiting factor. If it&#039;s a specific value (e.g., <code>536870912<\/code> for 512MB), that&#039;s your hard limit.<\/p>\n<\/li>\n<li><p><strong>Monitor live memory usage (if the container starts but crashes later):<\/strong><\/p>\n<pre><code class=\"language-bash\">docker stats &lt;container_id_or_name&gt;\n<\/code><\/pre>\n<p>This command provides real-time statistics, including memory usage, against the configured limits. Run this frequently to observe memory trends before a crash.<\/p>\n<\/li>\n<\/ul>\n<h4>3. Analyze Application Memory Footprint and Configuration<\/h4>\n<p>This is often the most critical step. You need to understand how much memory your application genuinely needs.<\/p>\n<ul>\n<li><strong>Profile the application:<\/strong><ul>\n<li><strong>Run with generous limits:<\/strong> Temporarily remove or significantly increase memory limits (<code>-m 2g --memory-swap -1<\/code>) on a test environment to see how much memory the application consumes at peak load and steady state.<\/li>\n<li><strong>Use in-container tools:<\/strong> If your Alpine image has tools like <code>ps<\/code> or <code>top<\/code> (you might need to install <code>procps<\/code>), exec into a running container:<pre><code class=\"language-bash\">docker exec -it &lt;container_id&gt; sh\n\/ # apk add procps # if not already installed\n\/ # ps aux\n\/ # top\n<\/code><\/pre>\nMonitor the <code>VSZ<\/code> (virtual size) and <code>RSS<\/code> (resident set size) columns.<\/li>\n<li><strong>Language-specific profiling:<\/strong><ul>\n<li><strong>Java:<\/strong> Tune <code>JAVA_OPTS<\/code> with <code>-Xmx<\/code> (max heap size) and <code>-Xms<\/code> (initial heap size). Remember that JVM also uses off-heap memory.<\/li>\n<li><strong>Node.js:<\/strong> Check for memory leaks using built-in profiling tools or Chrome DevTools. Adjust <code>--max-old-space-size<\/code> if needed via <code>NODE_OPTIONS<\/code>.<\/li>\n<li><strong>PHP:<\/strong> Adjust <code>memory_limit<\/code> in <code>php.ini<\/code> or via <code>ini_set()<\/code>.<\/li>\n<li><strong>Python:<\/strong> Libraries like <code>memory_profiler<\/code> can help.<\/li>\n<li><strong>Go:<\/strong> Go applications are often very efficient, but Goroutine leaks can lead to memory growth.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Review application logs:<\/strong> Look for errors or warnings that might indicate resource exhaustion, large data operations, or unhandled exceptions that could trigger memory spikes.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h4>4. Adjust Docker Container Memory Limits<\/h4>\n<p>Based on your analysis, you will likely need to increase the memory allocated to your container.<\/p>\n<ul>\n<li><p><strong>Increase <code>mem_limit<\/code>:<\/strong> This directly increases the available RAM.<\/p>\n<ul>\n<li><strong>Docker CLI:<\/strong><pre><code class=\"language-bash\">docker run -d --name my-app --restart always -m 768m my-alpine-app:latest\n<\/code><\/pre>\nThis sets the RAM limit to 768MB.<\/li>\n<li><strong>Docker Compose:<\/strong> This is the recommended approach for defining services.<pre><code class=\"language-yaml\"># docker-compose.yml\nversion: &#039;3.8&#039;\nservices:\n  my_app:\n    image: my-alpine-app:latest\n    container_name: my-app\n    restart: always\n    mem_limit: 768m           # Max RAM usage\n    mem_reservation: 512m     # Soft limit, Docker tries to keep usage below this\n    memswap_limit: 768m       # Total RAM + SWAP. Set to -1 for unlimited swap\n<\/code><\/pre>\n<blockquote class=\"important\"><p>If <code>memswap_limit<\/code> is set to the same value as <code>mem_limit<\/code>, it means the container has <em>no<\/em> swap available. If the application hits its <code>mem_limit<\/code>, it will immediately be OOM killed. Setting <code>memswap_limit<\/code> to <code>-1<\/code> allows the container to use unlimited swap on the host if its <code>mem_limit<\/code> is reached. While this prevents OOM kills, it can lead to severe performance degradation if the application frequently swaps. A better approach is to set <code>memswap_limit<\/code> higher than <code>mem_limit<\/code> (e.g., <code>1g<\/code> for <code>512m<\/code> RAM limit) to allow for some controlled swapping.<\/p>\n<\/blockquote>\n<\/li>\n<\/ul>\n<\/li>\n<li><p><strong>Consider <code>mem_reservation<\/code>:<\/strong> This is a &quot;soft limit.&quot; Docker will try to keep the container&#039;s memory usage below this value, but will allow it to exceed it up to <code>mem_limit<\/code> if the host has spare memory. This is useful for resource scheduling.<\/p>\n<\/li>\n<\/ul>\n<h4>5. Optimize the Application and Dockerfile<\/h4>\n<p>Reduce your application&#039;s memory footprint or ensure it&#039;s configured to respect container limits.<\/p>\n<ul>\n<li><p><strong>Application-level tuning:<\/strong><\/p>\n<ul>\n<li><strong>JVM:<\/strong> Reduce <code>-Xmx<\/code> if it&#039;s too high for the container&#039;s <code>mem_limit<\/code>. Ensure the container&#039;s <code>mem_limit<\/code> is greater than <code>-Xmx<\/code> to account for off-heap memory.<\/li>\n<li><strong>Node.js:<\/strong> <code>NODE_OPTIONS=--max-old-space-size=512<\/code> (for 512MB) can prevent Node from attempting to use too much memory.<\/li>\n<li><strong>PHP:<\/strong> Set <code>memory_limit<\/code> in <code>php.ini<\/code> to a value below your Docker container&#039;s <code>mem_limit<\/code>.<\/li>\n<li><strong>Caching:<\/strong> Implement efficient caching strategies to reduce repetitive memory-intensive operations.<\/li>\n<li><strong>Data Structures:<\/strong> Review code for inefficient data structures or algorithms that cause excessive memory allocation.<\/li>\n<li><strong>Garbage Collection:<\/strong> Tune garbage collection parameters for languages like Java or Node.js.<\/li>\n<\/ul>\n<\/li>\n<li><p><strong>Dockerfile optimization (especially for Alpine):<\/strong><\/p>\n<ul>\n<li><strong>Multi-stage builds:<\/strong> Use multi-stage builds to create extremely lean final images that only contain the necessary runtime dependencies, removing build-time tools and intermediate files.<pre><code class=\"language-dockerfile\"># Example Multi-stage Dockerfile for Node.js on Alpine\n# Stage 1: Build dependencies\nFROM node:18-alpine AS builder\nWORKDIR \/app\nCOPY package*.json .\/\nRUN npm ci --production --frozen-lockfile\nCOPY . .\nRUN npm run build # if you have a build step\n\n# Stage 2: Runtime image\nFROM node:18-alpine\nWORKDIR \/app\nCOPY --from=builder \/app\/node_modules .\/node_modules\nCOPY --from=builder \/app\/dist .\/dist # Assuming build output is in dist\nCOPY --from=builder \/app\/package.json .\/package.json\nCOPY --from=builder \/app\/index.js .\/index.js # Your main app file\nEXPOSE 3000\nCMD [&quot;node&quot;, &quot;index.js&quot;]\n<\/code><\/pre>\n<\/li>\n<li><strong>Minimalist packages:<\/strong> When installing packages with <code>apk add<\/code>, only install what&#039;s absolutely necessary. Use <code>apk add --no-cache<\/code> to prevent package caches from increasing image size.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h4>6. Increase Host System Memory or Swap (If Necessary)<\/h4>\n<p>If multiple containers are consistently hitting OOM issues, or the host itself is frequently low on memory, it might be time to provision more RAM for your server.<\/p>\n<ul>\n<li><strong>Add more RAM:<\/strong> The most straightforward solution for overall system capacity.<\/li>\n<li><strong>Increase host swap space:<\/strong> While not a substitute for RAM, sufficient swap space can prevent the host&#039;s global OOM killer from being invoked during temporary memory spikes.<pre><code class=\"language-bash\">&gt; [!IMPORTANT]\n&gt; Relying heavily on swap can severely degrade application performance due to disk I\/O. Use it as a last resort or for minor overflow, not as a primary memory extension.\n\n# Example: Add a 4GB swap file on Ubuntu\/Debian\nsudo fallocate -l 4G \/swapfile\nsudo chmod 600 \/swapfile\nsudo mkswap \/swapfile\nsudo swapon \/swapfile\n\n# Make swap persistent across reboots by adding to \/etc\/fstab:\necho &#039;\/swapfile none swap sw 0 0&#039; | sudo tee -a \/etc\/fstab\n<\/code><\/pre>\n<\/li>\n<\/ul>\n<h4>7. Adjust OOM Score (Advanced &#8211; Use with Extreme Caution)<\/h4>\n<p>For highly critical containers that <em>must<\/em> stay alive at all costs (e.g., a monitoring agent), you can adjust their <code>oom_score_adj<\/code>. A lower score makes the process less likely to be chosen by the OOM killer.<\/p>\n<ul>\n<li><p><strong>Docker CLI:<\/strong><\/p>\n<pre><code class=\"language-bash\">docker run -d --name critical-service --oom-score-adj -500 my-alpine-app:latest\n<\/code><\/pre>\n<p>Values range from -1000 (least likely to be killed) to 1000 (most likely).<\/p>\n<blockquote class=\"warning\"><p>Lowering the OOM score for a memory-hungry process can lead to the OOM killer targeting other, potentially more critical, system processes or even the Docker daemon itself, potentially destabilizing the host system. Use this only for processes that are truly essential and where all other alternatives for memory optimization and provisioning have been exhausted.<\/p>\n<\/blockquote>\n<\/li>\n<\/ul>\n<h4>8. Implement Robust Monitoring and Alerting<\/h4>\n<p>Proactive monitoring can help identify memory pressure before it leads to a full OOM kill.<\/p>\n<ul>\n<li><strong>Memory Usage:<\/strong> Monitor container memory usage (RSS, cache, swap) using tools like Prometheus, Grafana, cAdvisor, or specialized APM solutions.<\/li>\n<li><strong>Container Status:<\/strong> Set up alerts for containers exiting with a <code>137<\/code> status.<\/li>\n<li><strong>Host System Metrics:<\/strong> Monitor overall host memory usage, swap usage, and <code>dmesg<\/code> output for OOM events.<\/li>\n<li><strong>Application Metrics:<\/strong> If your application exposes memory metrics, integrate them into your monitoring stack.<\/li>\n<\/ul>\n<p>By following these steps, you can effectively diagnose and resolve Docker container OOM issues, ensuring the stability and reliability of your containerized applications on Alpine Linux.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Resolve Docker containers exiting with code 137 due to OOM kills on Alpine Linux. Learn to identify, diagnose, and fix out-of-memory issues effectively.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[68],"tags":[84,85,86,69,24,66,83,6],"class_list":["post-28","post","type-post","status-publish","format-standard","hentry","category-containers","tag-alpine","tag-cgroups","tag-code-137","tag-docker","tag-linux","tag-memory","tag-oom","tag-troubleshooting"],"_links":{"self":[{"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/posts\/28","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/comments?post=28"}],"version-history":[{"count":2,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/posts\/28\/revisions"}],"predecessor-version":[{"id":276,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/posts\/28\/revisions\/276"}],"wp:attachment":[{"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/media?parent=28"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/categories?post=28"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/tags?post=28"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}