{"id":4726,"date":"2026-02-28T21:01:57","date_gmt":"2026-02-28T15:31:57","guid":{"rendered":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/"},"modified":"2026-02-28T21:01:57","modified_gmt":"2026-02-28T15:31:57","slug":"master-docker-compose-the-ultimate-guide-for-developers","status":"publish","type":"post","link":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/","title":{"rendered":"Master Docker Compose: The Ultimate Guide for Developers"},"content":{"rendered":"<p>text<br \/>\n[2024-05-22T03:14:02.881Z] ERROR: Container &#8220;api-gateway&#8221; exited with code 137 (OOMKilled)<br \/>\n[2024-05-22T03:14:05.112Z] CRITICAL: Service &#8220;auth-provider&#8221; failed to bind to 0.0.0.0:8080. Address already in use.<br \/>\n[2024-05-22T03:14:05.115Z] FATAL: Dependency check failed. &#8220;postgres-db&#8221; not reachable at 172.17.0.2:5432.<br \/>\n[2024-05-22T03:14:05.118Z] STACK_TRACE: deploy.sh: line 44: docker run -d &#8211;name api-gateway &#8230;<br \/>\n[2024-05-22T03:14:05.120Z] SYSTEM_STATE: Load Average 45.12, 38.01, 22.10. Disk I\/O 98% saturated.<br \/>\n[2024-05-22T03:14:05.122Z] TERMINATING: Manual cleanup required. God is dead and we killed Him with a shell script.<\/p>\n<pre class=\"codehilite\"><code># The YAML That Bit Back: A Post-Mortem on Orchestration Laziness\n\nI\u2019ve been awake for 48 hours. My blood is 60% espresso and 40% spite. While the rest of the engineering team was dreaming of &quot;clean code&quot; and &quot;agile velocity,&quot; I was watching our production environment melt into a puddle of unrouted packets and orphaned processes. The culprit? A &quot;clever&quot; bash script named `deploy.sh`, written by a junior developer who thought `docker compose` was &quot;too much overhead for a simple microservice stack.&quot;\n\nWe are running Docker Engine v25.0.3. We have the tools. We have the specs. And yet, I spent my Sunday morning manually killing zombie containers because someone decided that a sequence of `docker run` commands was a viable orchestration strategy. It isn\u2019t. It\u2019s a suicide note written in Bourne Shell.\n\n## 1. The Bash Script Death Spiral\n\nThe incident began when the `deploy.sh` script attempted to update the `api-gateway`. In a sane world\u2014a world governed by `docker compose`\u2014this would be an atomic operation. Instead, the script executed a `docker stop api-gateway` followed by a `docker rm api-gateway`. Between those two commands, the health check for our load balancer failed, the auto-scaler panicked, and the script continued blindly to the next line.\n\nManual `docker run` commands are a professional liability because they lack state awareness. When you execute `docker run`, you are throwing a binary at the kernel and hoping it sticks. There is no source of truth. There is no desired state. There is only the immediate, fleeting command.\n\nThe junior\u2019s script didn't account for the fact that the `auth-provider` container had crashed five minutes earlier. Because there was no orchestration layer to verify the health of dependencies, the `api-gateway` started, tried to connect to a non-existent auth service, and entered a crash loop. But the script didn't care. It just kept running `docker run` for the next ten services, each one failing more spectacularly than the last.\n\nBy the time I was paged at 3:15 AM, the host was a graveyard of containers in `Exited (1)` states, all of them holding onto port bindings that prevented the script from being re-run. This is the &quot;Death Spiral.&quot; Without the declarative nature of `docker compose`, you aren't managing a system; you're playing a high-stakes game of Whac-A-Mole where the hammer is broken and the moles are on fire.\n\n## 2. The Race Condition of the Unchecked Container\n\nThe second stage of the failure was the database. In the junior's &quot;orchestration&quot; script, the PostgreSQL container was started first, followed immediately by the application. \n\n```bash\n# The &quot;Clever&quot; Way (i.e., The Wrong Way)\ndocker run -d --name postgres-db postgres:16\ndocker run -d --name app-service my-app:latest\n<\/code><\/pre>\n<p>The problem? PostgreSQL takes approximately 10 to 15 seconds to initialize its internal storage and start listening on port 5432. The application container, written in a language that prizes &#8220;startup performance,&#8221; tried to connect in 0.5 seconds. It failed. It died. It didn&#8217;t have a retry loop because &#8220;the infrastructure should handle it.&#8221;<\/p>\n<p>This is where we exploit the <code>depends_on<\/code> feature of the Compose specification v2.24.6. We don&#8217;t just want the container to be <em>running<\/em>; we want it to be <em>ready<\/em>.<\/p>\n<pre class=\"codehilite\"><code class=\"language-yaml\">services:\n  postgres-db:\n    image: postgres:16\n    healthcheck:\n      test: [&quot;CMD-SHELL&quot;, &quot;pg_isready -U postgres&quot;]\n      interval: 5s\n      timeout: 5s\n      retries: 5\n    networks:\n      - backend_mesh\n\n  app-service:\n    image: my-app:latest\n    depends_on:\n      postgres-db:\n        condition: service_healthy\n    networks:\n      - backend_mesh\n    deploy:\n      resources:\n        limits:\n          cpus: '0.50'\n          memory: 512M\n<\/code><\/pre>\n<p>In this snippet, <code>docker compose<\/code> acts as the adult in the room. It understands the dependency graph. It waits for the <code>pg_isready<\/code> command to return a zero exit code before it even attempts to pull the <code>app-service<\/code> image. This prevents the 3 AM &#8220;Database not found&#8221; errors that haunt my nightmares. The junior dev&#8217;s bash script has no concept of a <code>healthcheck<\/code>. It only knows if the <code>docker run<\/code> command itself succeeded, which it always does, because the daemon successfully <em>started<\/em> the container, even if the process inside is currently vomiting stack traces.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_80 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<label for=\"ez-toc-cssicon-toggle-item-69d8bc33842a1\" class=\"ez-toc-cssicon-toggle-label\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/label><input type=\"checkbox\"  id=\"ez-toc-cssicon-toggle-item-69d8bc33842a1\"  aria-label=\"Toggle\" \/><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/#3_Networking_is_a_Lie_and_the_Default_Bridge_is_a_Trap\" >3. Networking is a Lie (and the Default Bridge is a Trap)<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/#4_The_UIDGID_Purgatory_of_Persistent_Volumes\" >4. The UID\/GID Purgatory of Persistent Volumes<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/#5_Environment_Variable_Poisoning_and_Secret_Leakage\" >5. Environment Variable Poisoning and Secret Leakage<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/#6_Profiles_Pruning_the_Resource-Hungry_Forest\" >6. Profiles: Pruning the Resource-Hungry Forest<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/#7_The_Orchestration_Tax_and_the_V2_Specification\" >7. The Orchestration Tax and the V2 Specification<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/#Hard_Truths\" >Hard Truths<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/#Related_Articles\" >Related Articles<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"3_Networking_is_a_Lie_and_the_Default_Bridge_is_a_Trap\"><\/span>3. Networking is a Lie (and the Default Bridge is a Trap)<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>If you don&#8217;t define a network in <code>docker compose<\/code>, or if you manually run containers, they often end up on the default <code>bridge<\/code> network. This is a legacy wasteland. On the default bridge, containers cannot resolve each other by name. You have to use IP addresses. <\/p>\n<p>The junior\u2019s script tried to solve this by parsing <code>docker inspect<\/code> output to find the IP of the database and passing it as an environment variable to the app. <\/p>\n<p><strong>War Story:<\/strong> Three years ago, at a fintech startup that shall remain nameless, we had a dev who did exactly this. One night, the Docker daemon restarted. The containers came back up in a different order. The database, which used to be <code>172.17.0.2<\/code>, was now <code>172.17.0.3<\/code>. The application spent four hours sending encrypted transaction data to a defunct Nginx cache container that happened to grab the old IP. We lost $40k in unrecoverable API calls because someone thought <code>docker compose<\/code> networks were &#8220;too complex.&#8221;<\/p>\n<p>Using <code>docker compose<\/code> forces the creation of a user-defined bridge network. This provides automatic DNS resolution. The app-service can simply look for <code>postgres-db<\/code>.<\/p>\n<pre class=\"codehilite\"><code class=\"language-yaml\">networks:\n  backend_mesh:\n    driver: bridge\n    ipam:\n      config:\n        - subnet: 10.5.0.0\/16\n          gateway: 10.5.0.1\n<\/code><\/pre>\n<p>By explicitly defining the network, we isolate the traffic. The <code>api-gateway<\/code> doesn&#8217;t need to be on the same network as the <code>postgres-db<\/code>. We can create a <code>frontend_net<\/code> and a <code>backend_net<\/code>, and only the <code>app-service<\/code> sits on both. This is basic security posture, yet it&#8217;s impossible to manage via a bash script without writing a 500-line wrapper around <code>docker network create<\/code> and <code>docker network connect<\/code>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"4_The_UIDGID_Purgatory_of_Persistent_Volumes\"><\/span>4. The UID\/GID Purgatory of Persistent Volumes<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We hit a Sev-0 because the logs couldn&#8217;t be written. Why? Because the bash script ran <code>docker run -v \/var\/log\/app:\/app\/logs<\/code>. On the host, <code>\/var\/log\/app<\/code> was owned by <code>root<\/code>. Inside the container, the app was running as <code>node<\/code> (UID 1000). <\/p>\n<p>Result: <code>EACCES: permission denied, open '\/app\/logs\/error.log'<\/code>.<\/p>\n<p>The container crashed. The bash script, in its infinite wisdom, saw the crash and tried to restart it. But it didn&#8217;t clean up the volume. It just kept trying, creating a loop that filled the kernel&#8217;s process table.<\/p>\n<p>In <code>docker compose<\/code>, we can manage volume definitions and even use <code>user:<\/code> mapping or <code>bind<\/code> mounts with specific propagation settings. But more importantly, Compose allows us to standardize the environment across every developer&#8217;s machine. <\/p>\n<pre class=\"codehilite\"><code class=\"language-yaml\">services:\n  app-service:\n    image: my-app:latest\n    volumes:\n      - type: bind\n        source: .\/logs\n        target: \/app\/logs\n        read_only: false\n      - type: volume\n        source: app_data\n        target: \/data\n    user: &quot;${UID}:${GID}&quot;\n\nvolumes:\n  app_data:\n    driver: local\n<\/code><\/pre>\n<p>By using <code>${UID}:${GID}<\/code>, we can pass the host user&#8217;s identity into the container at runtime. This prevents the &#8220;it works on my machine&#8221; syndrome where a dev runs everything as root on their Ubuntu laptop, but the production RHEL server (rightfully) screams in agony. The junior&#8217;s script had hardcoded paths. When it ran on the CI\/CD runner, it tried to mount <code>\/Users\/juniordev\/project\/logs<\/code>. There is no <code>\/Users<\/code> on a Linux production node. The deployment failed, the script didn&#8217;t catch the error, and we pushed a broken config to the entire cluster.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"5_Environment_Variable_Poisoning_and_Secret_Leakage\"><\/span>5. Environment Variable Poisoning and Secret Leakage<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>The bash script was a sieve for secrets. To get the environment variables into the containers, the script used a series of <code>-e<\/code> flags. <\/p>\n<pre class=\"codehilite\"><code class=\"language-bash\">docker run -e DB_PASSWORD=$DB_PASSWORD -e API_KEY=$API_KEY ...\n<\/code><\/pre>\n<p>Do you know where those variables end up? In the process list. Anyone with access to the host can run <code>ps aux<\/code> or <code>docker inspect<\/code> and see the production database password in plain text. It\u2019s an auditor\u2019s nightmare and a security engineer\u2019s reason for early retirement.<\/p>\n<p><code>docker compose<\/code> supports <code>env_file<\/code> and, more importantly, it respects the hierarchy of configuration. We can use a <code>.env<\/code> file for local development and override it with actual secrets management in production.<\/p>\n<pre class=\"codehilite\"><code class=\"language-yaml\">services:\n  backend:\n    image: backend:v1.2.3\n    env_file:\n      - .env.base\n      - .env.production\n    environment:\n      - NODE_ENV=production\n      - DEBUG=false\n    secrets:\n      - db_password\n      - api_key\n\nsecrets:\n  db_password:\n    external: true\n  api_key:\n    file: .\/secrets\/api_key.txt\n<\/code><\/pre>\n<p>Using the <code>secrets<\/code> directive in Compose (even in non-Swarm mode, though it&#8217;s limited to file mounts) is a step toward sanity. It separates the <em>configuration<\/em> of the application from the <em>credentials<\/em> of the application. The junior&#8217;s script blurred these lines until they were non-existent. I found the production Stripe key in the shell history of the jump box. I\u2019m still vibrating with rage.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"6_Profiles_Pruning_the_Resource-Hungry_Forest\"><\/span>6. Profiles: Pruning the Resource-Hungry Forest<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Our local development environment is a beast. We have 42 microservices. No single developer needs all 42 running to fix a CSS bug in the billing UI. The junior&#8217;s bash script, however, didn&#8217;t have &#8220;modes.&#8221; It just started everything. <\/p>\n<p>This led to &#8220;The Great Meltdown of Tuesday,&#8221; where three new hires tried to run the script simultaneously on their 16GB MacBooks. The resulting OOM kills and swap-file thrashing brought the local network to its knees as Docker tried to pull 80GB of images at once.<\/p>\n<p>Compose V2 (specifically v2.24.6) handles this with <code>profiles<\/code>. <\/p>\n<pre class=\"codehilite\"><code class=\"language-yaml\">services:\n  frontend:\n    image: frontend:latest\n    profiles: [&quot;ui&quot;, &quot;full-stack&quot;]\n    ports:\n      - &quot;3000:3000&quot;\n\n  billing-service:\n    image: billing:latest\n    profiles: [&quot;billing&quot;, &quot;full-stack&quot;]\n\n  legacy-monolith:\n    image: monolith:latest\n    profiles: [&quot;debug&quot;]\n    logging:\n      driver: &quot;json-file&quot;\n      options:\n        max-size: &quot;10m&quot;\n<\/code><\/pre>\n<p>With <code>docker compose --profile ui up<\/code>, a developer only gets what they need. The bash script didn&#8217;t have this granularity. It was all or nothing. And &#8220;all&#8221; usually meant the developer&#8217;s machine became a very expensive space heater. <\/p>\n<p>The <code>profiles<\/code> feature also allows us to include debugging tools\u2014like a containerized Wireshark or a database GUI\u2014that should <em>never<\/em> be started in production but are invaluable in staging. The bash script just ran whatever was in the <code>docker run<\/code> list, meaning we accidentally deployed <code>adminer<\/code> (a database management tool) to a public-facing endpoint. We were lucky the firewall caught it. The script certainly didn&#8217;t.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"7_The_Orchestration_Tax_and_the_V2_Specification\"><\/span>7. The Orchestration Tax and the V2 Specification<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Let&#8217;s talk about the nuances of <code>tty: true<\/code> and <code>stdin_open: true<\/code>. In a manual <code>docker run<\/code>, you often forget these. Then you wonder why your process doesn&#8217;t receive <code>SIGTERM<\/code> signals and takes 10 seconds to die (until the kernel sends a <code>SIGKILL<\/code>). <\/p>\n<p>When you use <code>docker compose<\/code>, the lifecycle management is handled according to the Compose Specification. When I run <code>docker compose stop<\/code>, the plugin sends <code>SIGTERM<\/code> to the processes in the correct order, respecting the <code>stop_grace_period<\/code>. <\/p>\n<p>The junior&#8217;s script used <code>docker kill<\/code>. It didn&#8217;t wait for the application to flush its buffers or close database connections. We ended up with corrupted WAL files in Postgres and half-written JSON payloads in our S3 buckets. <\/p>\n<p>The V2 specification (v2.24.6) also introduced better support for the <code>build<\/code> context. <\/p>\n<pre class=\"codehilite\"><code class=\"language-yaml\">services:\n  custom-app:\n    build:\n      context: .\n      dockerfile: Dockerfile.prod\n      args:\n        - BUILD_VERSION=1.2.3\n      cache_from:\n        - type=registry,ref=myrepo\/cache:latest\n    image: myrepo\/custom-app:v1.2.3\n<\/code><\/pre>\n<p>This allows us to exploit BuildKit features like cache imports and multi-stage build targets directly from the orchestration file. The bash script was doing <code>docker build -t my-app .<\/code> every single time, ignoring the cache and wasting 20 minutes of CI time per PR. <\/p>\n<p>I spent four hours of my shift just rewriting the networking bridge configuration because the bash script had created a conflict with the corporate VPN&#8217;s routing table. <code>docker compose<\/code> allows you to define the <code>com.docker.network.bridge.name<\/code> and other driver options to avoid this.<\/p>\n<pre class=\"codehilite\"><code class=\"language-yaml\">networks:\n  corporate_safe:\n    driver: bridge\n    driver_opts:\n      com.docker.network.bridge.name: br-prod-safe\n      com.docker.network.bridge.enable_icc: &quot;true&quot;\n<\/code><\/pre>\n<p>The junior didn&#8217;t know what ICC (Inter-Container Communication) was. They just knew that &#8220;sometimes the containers can&#8217;t talk,&#8221; so they disabled the host firewall. Let that sink in. They disabled the firewall on a production node because they couldn&#8217;t figure out Docker networking.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Hard_Truths\"><\/span>Hard Truths<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li>Your bash script is not &#8220;simpler&#8221; than YAML; it is a debt-laden hallucination that will fail the moment a network packet is dropped.<\/li>\n<li>If you are using <code>docker run<\/code> in a production environment, you are not an SRE; you are a digital arsonist.<\/li>\n<li>&#8220;It works on my machine&#8221; is a valid reason for immediate revocation of SSH access.<\/li>\n<li>The default Docker bridge is a security hole and a DNS nightmare; use named networks or don&#8217;t use Docker at all.<\/li>\n<li><code>depends_on<\/code> without a <code>healthcheck<\/code> is just a race condition with a fancy name.<\/li>\n<li>If you don&#8217;t set memory and CPU limits in your Compose file, you are giving your containers a license to kill the host.<\/li>\n<li>The time you &#8220;save&#8221; by avoiding <code>docker compose<\/code> will be repaid tenfold in the form of 3 AM incidents and my caffeine-induced wrath.<\/li>\n<li>Version 2 of the Compose plugin is not a suggestion; it is the standard. Use it or find a job in a field where failure doesn&#8217;t involve a pager.<\/li>\n<li>A container that requires manual <code>chown<\/code> commands to start is a failed container.<\/li>\n<li>Orchestration is not about making things easy; it\u2019s about making them predictable. Your script is the opposite of predictable.<\/li>\n<\/ul>\n<p>I&#8217;m going to sleep now. If I get paged because someone touched that <code>deploy.sh<\/code> script again, I\u2019m not fixing the server. I\u2019m deleting the repository. Use <code>docker compose<\/code>. It\u2019s not for the machine\u2019s sake; it\u2019s for mine.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Related_Articles\"><\/span>Related Articles<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Explore more insights and best practices:<\/p>\n<ul>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/3-simple-ways-to-create-bootable-usb-in-ubuntu-linux\/\">3 Simple Ways To Create Bootable Usb In Ubuntu Linux<\/a><\/li>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/how-to-install-latest-php-7-3-on-ubuntu-18-04\/\">How To Install Latest Php 7 3 On Ubuntu 18 04<\/a><\/li>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/what-is-a-docker-container-a-complete-guide-for-beginners\/\">What Is A Docker Container A Complete Guide For Beginners<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>text [2024-05-22T03:14:02.881Z] ERROR: Container &#8220;api-gateway&#8221; exited with code 137 (OOMKilled) [2024-05-22T03:14:05.112Z] CRITICAL: Service &#8220;auth-provider&#8221; failed to bind to 0.0.0.0:8080. Address already in use. [2024-05-22T03:14:05.115Z] FATAL: Dependency check failed. &#8220;postgres-db&#8221; not reachable at 172.17.0.2:5432. [2024-05-22T03:14:05.118Z] STACK_TRACE: deploy.sh: line 44: docker run -d &#8211;name api-gateway &#8230; [2024-05-22T03:14:05.120Z] SYSTEM_STATE: Load Average 45.12, 38.01, 22.10. Disk I\/O 98% saturated. &#8230; <a title=\"Master Docker Compose: The Ultimate Guide for Developers\" class=\"read-more\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/\" aria-label=\"Read more  on Master Docker Compose: The Ultimate Guide for Developers\">Read more<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-4726","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.0 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Master Docker Compose: The Ultimate Guide for Developers - ITSupportWale<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Master Docker Compose: The Ultimate Guide for Developers - ITSupportWale\" \/>\n<meta property=\"og:description\" content=\"text [2024-05-22T03:14:02.881Z] ERROR: Container &#8220;api-gateway&#8221; exited with code 137 (OOMKilled) [2024-05-22T03:14:05.112Z] CRITICAL: Service &#8220;auth-provider&#8221; failed to bind to 0.0.0.0:8080. Address already in use. [2024-05-22T03:14:05.115Z] FATAL: Dependency check failed. &#8220;postgres-db&#8221; not reachable at 172.17.0.2:5432. [2024-05-22T03:14:05.118Z] STACK_TRACE: deploy.sh: line 44: docker run -d &#8211;name api-gateway &#8230; [2024-05-22T03:14:05.120Z] SYSTEM_STATE: Load Average 45.12, 38.01, 22.10. Disk I\/O 98% saturated. ... Read more\" \/>\n<meta property=\"og:url\" content=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/\" \/>\n<meta property=\"og:site_name\" content=\"ITSupportWale\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Itsupportwale-298547177495978\" \/>\n<meta property=\"article:published_time\" content=\"2026-02-28T15:31:57+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2021\/05\/android-chrome-512x512-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"512\" \/>\n\t<meta property=\"og:image:height\" content=\"512\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Techie\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Techie\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/\"},\"author\":{\"name\":\"Techie\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d\"},\"headline\":\"Master Docker Compose: The Ultimate Guide for Developers\",\"datePublished\":\"2026-02-28T15:31:57+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/\"},\"wordCount\":1569,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/\",\"url\":\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/\",\"name\":\"Master Docker Compose: The Ultimate Guide for Developers - ITSupportWale\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#website\"},\"datePublished\":\"2026-02-28T15:31:57+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/itsupportwale.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Master Docker Compose: The Ultimate Guide for Developers\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#website\",\"url\":\"https:\/\/itsupportwale.com\/blog\/\",\"name\":\"ITSupportWale\",\"description\":\"Tips, Tricks, Fixed-Errors, Tutorials &amp; Guides\",\"publisher\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/itsupportwale.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\",\"name\":\"itsupportwale\",\"url\":\"https:\/\/itsupportwale.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png\",\"contentUrl\":\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png\",\"width\":1119,\"height\":144,\"caption\":\"itsupportwale\"},\"image\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/Itsupportwale-298547177495978\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d\",\"name\":\"Techie\",\"sameAs\":[\"https:\/\/itsupportwale.com\",\"iswblogadmin\"],\"url\":\"https:\/\/itsupportwale.com\/blog\/author\/iswblogadmin\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Master Docker Compose: The Ultimate Guide for Developers - ITSupportWale","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/","og_locale":"en_US","og_type":"article","og_title":"Master Docker Compose: The Ultimate Guide for Developers - ITSupportWale","og_description":"text [2024-05-22T03:14:02.881Z] ERROR: Container &#8220;api-gateway&#8221; exited with code 137 (OOMKilled) [2024-05-22T03:14:05.112Z] CRITICAL: Service &#8220;auth-provider&#8221; failed to bind to 0.0.0.0:8080. Address already in use. [2024-05-22T03:14:05.115Z] FATAL: Dependency check failed. &#8220;postgres-db&#8221; not reachable at 172.17.0.2:5432. [2024-05-22T03:14:05.118Z] STACK_TRACE: deploy.sh: line 44: docker run -d &#8211;name api-gateway &#8230; [2024-05-22T03:14:05.120Z] SYSTEM_STATE: Load Average 45.12, 38.01, 22.10. Disk I\/O 98% saturated. ... Read more","og_url":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/","og_site_name":"ITSupportWale","article_publisher":"https:\/\/www.facebook.com\/Itsupportwale-298547177495978","article_published_time":"2026-02-28T15:31:57+00:00","og_image":[{"width":512,"height":512,"url":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2021\/05\/android-chrome-512x512-1.png","type":"image\/png"}],"author":"Techie","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Techie","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/#article","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/"},"author":{"name":"Techie","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d"},"headline":"Master Docker Compose: The Ultimate Guide for Developers","datePublished":"2026-02-28T15:31:57+00:00","mainEntityOfPage":{"@id":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/"},"wordCount":1569,"commentCount":0,"publisher":{"@id":"https:\/\/itsupportwale.com\/blog\/#organization"},"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/","url":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/","name":"Master Docker Compose: The Ultimate Guide for Developers - ITSupportWale","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/#website"},"datePublished":"2026-02-28T15:31:57+00:00","breadcrumb":{"@id":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-the-ultimate-guide-for-developers\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/itsupportwale.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Master Docker Compose: The Ultimate Guide for Developers"}]},{"@type":"WebSite","@id":"https:\/\/itsupportwale.com\/blog\/#website","url":"https:\/\/itsupportwale.com\/blog\/","name":"ITSupportWale","description":"Tips, Tricks, Fixed-Errors, Tutorials &amp; Guides","publisher":{"@id":"https:\/\/itsupportwale.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/itsupportwale.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/itsupportwale.com\/blog\/#organization","name":"itsupportwale","url":"https:\/\/itsupportwale.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png","contentUrl":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png","width":1119,"height":144,"caption":"itsupportwale"},"image":{"@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Itsupportwale-298547177495978"]},{"@type":"Person","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d","name":"Techie","sameAs":["https:\/\/itsupportwale.com","iswblogadmin"],"url":"https:\/\/itsupportwale.com\/blog\/author\/iswblogadmin\/"}]}},"_links":{"self":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/4726","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/comments?post=4726"}],"version-history":[{"count":0,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/4726\/revisions"}],"wp:attachment":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/media?parent=4726"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/categories?post=4726"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/tags?post=4726"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}