I Dockerized my app and one button broke - thanks, localhost
The setup
My side project, TestFlow Agent, is three little services:
- a mock API (the thing under test)
- a backend that generates and runs test flows
- a React frontend
One of the headline features is a "Run Enrollment Flow" button: the backend fires a real sequence of requests at the mock API and reports back "enrollment created, status ACTIVE." Locally, it worked every single time. Then I added a one-command Docker setup so anyone could try it without juggling three terminals.
docker compose up --build, open the app, click around⦠analyze works, generate works, and then - "Run Enrollment Flow" fails. Only that button. The one that talks between services.
The hunt
The frontend calls the backend fine (both ports are published to the host). Generation is pure computation - fine. The only thing different about "Run Flow" is that the backend itself makes an outbound HTTP call to the mock API. I opened the backend and there it was:
const baseUrl = ' http://localhost:4000 ' ; // π Natively, localhost:4000 is the mock API - same machine.
But inside the backend container, localhost means the container itself. There's no mock API on port 4000 in there. The backend was cheerfully calling itself and getting nothing. Classic "works on my machine," except the machine changed out from under it the moment it went into a container.
The fix
In Docker Compose, services reach each other by service name, not localhost. So I made the target configurable and pointed it at the service name inside the compose network - while keeping localhost as the default so native runs don't change at all:
// backend/routes/analysisRoutes.js
const baseUrl = process . env . MOCK_API_URL || ' http://localhost:4000 ' ;
# docker-compose.yml
backend :
environment :
- MOCK_API_URL=http://mock-api:4000 # reach the mock by its service name
depends_on :
- mock-api
π PR: https://github.com/sshankar07/test-flow-agent/pull/3
Before / after
- Before: Run Flow in Docker β backend calls
localhost:4000β nothing there β failure. - After: Run Flow in Docker β backend calls
mock-api:4000β cross-container SUCCESS. I watched it create an enrollment (ENR-4C554E55, status ACTIVE) from inside the compose stack.
What I took away
- Hardcoded
localhostis a time bomb the day you containerize. It's invisible until the network boundary moves. - The fix isn't "hardcode the service name" either - it's make it configurable with a sane default, so both native and Docker runs work with zero edits.
- Container-to-container = service-name DNS; host-to-container = published ports. Mixing them up is the root of a huge share of "it works locally but not in Docker" bugs.
Small diff, satisfying fix, and a reminder that the oldest bugs are the ones that keep showing up in new outfits.
Repo (open source, MIT): https://github.com/sshankar07/test-flow-agent
Comments
No comments yet. Start the discussion.