NetSendo
Platform Updates

NetSendo v2.11.4: Fix the Unhealthy Webserver Healthcheck

NetSendo Team6 min read

Your monitoring says the NetSendo webserver is down, but campaigns are going out, the dashboard loads and subscribers are signing up. So what's going on? If you run NetSendo with Docker Compose, you may have seen this: docker compose ps showed the webserver container as "unhealthy" while everything else worked.

NetSendo v2.11.4 fixes this. It's a small change, but it affects every installation. It also matters more than it looks, because a false health signal makes your alerts useless and blocks services that depend on a healthy webserver.

TL;DR: The webserver healthcheck asked http://localhost/. Inside the nginx image that name resolves to IPv6 ::1 first, but nginx only listens on IPv4, so every check was refused. v2.11.4 changes the check to http://127.0.0.1/. The fix is in docker-compose.yml only, so pulling the new image won't fix it. You need to update the compose file (see README → Updates) or edit the healthcheck yourself, then run docker compose up -d.

🚀 What's New in v2.11.4

The Problem: A Healthcheck Knocking on the Wrong Door

Docker healthchecks run a command inside the container at regular intervals. If the command fails, Docker marks the container unhealthy. NetSendo's webserver healthcheck sent a request to http://localhost/, which seems reasonable.

The catch is name resolution. In the nginx image, localhost maps to both 127.0.0.1 (IPv4) and ::1 (IPv6), and the client tries ::1 first. NetSendo's nginx listens on IPv4 only. Each check hit an IPv6 address where nothing was listening, the connection was refused, and Docker logged another failure.

ℹ️ Analogy: Think of a building with two front doors, one labeled IPv4 and one labeled IPv6. The receptionist (nginx) only works behind the IPv4 door. The healthcheck kept knocking on the IPv6 door, got no answer and reported that nobody was home. Meanwhile, real visitors kept walking through the IPv4 door.

The Fix: Be Explicit About the Address

The healthcheck now asks http://127.0.0.1/ directly. There's no name resolution and no guessing between IPv4 and IPv6, so the check reaches the address nginx actually listens on.

🩺

Accurate Health Status

The webserver reports "healthy" when it is healthy.

📈

Reliable Monitoring

Tools that read Docker health stop raising false alarms.

🧩

MCP Service Starts

The opt-in mcp service, which waits for a healthy webserver, can now start.

⚠️ Important: The application didn't change in this release. Only docker-compose.yml did. If you just run docker compose pull, your containers will still be "unhealthy" because the old healthcheck is still in your local compose file.

💼 Why This Matters: Real-World Scenarios

🛒

E-commerce: Trustworthy Alerts

When your abandoned cart and post-purchase sequences run on NetSendo, you need to know right away if the webserver really goes down. A container that always shows "unhealthy" teaches your team to ignore alerts, and then they'll miss a real outage too. With this fix:

  • Uptime dashboards that read Docker health show green when the service is up
  • Alerts start meaning something again
  • A real failure stands out from the noise
🤖

SaaS: Unblocking the MCP Service

Teams that enabled the opt-in mcp service for AI-assisted workflows, such as onboarding or churn-prevention automation, found it never started. It waits for a healthy webserver, and the webserver never became healthy. After you apply the fix, mcp starts as expected.

🏢

Agencies: Clean Multi-Instance Fleets

If you run separate NetSendo instances for several clients, a false "unhealthy" status on every one of them clutters your fleet overview. Once you roll the updated compose file out across instances, you get one clear view of which clients actually need attention.

🛠️ Step-by-Step: Applying the Fix

You can use either of two methods. Method A is recommended because it keeps your compose file in sync with upstream.

Method A: Take the New docker-compose.yml

  1. Back up your current compose file

    Before you replace it, keep a copy so you can bring back any local customizations.

    cp docker-compose.yml docker-compose.yml.bak
  2. Follow README → Updates

    The NetSendo README has an Updates section that explains how to get the latest docker-compose.yml. Follow it to get the v2.11.4 version.

  3. Merge your customizations back

    Compare the new file with your backup and re-apply your own changes, such as ports, volumes or extra services.

    diff docker-compose.yml.bak docker-compose.yml
  4. Recreate the containers
    docker compose up -d

Method B: Edit the Healthcheck Manually

  1. Open docker-compose.yml

    Find the webserver service and its healthcheck block.

  2. Replace localhost with 127.0.0.1

    In the healthcheck's test command, change http://localhost/ to http://127.0.0.1/. Leave the rest of the command unchanged. Conceptually, the change looks like this:

    # before
    healthcheck:
      test: [ ... "http://localhost/" ... ]
    
    # after
    healthcheck:
      test: [ ... "http://127.0.0.1/" ... ]
  3. Apply the change
    docker compose up -d

    Compose sees that the service configuration changed and recreates the webserver container.

Verify It Worked

After the healthcheck interval passes, check the status:

docker compose ps

The webserver should now show (healthy). For more detail, you can inspect the recent health log of the container:

docker inspect --format '{{json .State.Health}}' $(docker compose ps -q webserver)

Common Pitfalls

  • Only pulling images: docker compose pull doesn't touch your compose file, so the old healthcheck stays.
  • Checking too early: right after a restart, the status shows "starting" for a short time. Wait for at least one healthcheck interval.
  • A broad search-and-replace: if you use a global sed on localhost, you may change other services that need it. Edit only the webserver's healthcheck.

🎯 Expert Tips

1
Prefer explicit loopback addresses in healthchecks

In your own containers, use 127.0.0.1 (or [::1] if the service listens on IPv6) instead of localhost. This avoids surprises from resolver ordering across base images.

2
Keep customizations in an override file

Put your local changes in docker-compose.override.yml. Then you can take the upstream docker-compose.yml on every update without merging anything by hand.

3
Read the release notes for compose changes

When a release says only docker-compose.yml changed, treat it as a manual step. Add a "diff the compose file" check to your update routine.

4
Alert on health transitions, not one-off failures

Set up monitoring to fire when the status changes from healthy to unhealthy. This keeps alerts useful, and now that the check reports correctly, it works well.

✅ Conclusion

  • ✅ The webserver healthcheck now targets 127.0.0.1 and reports the correct status
  • ✅ Monitoring that reads Docker health stops showing false failures
  • ✅ The opt-in mcp service can start again
  • ✅ No application changes, so the update carries no risk to your data or campaigns

Remember that the fix lives in docker-compose.yml. Update the file or edit the single line, then run docker compose up -d. Because NetSendo is self-hosted, you own the whole stack, and small fixes like this keep it reporting accurately. For more on running NetSendo in production, see the documentation and browse the available integrations.

Keep Your NetSendo Stack Healthy

Update to v2.11.4 and get Docker health statuses you can trust:

Like what we're building? Star NetSendo on GitHub ⭐

#docker#healthcheck#nginx#self-hosted#bugfix#devops
Share