{"id":4729,"date":"2026-03-07T21:02:30","date_gmt":"2026-03-07T15:32:30","guid":{"rendered":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/"},"modified":"2026-03-07T21:02:30","modified_gmt":"2026-03-07T15:32:30","slug":"master-docker-compose-simplify-multi-container-workflows","status":"publish","type":"post","link":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/","title":{"rendered":"Master Docker Compose: Simplify Multi-Container Workflows"},"content":{"rendered":"<p>text<br \/>\n[2024-05-24T03:14:22.891Z] ERROR: worker-node-04 kernel: [192834.12] Out of memory: Killed process 28491 (python3) total-vm:4.2GB, anon-rss:3.8GB, file-rss:0B, shmem-rss:0B, uid:1000 pgtables:8420kB oom_score_adj:0<br \/>\n[2024-05-24T03:14:23.002Z] CRITICAL: container_id=f3a2b1c0d9e8 exited with code 137.<br \/>\n[2024-05-24T03:14:23.450Z] DEBUG: Attempting manual restart of service &#8216;api-gateway&#8217;&#8230;<br \/>\n[2024-05-24T03:14:23.501Z] ERROR: docker: Error response from daemon: driver failed programming external connectivity on endpoint api-gateway (hash): Bind for 0.0.0.0:8080 failed: port is already allocated.<br \/>\n[2024-05-24T03:14:24.110Z] FATAL: Network collision detected on bridge br-f92a11c82. Context propagation failed. Race condition in manual container deployment.<\/p>\n<pre class=\"codehilite\"><code>It is 04:30 AM on a Sunday. I have been staring at a flickering terminal for forty-eight hours because one of you &quot;cowboy&quot; developers decided that writing a declarative configuration was too much work for a &quot;quick fix&quot; on Friday afternoon. You thought running a raw `docker run` command directly on the staging host was a shortcut. Instead, you created a cascading failure that triggered an OOMKill, wiped out the ephemeral storage of three sidecars, and left me untangling a web of orphaned network namespaces that looked like a bowl of digital spaghetti.\n\nI am done. My patience has evaporated along with my weekend. This isn't a suggestion. This isn't a &quot;best practice&quot; shared over a latte. This is a mandatory shift in how we operate. As of Docker Engine 26.1.4, we are officially banning the use of raw `docker` commands for anything other than `ps` or `logs`. If I catch another person manually mapping ports or injecting environment variables via the CLI, I will revoke your SSH access faster than the kernel can reclaim leaked memory.\n\nWe are moving to `docker compose` v2.27.0. Exclusively. Here is why your manual workflow is a liability and why this manifesto is the only thing standing between you and a formal performance review.\n\n## The Myth of the &quot;One-Liner&quot; and the Death of Reproducibility\n\nYou think you\u2019re being fast. You type `docker run -d -p 8080:80 --name my-app my-image:latest` and walk away. But you\u2019ve just committed a crime against reproducibility. Where is the record of that command? It\u2019s buried in your shell history, which will be purged. It\u2019s not in version control. It\u2019s not peer-reviewed.\n\nWhen that container crashed at 3 AM, I had no idea what environment variables you passed to it. I didn't know if you set memory limits (you didn't, which is why the OOMKiller nuked the entire node). I didn't know which network you attached it to. Using `docker compose` forces you to define the state of the world in a YAML file that lives in Git. It turns your &quot;tribal knowledge&quot; into a documented, executable reality.\n\nWithout a `docker-compose.yml`, we are guessing. And in SRE, guessing is just a slow way of failing.\n\n## Why Your Manual Port Mapping is a Security Nightmare\n\nLast night, the &quot;Friday Night Massacre&quot; happened because two of you tried to run different versions of the same microservice on the same host using manual `docker run` commands. One of you mapped `8080:80`. The other tried `8080:80` and failed, so you &quot;cleverly&quot; changed it to `8081:80`. \n\nYou didn't realize that the application logic was hardcoded to look for a specific port on the bridge gateway. By bypassing `docker compose`, you bypassed the internal DNS resolution that `docker compose` provides by default. In a `docker compose` environment, services talk to each other by service name over an isolated virtual bridge. You don't need to expose ports to the host interface at all unless it's the actual ingress point.\n\nWhen you use raw commands, you end up exposing internal databases to `0.0.0.0` just so your worker container can find them. You\u2019re opening the front door to the entire internet because you\u2019re too lazy to define a network namespace.\n\n## The YAML Indentation Hell We Deserve (And Why It\u2019s Better)\n\nLook at this mess. This is what I found on the staging server\u2014a shell script trying to mimic what `docker compose` does natively. It\u2019s fragile, it\u2019s ugly, and it failed the moment a container didn't exit cleanly.\n\n### Code Block 1: The &quot;Cowboy&quot; Failure (What NOT to do)\n```bash\n# This is garbage. Do not do this.\ndocker run -d --name redis-prod redis:7.0\ndocker run -d --name python-worker \\\n  -e REDIS_HOST=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' redis-prod) \\\n  --memory=&quot;2g&quot; \\\n  my-python-app:v1.2\n# Result: If redis-prod restarts and gets a new IP, python-worker breaks. \n# There is no healthcheck. There is no retry logic.\n<\/code><\/pre>\n<p>The above is why I was paged. The Redis container restarted, the IP changed, and the Python worker spent four hours screaming into the void because it had a hardcoded IP address injected at runtime. <\/p>\n<p>Now, look at how we are doing it moving forward. We are using <code>docker compose<\/code> to handle the service discovery. The internal DNS server built into the Docker daemon handles the mapping of the service name <code>redis<\/code> to whatever ephemeral IP the container currently holds.<\/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-69d8b4dc76b1e\" 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-69d8b4dc76b1e\"  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-simplify-multi-container-workflows\/#Environment_Variable_Hell_Why_Your_Shell_History_is_a_Security_Breach\" >Environment Variable Hell: Why Your Shell History is a Security Breach<\/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-simplify-multi-container-workflows\/#The_Race_Condition_Symphony_depends_on_or_Die\" >The Race Condition Symphony: depends_on or Die<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/#Code_Block_2_The_%E2%80%9CIntermediate%E2%80%9D_Transition\" >Code Block 2: The &#8220;Intermediate&#8221; Transition<\/a><\/li><\/ul><\/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-simplify-multi-container-workflows\/#Project_Isolation_and_the_Networking_Abyss\" >Project Isolation and the Networking Abyss<\/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-simplify-multi-container-workflows\/#Volume_Persistence_and_the_Ephemeral_Storage_Lie\" >Volume Persistence and the Ephemeral Storage Lie<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/#Mandatory_Hardening_The_Final_Standard\" >Mandatory Hardening: The Final Standard<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/#Code_Block_3_The_Production-Hardened_Standard\" >Code Block 3: The Production-Hardened Standard<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/#The_%E2%80%9CI_Dont_Care_About_Your_Local_Machine%E2%80%9D_Clause\" >The &#8220;I Don&#8217;t Care About Your Local Machine&#8221; Clause<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/#Terminal_Verification_The_Only_Way_to_Work\" >Terminal Verification: The Only Way to Work<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/#The_Cost_of_Your_Inefficiency\" >The Cost of Your Inefficiency<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/#Mandatory_Action_Items\" >Mandatory Action Items<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/#Related_Articles\" >Related Articles<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"Environment_Variable_Hell_Why_Your_Shell_History_is_a_Security_Breach\"><\/span>Environment Variable Hell: Why Your Shell History is a Security Breach<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>I found secrets in the <code>~\/.bash_history<\/code> of the service account. Why? Because someone ran <code>docker run -e DB_PASSWORD=SuperSecretPassword123<\/code>. This is amateur hour. <\/p>\n<p>With <code>docker compose<\/code>, we integrate with <code>.env<\/code> files and secret management. We don&#8217;t leak credentials into the process tree where any <code>ps aux<\/code> command can scrape them. We use the <code>env_file<\/code> attribute to keep our configuration separate from our execution. This allows us to swap environments (dev, staging, prod) without changing a single line of the core logic.<\/p>\n<p>Furthermore, <code>docker compose<\/code> allows for interpolation. We can ensure that the image tags are consistent across the entire stack. No more &#8220;oh, I forgot to update the sidecar image version&#8221; excuses.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_Race_Condition_Symphony_depends_on_or_Die\"><\/span>The Race Condition Symphony: <code>depends_on<\/code> or Die<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>The most infuriating part of the 48-hour shift was the circular dependency between the Redis cache and the Python worker. The worker would start, try to connect to Redis, find that Redis was still &#8220;booting,&#8221; and then crash-loop. Because the worker had no internal retry logic (another thing we need to discuss), it eventually hit the kernel&#8217;s rate limit for process spawning and triggered a system-wide hang.<\/p>\n<p>Raw <code>docker<\/code> has no concept of &#8220;readiness.&#8221; It only knows &#8220;running.&#8221; A container can be &#8220;running&#8221; while the application inside is still JIT-compiling or waiting for a socket.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Code_Block_2_The_%E2%80%9CIntermediate%E2%80%9D_Transition\"><\/span>Code Block 2: The &#8220;Intermediate&#8221; Transition<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<pre class=\"codehilite\"><code class=\"language-yaml\"># docker-compose.yml - Docker Compose version v2.27.0\nservices:\n  redis:\n    image: redis:7.2-alpine\n    networks:\n      - backend_net\n    healthcheck:\n      test: [&quot;CMD&quot;, &quot;redis-cli&quot;, &quot;ping&quot;]\n      interval: 5s\n      timeout: 3s\n      retries: 5\n\n  worker:\n    image: our-registry.io\/python-worker:2.4.1\n    environment:\n      - REDIS_HOST=redis\n    depends_on:\n      redis:\n        condition: service_healthy\n    networks:\n      - backend_net\n\nnetworks:\n  backend_net:\n    driver: bridge\n<\/code><\/pre>\n<p>In this configuration, <code>docker compose<\/code> understands the state of the application, not just the container. The <code>worker<\/code> will not even attempt to start until the <code>redis<\/code> healthcheck returns a successful exit code. This eliminates the race condition that cost me my Saturday night.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Project_Isolation_and_the_Networking_Abyss\"><\/span>Project Isolation and the Networking Abyss<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>When you run <code>docker compose up<\/code>, the tool automatically creates a network prefixed with your project name. This is critical. It means that &#8220;Project A&#8221; cannot accidentally talk to &#8220;Project B&#8221; just because they happen to be on the same host. <\/p>\n<p>When you use raw <code>docker run<\/code>, you are likely dumping everything into the default <code>bridge<\/code> network. This is a flat network where every container can see every other container. It\u2019s a lateral movement dream for an attacker and a debugging nightmare for an SRE. <\/p>\n<p>I spent three hours yesterday trying to figure out why a staging container was receiving traffic meant for a legacy dev instance. It turns out they were both listening on the same internal bridge and the load balancer was round-robining between them because they both responded to the same alias. <code>docker compose<\/code> prevents this by scoping everything to the project.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Volume_Persistence_and_the_Ephemeral_Storage_Lie\"><\/span>Volume Persistence and the Ephemeral Storage Lie<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>One of you &#8220;cowboys&#8221; lost 40GB of processed logs because you used a bind mount incorrectly in a manual command. You pointed the container to a directory that didn&#8217;t exist on the host, and Docker, in its infinite wisdom (Engine 26.1.4), created it as a root-owned directory. When the application tried to write to it, it failed, fell back to the container&#8217;s ephemeral layer, and when the container was deleted, so was the data.<\/p>\n<p><code>docker compose<\/code> makes volume management explicit. We use named volumes. We define them at the top level. We ensure they are managed by the Docker volume driver, not by some random path on your local disk that won&#8217;t exist in the staging environment.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Mandatory_Hardening_The_Final_Standard\"><\/span>Mandatory Hardening: The Final Standard<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>This is the final form. Every service we deploy from now on must look like this. If it doesn&#8217;t have resource limits, if it doesn&#8217;t have healthchecks, and if it isn&#8217;t using <code>docker compose<\/code>, it will be deleted by a cron job I am writing tonight.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Code_Block_3_The_Production-Hardened_Standard\"><\/span>Code Block 3: The Production-Hardened Standard<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<pre class=\"codehilite\"><code class=\"language-yaml\">version: &quot;3.9&quot; # Compatible with docker compose v2.27.0\n\nservices:\n  api:\n    image: our-registry.io\/api-service:v4.12.0\n    deploy:\n      resources:\n        limits:\n          cpus: '0.50'\n          memory: 512M\n        reservations:\n          cpus: '0.25'\n          memory: 256M\n      restart_policy:\n        condition: on-failure\n        delay: 5s\n        max_attempts: 3\n        window: 120s\n    logging:\n      driver: &quot;json-file&quot;\n      options:\n        max-size: &quot;10m&quot;\n        max-file: &quot;3&quot;\n    networks:\n      - frontend\n      - backend\n    healthcheck:\n      test: [&quot;CMD&quot;, &quot;curl&quot;, &quot;-f&quot;, &quot;http:\/\/localhost:3000\/health&quot;]\n      interval: 30s\n      timeout: 10s\n      retries: 3\n      start_period: 40s\n\n  db:\n    image: postgres:16-alpine\n    volumes:\n      - db_data:\/var\/lib\/postgresql\/data\n    environment:\n      - POSTGRES_DB_FILE=\/run\/secrets\/db_name\n      - POSTGRES_USER_FILE=\/run\/secrets\/db_user\n      - POSTGRES_PASSWORD_FILE=\/run\/secrets\/db_password\n    secrets:\n      - db_name\n      - db_user\n      - db_password\n    networks:\n      - backend\n    deploy:\n      resources:\n        limits:\n          memory: 1G\n\nnetworks:\n  frontend:\n    internal: false\n  backend:\n    internal: true\n\nvolumes:\n  db_data:\n    driver: local\n\nsecrets:\n  db_name:\n    file: .\/secrets\/db_name.txt\n  db_user:\n    file: .\/secrets\/db_user.txt\n  db_password:\n    file: .\/secrets\/db_password.txt\n<\/code><\/pre>\n<p>Notice the <code>internal: true<\/code> flag on the backend network. This ensures that the database has <em>zero<\/em> path to the outside world. It can only talk to the API. This is the level of isolation I expect. Notice the <code>resources<\/code> block. This prevents the OOMKill 137 errors that have been haunting my pagers. If your app leaks memory, it dies alone, without taking the whole node with it.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_%E2%80%9CI_Dont_Care_About_Your_Local_Machine%E2%80%9D_Clause\"><\/span>The &#8220;I Don&#8217;t Care About Your Local Machine&#8221; Clause<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>I am tired of hearing &#8220;it worked on my machine.&#8221; Your machine is a MacBook with 32GB of RAM and a different architecture. Staging is a hardened Linux environment with strict cgroup constraints. <\/p>\n<p>By using <code>docker compose<\/code>, we can use multiple override files. You can have your <code>docker-compose.override.yml<\/code> for your local development\u2014keep your debug ports, keep your hot-reloading bind mounts. But the base <code>docker-compose.yml<\/code> must be the source of truth for how the application behaves in the wild.<\/p>\n<p>If you cannot run <code>docker compose up<\/code> and have the entire stack initialize correctly, your code is broken. Period. No more manual setup steps. No more &#8220;oh, you have to run this script first to seed the DB.&#8221; Use an <code>init<\/code> container or an <code>entrypoint<\/code> script defined in the YAML.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Terminal_Verification_The_Only_Way_to_Work\"><\/span>Terminal Verification: The Only Way to Work<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>From this point forward, before you even think about pushing a change, you will verify your local state using the following commands. I will be checking the logs.<\/p>\n<pre class=\"codehilite\"><code class=\"language-bash\"># Verify all services are healthy, not just &quot;running&quot;\n$ docker compose ps\n\nNAME                IMAGE               COMMAND                  SERVICE             STATUS              PORTS\nstack-api-1         api-service:v4.12   &quot;docker-php-entrypoi\u2026&quot;   api                 running (healthy)   0.0.0.0:80-&gt;3000\/tcp\nstack-db-1          postgres:16         &quot;docker-entrypoint.s\u2026&quot;   db                  running (healthy)   5432\/tcp\n\n# Check for resource-related warnings\n$ docker compose logs --tail=100 | grep -iE &quot;error|critical|oom|fail&quot;\n<\/code><\/pre>\n<p>If <code>docker compose ps<\/code> shows anything other than <code>(healthy)<\/code>, you do not merge. If you see a <code>137<\/code> exit code in the logs, you do not ask me for help until you have added memory limits and profiled your heap usage.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_Cost_of_Your_Inefficiency\"><\/span>The Cost of Your Inefficiency<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Do you know what happens when a node goes down because of an OOMKill? It\u2019s not just one container. The kernel gets desperate. It starts killing processes based on an <code>oom_score<\/code>. Often, it kills the SSH daemon or the monitoring agent before it hits your bloated Python script. This leaves the node in a &#8220;zombie&#8221; state\u2014it&#8217;s alive, but unreachable. I have to manually power-cycle the instance via the cloud console, wait for the EBS volumes to detach (which always takes forever), and then rebuild the local cache.<\/p>\n<p>This process takes 45 minutes. 45 minutes of downtime because you couldn&#8217;t be bothered to write twenty lines of YAML. <\/p>\n<p>We are SREs. Our job is to manage entropy. Your job, as developers, is to stop creating it. <code>docker compose<\/code> is the tool that bridges that gap. It provides the context propagation we need to understand how services relate to one another. It ensures that ephemeral storage is handled correctly. It prevents the race conditions that turn a simple deployment into a weekend-long nightmare.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Mandatory_Action_Items\"><\/span>Mandatory Action Items<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<ol>\n<li><strong>Audit:<\/strong> Every repository must have a <code>docker-compose.yml<\/code> by EOD Tuesday.<\/li>\n<li><strong>Cleanup:<\/strong> Run <code>docker system prune -f<\/code> on your dev machines to clear out the hundreds of orphaned volumes and networks your manual commands have left behind.<\/li>\n<li><strong>Standardize:<\/strong> Use the hardened template provided in Code Block 3. No exceptions.<\/li>\n<li><strong>Education:<\/strong> If you don&#8217;t understand how <code>depends_on<\/code> works with healthchecks, read the documentation for <code>docker compose<\/code> v2.27.0. Do not ask me. I am going to sleep.<\/li>\n<\/ol>\n<p>If I see another <code>docker run<\/code> command in our CI\/CD pipelines or in the shell history of any staging server, I will personally ensure that your next &#8220;quick fix&#8221; is reviewed with a microscope and a blowtorch. <\/p>\n<p>We are professionals. Start acting like it. Use <code>docker compose<\/code>. Stop the bleeding.<\/p>\n<p>I\u2019m going home. Don&#8217;t page me unless the data center is literally on fire. And even then, check the healthchecks first.<\/p>\n<p><strong>SRE Lead Out.<\/strong><\/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\/how-to-increase-migration-speed-in-office-365\/\">How To Increase Migration Speed In Office 365<\/a><\/li>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/install-and-secure-phpmyadmin-with-nginx-on-ubuntu-18-04\/\">Install And Secure Phpmyadmin With Nginx On Ubuntu 18 04<\/a><\/li>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/mastering-amazon-aws-a-complete-guide-for-beginners\/\">Mastering Amazon Aws A Complete Guide For Beginners<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>text [2024-05-24T03:14:22.891Z] ERROR: worker-node-04 kernel: [192834.12] Out of memory: Killed process 28491 (python3) total-vm:4.2GB, anon-rss:3.8GB, file-rss:0B, shmem-rss:0B, uid:1000 pgtables:8420kB oom_score_adj:0 [2024-05-24T03:14:23.002Z] CRITICAL: container_id=f3a2b1c0d9e8 exited with code 137. [2024-05-24T03:14:23.450Z] DEBUG: Attempting manual restart of service &#8216;api-gateway&#8217;&#8230; [2024-05-24T03:14:23.501Z] ERROR: docker: Error response from daemon: driver failed programming external connectivity on endpoint api-gateway (hash): Bind for 0.0.0.0:8080 failed: &#8230; <a title=\"Master Docker Compose: Simplify Multi-Container Workflows\" class=\"read-more\" href=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/\" aria-label=\"Read more  on Master Docker Compose: Simplify Multi-Container Workflows\">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-4729","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: Simplify Multi-Container Workflows - 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-simplify-multi-container-workflows\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Master Docker Compose: Simplify Multi-Container Workflows - ITSupportWale\" \/>\n<meta property=\"og:description\" content=\"text [2024-05-24T03:14:22.891Z] ERROR: worker-node-04 kernel: [192834.12] Out of memory: Killed process 28491 (python3) total-vm:4.2GB, anon-rss:3.8GB, file-rss:0B, shmem-rss:0B, uid:1000 pgtables:8420kB oom_score_adj:0 [2024-05-24T03:14:23.002Z] CRITICAL: container_id=f3a2b1c0d9e8 exited with code 137. [2024-05-24T03:14:23.450Z] DEBUG: Attempting manual restart of service &#8216;api-gateway&#8217;&#8230; [2024-05-24T03:14:23.501Z] ERROR: docker: Error response from daemon: driver failed programming external connectivity on endpoint api-gateway (hash): Bind for 0.0.0.0:8080 failed: ... Read more\" \/>\n<meta property=\"og:url\" content=\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/\" \/>\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-03-07T15:32:30+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-simplify-multi-container-workflows\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/\"},\"author\":{\"name\":\"Techie\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d\"},\"headline\":\"Master Docker Compose: Simplify Multi-Container Workflows\",\"datePublished\":\"2026-03-07T15:32:30+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/\"},\"wordCount\":1378,\"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-simplify-multi-container-workflows\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/\",\"url\":\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/\",\"name\":\"Master Docker Compose: Simplify Multi-Container Workflows - ITSupportWale\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#website\"},\"datePublished\":\"2026-03-07T15:32:30+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/itsupportwale.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Master Docker Compose: Simplify Multi-Container Workflows\"}]},{\"@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: Simplify Multi-Container Workflows - 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-simplify-multi-container-workflows\/","og_locale":"en_US","og_type":"article","og_title":"Master Docker Compose: Simplify Multi-Container Workflows - ITSupportWale","og_description":"text [2024-05-24T03:14:22.891Z] ERROR: worker-node-04 kernel: [192834.12] Out of memory: Killed process 28491 (python3) total-vm:4.2GB, anon-rss:3.8GB, file-rss:0B, shmem-rss:0B, uid:1000 pgtables:8420kB oom_score_adj:0 [2024-05-24T03:14:23.002Z] CRITICAL: container_id=f3a2b1c0d9e8 exited with code 137. [2024-05-24T03:14:23.450Z] DEBUG: Attempting manual restart of service &#8216;api-gateway&#8217;&#8230; [2024-05-24T03:14:23.501Z] ERROR: docker: Error response from daemon: driver failed programming external connectivity on endpoint api-gateway (hash): Bind for 0.0.0.0:8080 failed: ... Read more","og_url":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/","og_site_name":"ITSupportWale","article_publisher":"https:\/\/www.facebook.com\/Itsupportwale-298547177495978","article_published_time":"2026-03-07T15:32:30+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-simplify-multi-container-workflows\/#article","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/"},"author":{"name":"Techie","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d"},"headline":"Master Docker Compose: Simplify Multi-Container Workflows","datePublished":"2026-03-07T15:32:30+00:00","mainEntityOfPage":{"@id":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/"},"wordCount":1378,"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-simplify-multi-container-workflows\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/","url":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/","name":"Master Docker Compose: Simplify Multi-Container Workflows - ITSupportWale","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/#website"},"datePublished":"2026-03-07T15:32:30+00:00","breadcrumb":{"@id":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/itsupportwale.com\/blog\/master-docker-compose-simplify-multi-container-workflows\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/itsupportwale.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Master Docker Compose: Simplify Multi-Container Workflows"}]},{"@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\/4729","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=4729"}],"version-history":[{"count":0,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/4729\/revisions"}],"wp:attachment":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/media?parent=4729"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/categories?post=4729"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/tags?post=4729"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}