Troubleshoot a Container Network
Diagnose and repair a Docker Compose service discovery failure on your Mac.
Introduction
30 Second Summary
A status page can look healthy while another system quietly fails to reach it. Both observations can be true at the same time.
In this project, you will build a local incident lab with a status page and a monitor using Docker Desktop and Docker Compose. The lab takes you from isolated containers to a verified recovery supported by logs and network evidence.
What You'll Build
Your demo starts with a working status page beside status=unreachable monitor logs before ending with those logs switching to status=healthy after your repair.
By the end of this project, you'll have:
- A visible status page at http://localhost:8080 that proves the site responds from your Mac.
- A repeatable network failure where the page still works while the monitor records status=unreachable.
- An evidence-led troubleshooting workflow that uses service status, logs, and network inspection to find the isolation before confirming recovery.
- Secret Mission: Remove the host port while preserving healthy container-to-container monitoring.
Are there any prerequisites?
Use a personally controlled Mac with administrator access, at least 4 GB of RAM, and a Docker-supported macOS release. Keep classified, sensitive, employer, and government data out of this local lab.
Before We Start
Before the hands-on work begins, lock in the purpose of this incident lab. Connecting service recovery to the infrastructure roles you are exploring gives every troubleshooting choice a clear reason.
Set Up Docker Desktop
The site service and monitor need a consistent Linux container runtime. That consistency makes every later networking symptom trustworthy.
In this step, you will install Docker Desktop 4.94.0 on your Mac. You will use Terminal to prove that Docker Engine is ready through the Docker Compose command line.
In this step, get ready to:
- Check your Mac against Docker's macOS and memory requirements.
- Install Docker Desktop 4.94.0 under the applicable license terms.
- Verify Docker Engine and Docker Compose from Terminal.
Check your Mac's requirements
Docker Desktop supports the current macOS release plus the two previous major releases. Your Mac also needs at least 4 GB of memory.
- Click the Apple menu in the top-left corner of your screen.
- Click System Settings.
- Select General in the sidebar.
- Select About.
- Compare the version beside macOS with Docker's Mac installation requirements.
- Confirm that Memory shows at least 4 GB.
- Record the value shown beside Chip or Processor here: Apple silicon or Intel.
Once these checks match, your Mac is ready for the installer built for its processor type.
Install and start Docker Desktop
Docker Desktop may already be installed on your Mac. Choose the tab that matches its current state.
✔️ I have the required release
Docker Desktop 4.94.0 is already installed, so the application files are ready.
- Continue to the first-launch instructions below.
ⓧ I have an older release
Installing the pinned release keeps your environment aligned with the rest of this lab.
- Open the Docker Desktop release notes.
- Find the entry for 4.94.0.
- Download the Mac installer matching Apple silicon or Intel.
- Double-click Docker.dmg when the download finishes.
- Drag the Docker icon into the Applications folder.
- Confirm the replacement if macOS prompts you about the existing application.
ⓧ Docker Desktop is not installed
You need the processor-specific installer for Docker Desktop 4.94.0.
- Open the Docker Desktop release notes.
- Find the entry for 4.94.0.
- Download the Mac installer matching Apple silicon or Intel.
- Double-click Docker.dmg when the download finishes.
- Drag the Docker icon into the Applications folder.
Check the license before accepting
Licensing can feel like a billing step. Docker Desktop is free for personal learning on a personally controlled Mac.
Government entities and larger enterprises require a paid subscription. Keep this lab away from classified, sensitive, employer, or government data.
The first launch can take a few minutes while Docker Desktop starts its Linux engine. A quiet wait during this startup does not mean the application is stuck.
- Press Cmd+Space to open Spotlight.
- Type Docker and press Enter.
- Read the Docker Subscription Service Agreement.
- Select Accept if the terms apply to your use.
- Wait until Docker Desktop reports that the engine is ready.
That's the setup hurdle cleared. Docker Engine is now ready to receive commands from Terminal.
Docker Engine not becoming ready?
- Confirm that the installer matched the processor type shown in your Mac's About settings.
- Confirm that your macOS release and memory meet the requirements you checked earlier.
- Ask for help with an engine that remains unavailable: Help me diagnose why Docker Desktop is not becoming ready on my Mac.
Verify Docker in Terminal
The running desktop application provides the engine. The command line check proves that your Docker client can reach it.
- Press Cmd+Space to open Spotlight.
- Type Terminal and press Enter.
Before you run this check, do you expect the Docker client to reach the running engine?
- Verify the Docker client and engine by running this command:
docker version
What does this command prove?
- The Client section shows that the Docker command line is installed.
- The Server section shows that the command line reached Docker Engine.
You should see both Client and Server sections in the output.
Missing the Server section?
- Return to Docker Desktop from the macOS menu bar.
- Wait until the application reports that the engine is ready.
- Run docker version again in Terminal.
- Ask for help if the Server section still does not appear: Help me restore the Docker Server connection.
Before the final check, what kind of version information do you expect Docker Compose to print?
- Verify the bundled Docker Compose command by running:
docker compose version
What does this command prove?
Docker Desktop includes Docker Compose. This command confirms that its Compose plugin is available from Terminal.
You should see Docker Compose version information in the output.
Docker Compose command unavailable?
- Confirm that Docker Desktop is still running.
- Close Terminal if it was open during installation.
- Reopen Terminal through Spotlight.
- Run docker compose version again.
- Ask for help if Compose remains unavailable: Help me restore the Docker Compose command.
Your Docker client now reaches the engine. Docker Compose is also ready to define the two services in your incident lab.
Your container runtime is ready. Next, you'll launch the visible status page that becomes the known-good baseline for the incident.
Launch the Visible Status Page
Your Docker runtime is ready from the previous step. You can now build the first service in the incident lab.
A visible working service gives you a known-good baseline before the project introduces a failure. In this step, Docker Compose turns a local status page into a running container that you can reach from Safari.
In this step, get ready to:
- Create the lab folders and project files.
- Build a visible infrastructure status page.
- Launch the site service on a named network.
Create the project structure
The lab keeps its configuration and website content inside one folder on your Desktop. This gives every later troubleshooting command a predictable starting point.
- Return to Terminal from the previous step.
- Move to your Desktop by running this command:
cd ~/Desktop
What does this command do?
The cd command changes Terminal's current location. The ~/Desktop path points to the Desktop inside your macOS home folder.
- Create the lab folders and empty files by running these commands:
mkdir -p infrastructure-incident-lab/site
cd infrastructure-incident-lab
touch compose.yaml site/index.html
ls
What do these commands create?
- The infrastructure-incident-lab folder holds the complete project.
- The site folder holds the page served by nginx.
- The compose.yaml file describes the site service and its network.
- The final ls command lists the new files so you can confirm they exist.
Good progress. You should see compose.yaml and site in the output.
- Open both project files in TextEdit by running this command:
open -a TextEdit compose.yaml site/index.html
What does this command do?
The open command sends both files to TextEdit. You should see one empty document for compose.yaml and another for site/index.html.
Files missing from TextEdit?
- Switch back to Terminal.
- Confirm the prompt shows the infrastructure-incident-lab folder.
- Run the file creation commands again if either document is missing.
Help me find the project files in TextEdit.
Build the status page
The status page is your visible baseline. Its service details give you something concrete to preserve while the surrounding infrastructure changes.
- Switch to the TextEdit document for site/index.html.
- Paste this page structure into the empty document:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Infrastructure Status</title>
</head>
<body>
<main class="card">
<div class="status"><span class="dot" aria-hidden="true"></span>Online</div>
<h1>Infrastructure Status</h1>
<p>Site service is responding on port 80.</p>
<dl>
<dt>Service</dt>
<dd>site</dd>
<dt>Runtime</dt>
<dd>nginx container</dd>
<dt>Monitor target</dt>
<dd>http://site</dd>
</dl>
</main>
</body>
</html>
What does this HTML do?
- The main element groups the visible incident status inside one card.
- The definition list displays the service name, runtime, and monitor target as labelled evidence.
- The Online label gives the page an immediate visual health signal.
- Save site/index.html by pressing Cmd+S.
- Switch back to Terminal.
- Open the unstyled page in Safari by running this command:
open -a Safari site/index.html
What should open?
Safari opens the local HTML file directly from your project folder. This preview checks the page content before a container serves it.
You should see Infrastructure Status and Site service is responding on port 80. as plain browser text.
Page content missing?
- Confirm that you pasted the HTML into site/index.html.
- Save the TextEdit document again.
- Refresh the Safari page.
Help me debug the local status page.
The content is present. The next changes turn that plain baseline into a status card whose state is easy to read during the incident.
- Switch back to site/index.html in TextEdit.
- Find the line containing <title>Infrastructure Status</title>.
- Paste this style block directly below that line:
<style>
:root {
color-scheme: dark;
font-family: system-ui, sans-serif;
background: #0b1220;
color: #e5eefc;
}
</style>
What does this style control?
- The :root selector sets the page-wide colour scheme.
- The system font keeps the status text readable without downloading another font.
- Save site/index.html.
- Return to Safari.
- Refresh the page.
You should see a dark blue background with light text.
Background still white?
- Confirm that the style block sits inside the head element.
- Check that TextEdit saved the latest changes.
Help me fix the page-wide CSS.
- Find the closing </style> line in site/index.html.
- Add the following selector above that line with one blank line separating it from the previous selector:
body {
min-height: 100vh;
margin: 0;
display: grid;
place-items: center;
}
What does this selector control?
The body fills the browser height. Its grid layout places the page content in the centre.
- Save site/index.html.
- Return to Safari.
- Refresh the page.
You should see the status content move to the centre of the browser window.
Content still at the top?
- Confirm that the body selector appears before </style>.
- Check the spelling of place-items.
Help me centre the status page.
- Find the closing </style> line again.
- Add the following selector above that line with one blank line separating it from the previous selector:
.card {
width: min(34rem, calc(100% - 3rem));
padding: 2rem;
border: 1px solid #29415f;
border-radius: 1rem;
background: #111c2e;
box-shadow: 0 1rem 3rem rgb(0 0 0 / 35%);
}
What does this selector control?
The .card selector gives the incident details a responsive width, spacing, border, and raised surface.
- Save site/index.html.
- Return to Safari.
- Refresh the page.
You should see the content inside a rounded dark card with a subtle shadow.
Card styling missing?
- Confirm that the HTML still uses class="card" on the main element.
- Confirm that the CSS selector begins with a period.
Help me connect the card CSS to the HTML.
- Find the closing </style> line.
- Add the following selector above that line with one blank line separating it from the previous selector:
.status {
display: inline-flex;
align-items: center;
gap: 0.6rem;
color: #75e6a4;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
}
What does this selector control?
The .status selector turns the health label into a compact row. Its green uppercase text makes the current state stand out.
- Save site/index.html.
- Return to Safari.
- Refresh the page.
You should see the Online label become green and uppercase.
Online label unchanged?
- Confirm that the HTML uses class="status".
- Confirm that the selector remains inside the style element.
Help me style the status label.
- Find the closing </style> line.
- Add the following selector above that line with one blank line separating it from the previous selector:
.dot {
width: 0.75rem;
height: 0.75rem;
border-radius: 50%;
background: #45d483;
box-shadow: 0 0 1rem #45d483;
}
What does this selector control?
The .dot selector creates a circular health indicator. Its shadow gives the indicator a visible green glow.
- Save site/index.html.
- Return to Safari.
- Refresh the page.
You should see a glowing green dot beside the Online label.
Green dot missing?
- Confirm that the status row contains class="dot".
- Check that the .dot selector includes both width and height.
Help me restore the status dot.
- Find the closing </style> line.
- Add the following typography and metadata styles above that line with one blank line separating them from the previous selector:
h1 {
margin-bottom: 0.5rem;
}
p {
color: #b8c7dc;
}
dl {
display: grid;
grid-template-columns: auto 1fr;
gap: 0.6rem 1rem;
margin-top: 1.5rem;
}
dt {
color: #8da4c2;
}
dd {
margin: 0;
font-family: ui-monospace, monospace;
}
What do these styles control?
- The heading and paragraph rules separate the title from the softer response message.
- The dl rule arranges each label beside its value.
- The dt and dd rules give labels a muted colour and values a monospace font.
- Leave one blank line above the closing </style> line.
- Save site/index.html.
- Return to Safari.
- Refresh the page.
You should see the response message in muted text. You should also see the service details arranged in two aligned columns.
Service details not aligned?
- Confirm that every selector appears before the closing </style> line.
- Check that the definition list still uses matching dt and dd elements.
Help me fix the status page layout.
✔️ Awesome, I've got everything!
Your status page is complete. Double-check that you saved site/index.html before continuing.
ⓧ I'd like to double check the full code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Infrastructure Status</title>
<style>
:root {
color-scheme: dark;
font-family: system-ui, sans-serif;
background: #0b1220;
color: #e5eefc;
}
body {
min-height: 100vh;
margin: 0;
display: grid;
place-items: center;
}
.card {
width: min(34rem, calc(100% - 3rem));
padding: 2rem;
border: 1px solid #29415f;
border-radius: 1rem;
background: #111c2e;
box-shadow: 0 1rem 3rem rgb(0 0 0 / 35%);
}
.status {
display: inline-flex;
align-items: center;
gap: 0.6rem;
color: #75e6a4;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.dot {
width: 0.75rem;
height: 0.75rem;
border-radius: 50%;
background: #45d483;
box-shadow: 0 0 1rem #45d483;
}
h1 {
margin-bottom: 0.5rem;
}
p {
color: #b8c7dc;
}
dl {
display: grid;
grid-template-columns: auto 1fr;
gap: 0.6rem 1rem;
margin-top: 1.5rem;
}
dt {
color: #8da4c2;
}
dd {
margin: 0;
font-family: ui-monospace, monospace;
}
</style>
</head>
<body>
<main class="card">
<div class="status"><span class="dot" aria-hidden="true"></span>Online</div>
<h1>Infrastructure Status</h1>
<p>Site service is responding on port 80.</p>
<dl>
<dt>Service</dt>
<dd>site</dd>
<dt>Runtime</dt>
<dd>nginx container</dd>
<dt>Monitor target</dt>
<dd>http://site</dd>
</dl>
</main>
</body>
</html>
Configure and launch the site
The page currently opens as a local file. The Compose configuration turns it into a running nginx service with a host port, a read-only bind mount, and a named network.
- Switch to the TextEdit document for compose.yaml.
- Paste this configuration into the empty document:
services:
site:
image: nginx:1.30.5-alpine
ports:
- "127.0.0.1:8080:80"
volumes:
- ./site:/usr/share/nginx/html:ro
networks:
- web
networks:
web:
name: incident-web
What does this configuration do?
- The site service uses the pinned nginx:1.30.5-alpine image.
- The port mapping publishes container port 80 at 127.0.0.1:8080 on your Mac.
- The volume mapping serves the local site folder from nginx's static content path in read-only mode.
- The web key creates a network whose runtime name is incident-web.
- Save compose.yaml by pressing Cmd+S.
✔️ Awesome, I've got everything!
Your site service configuration is ready. Make sure compose.yaml is saved.
ⓧ I'd like to double check the full code
services:
site:
image: nginx:1.30.5-alpine
ports:
- "127.0.0.1:8080:80"
volumes:
- ./site:/usr/share/nginx/html:ro
networks:
- web
networks:
web:
name: incident-web
- Switch back to Terminal.
- Start the Compose project in detached mode by running this command:
docker compose up -d
What does this command do?
- Docker Compose reads compose.yaml from the current folder.
- It downloads the pinned nginx image when the image is absent from your Mac.
- It creates the incident-web network and starts the site container.
- The -d option leaves the container running in the background.
Before you check, do you expect the site container to be stopped or running?
- Check the site container's state by running this command:
docker compose ps
What does this command show?
The command lists the containers in this Compose project. It also shows their current status and published ports.
You should see the site container in a running state. Its published port should include 127.0.0.1:8080.
Before you open the host URL, what page do you expect nginx to serve from the read-only site folder?
- Open the container-served page in Safari by running this command:
open -a Safari http://localhost:8080
What does this check prove?
Safari requests the page through the host port published by Docker. A successful page load proves that the container is running and reachable from your Mac.
You should see Infrastructure Status and Site service is responding on port 80. inside the styled status card.
Status page unavailable?
- Confirm that Docker Desktop still shows its engine as ready.
- Confirm that Terminal remains inside the infrastructure-incident-lab folder.
- Compare compose.yaml with the full-code tab above.
- Confirm that site/index.html is saved.
Help me debug the unavailable status page.
That is your known-good baseline. The site container is running on incident-web while Safari can reach it through the loopback port.
Your visible service is working. Next, you'll add a monitor and observe a failure that the healthy Safari page cannot reveal.
Introduce the Broken Monitor
Your nginx status page is live in Safari. That confirms your Mac can reach the published site port.
A browser check does not prove that another container can reach the site service. You will add a repeating monitor to test that second path.
In this step, get ready to:
- Create a repeating shell monitor for the site service.
- Add a BusyBox monitor service to Docker Compose on a dedicated network.
- Observe the monitor result beside the working Safari page.
Create the repeating monitor script
The monitor performs the same HTTP check every five seconds. Each attempt produces a log entry that you can use as incident evidence.
- Create monitor.sh inside the current infrastructure-incident-lab folder by running this command in Terminal:
nano monitor.sh
What does this command do?
The nano editor opens a new plain-text file named monitor.sh. You can paste the monitor script directly into the Terminal window.
- Paste this script into monitor.sh.
#!/bin/sh
while true; do
if wget -q -O /dev/null http://site; then
echo "status=healthy target=http://site"
else
echo "status=unreachable target=http://site"
fi
sleep 5
done
What does this script do?
- The while true loop keeps the monitor running.
- The wget -q -O /dev/null http://site check sends a quiet HTTP request to the site service.
- The conditional writes either a healthy status or an unreachable status to the container logs.
- The sleep 5 line spaces the checks five seconds apart.
- Save monitor.sh by pressing Ctrl+O.
- Confirm the filename by pressing Enter.
- Exit nano by pressing Ctrl+X.
- Confirm that monitor.sh exists by running this command:
ls monitor.sh
What should I see?
Terminal should print monitor.sh. That output confirms the script was saved in the current folder.
Cannot find monitor.sh?
Make sure the nano filename was monitor.sh. Save it inside the infrastructure-incident-lab folder shown by your Terminal prompt.
Help me find the missing monitor script.
✔️ Awesome, I've got everything!
Your monitor script matches the incident lab configuration.
ⓧ I'd like to double check the full code
#!/bin/sh
while true; do
if wget -q -O /dev/null http://site; then
echo "status=healthy target=http://site"
else
echo "status=unreachable target=http://site"
fi
sleep 5
done
Isolate the monitor on a separate network
Compose needs a service definition before it can run the monitor script. The definition also controls which named network the monitor joins.
- Open the existing compose.yaml file in nano by running this command:
nano compose.yaml
What does this command do?
Nano opens the Compose file that already defines your site service. You will keep that service unchanged while adding the monitor configuration.
- Replace the contents of compose.yaml with this configuration.
services:
site:
image: nginx:1.30.5-alpine
ports:
- "127.0.0.1:8080:80"
volumes:
- ./site:/usr/share/nginx/html:ro
networks:
- web
monitor:
image: busybox:1.38.0
command: ["/bin/sh", "/monitor.sh"]
volumes:
- ./monitor.sh:/monitor.sh:ro
networks:
- monitoring
networks:
web:
name: incident-web
monitoring:
name: incident-monitor
What does this configuration do?
- The monitor service uses the BusyBox 1.38.0 image.
- The command value starts monitor.sh through the container shell.
- The read-only bind mount places your local script at /monitor.sh inside the container.
- The monitoring key creates a network named incident-monitor.
- Save compose.yaml by pressing Ctrl+O.
- Confirm the filename by pressing Enter.
- Exit nano by pressing Ctrl+X.
✔️ Awesome, I've got everything!
Your Compose file now defines both services and both named networks.
ⓧ I'd like to double check the full code
services:
site:
image: nginx:1.30.5-alpine
ports:
- "127.0.0.1:8080:80"
volumes:
- ./site:/usr/share/nginx/html:ro
networks:
- web
monitor:
image: busybox:1.38.0
command: ["/bin/sh", "/monitor.sh"]
volumes:
- ./monitor.sh:/monitor.sh:ro
networks:
- monitoring
networks:
web:
name: incident-web
monitoring:
name: incident-monitor
Run the monitor and observe the failure
The configuration is ready for a real reachability test. Compose can now create the monitor while keeping the existing site container running.
- Apply the updated Compose configuration by running this command:
docker compose up -d
What does this command do?
Docker Compose creates the new monitor service in detached mode. It keeps the existing site service available while the monitor runs in the background.
The BusyBox image may take a short moment to download on its first run. Terminal returns to the prompt after the services start.
Did the monitor fail to start?
Check that monitor.sh sits beside compose.yaml. Compare both files with the double-check tabs above.
Help me troubleshoot the monitor startup.
- Wait at least ten seconds for multiple monitoring cycles.
Before you inspect the logs, predict which result the monitor will report.
- Display the service logs by running this command:
docker compose logs
The designed failure is visible
You should see repeated status=unreachable target=http://site entries from the monitor. This shortfall is intentional.
The monitor process is running while its request fails. You now have evidence that process status and service reachability can disagree.
- Return to the Safari page from the previous step.
- Refresh http://localhost:8080.
Safari should still show Infrastructure Status with Site service is responding on port 80. The working page contrasts with the unreachable monitor logs.
Missing the unreachable logs?
Check that the monitor uses the monitoring network. Check that the script targets http://site.
Help me find the missing monitor logs.
You now have a reproducible incident where both services run but the monitor cannot reach its target. Next, you will inspect the container networks and trace the failure to its cause.
Diagnose the Network Isolation
You have captured a repeatable monitor failure while the status page remains available. Now you will trace the failure with runtime evidence.
Docker Desktop can keep both containers running even when no connection exists between them. This step separates process health from network reachability before you change compose.yaml.
In this step, get ready to:
- Confirm that both services remain running.
- Compare the container memberships of the two networks.
- Record why the separate networks cause the monitor failure.
Confirm both services are running
Container state is your first piece of evidence. A running state confirms that the service process exists.
- List the Compose project containers by running this command:
docker compose ps
What does this status check prove?
This status check lists the project containers with their current state and published ports. It tests process state without changing the lab.
You should see rows for site and monitor. Both should show that they are running.
That rules out a stopped process as the immediate cause of the failed check.
Is either service missing?
- Confirm that Terminal is still in the infrastructure-incident-lab folder.
- Return to the previous step if either service is absent.
Help me understand why a service is missing from the Compose project.
Compare the two network memberships
A bridge network tracks the containers attached to it. Comparing the two membership lists shows whether the services share a communication path.
Before you inspect the networks, which container do you expect each network to list?
- Inspect the site's network by running this command:
docker network inspect incident-web
What does this inspection show?
This inspection returns detailed network information as JSON. Its membership data shows which containers have endpoints on incident-web.
The incident-web result should list only the site container. The monitor container should be absent.
- Inspect the monitor's network by running this command:
docker network inspect incident-monitor
What does the second inspection show?
This inspection applies the same membership check to incident-monitor. Comparing it with the first result exposes the network boundary between the services.
The incident-monitor result should list only the monitor container. The site container should be absent.
Having trouble reading the inspections?
- Scroll through each result to find the container membership details.
- Compare the listed container names with site and monitor.
Help me interpret the network inspection evidence.
Record the network diagnosis
How does the evidence fit together?
The running states rule out a stopped container as the immediate cause.
The incident-web inspection contains site. The incident-monitor inspection contains monitor.
Docker service discovery requires both services to share a network. Their separate memberships explain why the monitor cannot reach http://site.
- Use the checkpoint below to capture the network evidence.
- Record your diagnosis from the evidence you collected.
You have isolated the fault to separate network memberships. Next, you will place the monitor on the shared network and validate its recovery.
Repair and Validate the Connection
Your network inspection proved that both containers are running on isolated networks. That evidence turns the earlier failure into a precise configuration repair.
The evidence points to network isolation. Docker Compose must place the monitor on the site's web network. Shared membership restores service-name discovery without a hard-coded IP address.
In this step, get ready to:
- Replace the isolated monitor network with the shared web network.
- Reapply the Compose configuration.
- Validate the complete incident recovery from both access paths.
Repair the shared network
Each service-level networks list controls the peers a service can reach. Matching entries let monitor resolve site on incident-web.
- Return to compose.yaml in the text editor from earlier.
- Replace its contents with the repaired configuration below:
services:
site:
image: nginx:1.30.5-alpine
ports:
- "127.0.0.1:8080:80"
volumes:
- ./site:/usr/share/nginx/html:ro
networks:
- web
monitor:
image: busybox:1.38.0
command: ["/bin/sh", "/monitor.sh"]
volumes:
- ./monitor.sh:/monitor.sh:ro
networks:
- web
networks:
web:
name: incident-web
What Changes in This Configuration?
- The monitor service now references web. Both containers therefore use incident-web.
- The unused top-level monitoring definition is removed.
- The site's port mapping remains unchanged. Its read-only content mount also remains unchanged.
- Save compose.yaml.
Before you reapply the configuration, do you expect Compose to recreate the site or the monitor? Hold that prediction.
- Reapply the repaired configuration by running this command:
docker compose up -d
What Does This Command Do?
This command re-reads compose.yaml. Compose recreates the monitor because its network configuration changed.
The site configuration stays the same. Its existing container can remain running.
You'll see Compose finish with both services running. The monitor is now attached to the repaired network.
Monitor Was Not Recreated?
- Confirm that compose.yaml is saved.
- Check that monitor contains - web under its networks list.
- Check that only the top-level web network remains.
Help me diagnose why Docker Compose did not recreate the monitor.
Your repaired compose.yaml should now match the full reference below.
✔️ Awesome, I've got everything!
Good work. Your Compose file now expresses the diagnosis as a shared network configuration.
ⓧ I'd like to double check the full code
services:
site:
image: nginx:1.30.5-alpine
ports:
- "127.0.0.1:8080:80"
volumes:
- ./site:/usr/share/nginx/html:ro
networks:
- web
monitor:
image: busybox:1.38.0
command: ["/bin/sh", "/monitor.sh"]
volumes:
- ./monitor.sh:/monitor.sh:ro
networks:
- web
networks:
web:
name: incident-web
How to Use This Reference
This reference shows the complete repaired network configuration. Its only top-level network is web.
Verify the monitor recovers
The monitor checks the site once every five seconds. This short pause gives the recreated container time to record a fresh result.
- Wait at least five seconds for one complete monitoring cycle.
Before you check, do you expect the next cycle to report a healthy or unreachable service? Hold that prediction.
- Display the service logs by running this command:
docker compose logs
What Do These Logs Prove?
- The logs command displays output from each service in the Compose project.
- Fresh status=healthy target=http://site entries prove that the monitor can resolve the site over incident-web.
- Earlier status=unreachable target=http://site entries can remain in the log history.
Still Seeing Only Unreachable Checks?
- Wait another five seconds.
- Run the logs command again.
- Compare your monitor network entry with the full compose.yaml reference above.
Help me find why the monitor still cannot reach the site.
Confirm both recovery paths
The monitor now tests the internal container path. Safari provides the separate host-access check.
Before you refresh, do you expect the network repair to change the host page? Keep your answer in mind.
- Switch back to Safari from earlier.
- Refresh the existing http://localhost:8080 page.
You'll still see Infrastructure Status. The page still shows Site service is responding on port 80.
Status Page No Longer Loading?
- Confirm that Safari still uses http://localhost:8080.
- Confirm that the site's ports block still contains 127.0.0.1:8080:80.
- Confirm that the site service remains attached to web.
Help me restore the status page after the network repair.
That closes the incident. Internal monitoring is healthy through incident-web.
Secret mission
Remove Host Exposure
Remove the site's published host port so Safari can no longer reach the status page. Then prove that the monitor still reaches the site through the shared container network.
Clean Up Your Resources
Clean Up Your Resources
This lab runs entirely on your Mac through Docker Desktop, so its containers and named network do not create usage charges. Choose whether to keep the lab running, pause its containers, or delete its Compose resources and project files.
Resources you used:
- A Docker Compose project containing the site and monitor containers.
- A named bridge network called incident-web.
- An infrastructure-incident-lab folder containing compose.yaml, monitor.sh, and site/index.html.
Keep everything running
No action needed. Choose this if you want the monitor to keep checking the site for demonstrations or later experiments.
- Leave the site and monitor containers running on incident-web.
- Keep the infrastructure-incident-lab folder for future troubleshooting practice.
- Keep the downloaded nginx and BusyBox images cached for faster future runs.
Pause - I'll come back to this later
Stop the running services to free local memory while preserving their containers. Your project files remain ready for your next session.
- Stop both services without removing them by running this command:
docker compose stop
What does this command do?
The docker compose stop command stops the site and monitor services without deleting their containers. Their configuration stays ready for a later restart.
- Return to Terminal in the infrastructure-incident-lab folder when you are ready to resume.
- Restart the existing containers by running this command:
docker compose start
How does the lab resume?
The docker compose start command starts the existing containers again. The services resume communicating through incident-web.
Delete - I don't want to use this again
Deleting this lab permanently removes its three project files. Your Docker Desktop installation stays untouched.
- Remove the lab's Compose resources and project folder by running these commands:
docker compose down
cd ..
rm -rf infrastructure-incident-lab
What does this cleanup remove?
- The docker compose down command stops and removes the two project containers.
- The same command removes the incident-web network created by the Compose project.
- The cd .. command moves Terminal into the parent directory.
- The rm -rf infrastructure-incident-lab command permanently deletes the project folder and its three files.
Cleanup complete. The lab containers, named network, and project files are now removed from your Mac.
Cached Images Stay Available
The downloaded nginx and BusyBox images remain in Docker's local cache. They do not create usage charges, but they continue to use local disk space.
Nice Work!
Nice Work!
Mission complete! You investigated a broken container network from the first failure through to verified recovery. Your finished lab proves that internal service discovery stays healthy without exposing the site to your Mac.
You've learned how to:
- Build a two-service incident lab with an nginx status page plus a repeating BusyBox monitor.
- Use process status, logs, and network inspection to prove that running services can still be unreachable.
- Repair service connectivity by placing both services on incident-web. Validate recovery through fresh status=healthy target=http://site monitor logs without using a hard-coded IP address.
- Secret Mission: Removed host exposure while preserving healthy container-to-container monitoring over incident-web.
Ready to quiz yourself?