Files
payguard-poc/NETWORKING_GUIDE.md
John Patrick TeruelandCopilot 982b70a2ca Add comprehensive Docker networking documentation
Created NETWORKING_GUIDE.md explaining the host.docker.internal solution:

- Problem: Docker frontend can't reach localhost:8000 on host
- Solution: Use host.docker.internal for automatic DNS mapping
- Implementation details for App.tsx, docker-compose.yml, .env
- Step-by-step usage guide (3-terminal setup)
- Verification procedures and troubleshooting
- Platform-specific notes (macOS, Windows, Linux)
- Development workflow (Docker vs npm dev)
- Technical background and alternatives
- Advanced configuration options
- Quick reference cheat sheet

Benefits:
- Frontend automatically selects correct API URL
- No manual configuration needed
- Works on Docker Desktop (macOS/Windows)
- Easy development workflow
- Production-ready setup

Co-authored-by: Copilot <[email protected]>
2026-04-24 20:05:21 +08:00

9.1 KiB

Docker Frontend-to-Backend Networking Guide

Problem Solved ✅

The React frontend running inside a Docker container needed to connect to the FastAPI backend running on the host machine at localhost:8000.

The Challenge: Inside a Docker container, localhost refers to the container itself, not your host machine.

The Solution: Use host.docker.internal - Docker Desktop's special DNS name that automatically maps to the host machine.


How It Works

Network Architecture

Your Host Machine                          Docker Container
┌──────────────────────────┐              ┌──────────────────────┐
│                          │              │                      │
│  FastAPI                 │◄─────────────┤  React Frontend      │
│  localhost:8000          │              │  host.docker         │
│                          │              │  .internal:8000      │
│  Consumer                │              │                      │
│  (Kafka listener)        │              └──────────────────────┘
│                          │
└──────────────────────────┘

The Magic

Docker Desktop automatically provides host.docker.internal as a DNS entry that resolves to your host machine's localhost. This allows containers to reach services running on the host.


Implementation Details

1. Frontend Code (App.tsx)

The frontend intelligently selects the API URL based on environment:

const API_BASE_URL = process.env.NODE_ENV === 'production' 
  ? 'http://host.docker.internal:8000'    // Docker containers
  : 'http://localhost:8000';                 // Local development

Benefits:

  • npm run dev (development mode) → uses localhost:8000 ✓
  • Docker (production mode) → uses host.docker.internal:8000 ✓
  • No hardcoding, automatic detection ✓

2. Docker Configuration (docker-compose.yml)

Frontend service explicitly sets the environment:

frontend:
  build:
    context: ./frontend
    dockerfile: Dockerfile
  environment:
    - NODE_ENV=production          # Triggers production API URL
    - VITE_API_URL=...             # Optional override

3. Environment Configuration (.env.example)

# Frontend can optionally override with environment variable
VITE_API_URL=http://host.docker.internal:8000
NODE_ENV=production

How to Use

Standard Setup (3 Terminals)

Terminal 1: Start infrastructure + frontend in Docker

docker compose up

Output shows:

frontend  | ✓ Ready on http://0.0.0.0:3000
kafka     | [Ready]
redis     | [Ready]
zookeeper | [Ready]

Terminal 2: Start API on your machine

source venv/bin/activate
uvicorn api.main:app --reload --port 8000

Output shows:

Uvicorn running on http://127.0.0.1:8000

Terminal 3: Start consumer on your machine

cd consumer && python pipeline.py

Output shows:

Connected to Kafka broker at kafka:9092
Consumer ready...

Browser: Open http://localhost:3000

The frontend automatically connects to your API at http://host.docker.internal:8000 and works seamlessly!


Verification

Check Frontend Can Reach API

# From inside the Docker container
docker exec $(docker ps -q -f name=payguard-frontend) \
  wget --spider -q http://host.docker.internal:8000

# Should complete without error (exit code 0)

Manual Test

# Start API first
uvicorn api.main:app --reload

# Then in another terminal, test the endpoint
curl http://localhost:8000/health
# Should return: {"status": "healthy", ...}

From Frontend

The frontend automatically tests connectivity:

const generateToken = async () => {
  try {
    const response = await axios.post(`${API_BASE_URL}/token`, {
      user_id: auth.userId,
    });
    // Connection succeeded!
    setAuth(prev => ({...prev, token: ...}));
  } catch (error) {
    // Connection failed
    setError("Could not connect to API at " + API_BASE_URL);
  }
};

Platform Compatibility

Platform Support Notes
macOS ✅ Full Docker Desktop includes host.docker.internal
Windows ✅ Full Docker Desktop includes host.docker.internal
Linux ⚠️ Manual May need configuration (see below)

Linux-Specific Setup

On Linux, host.docker.internal is not automatically available. Options:

Option 1: Use Docker gateway (Recommended)

frontend:
  environment:
    - VITE_API_URL=http://172.17.0.1:8000  # Default Docker gateway

Option 2: Pass --add-host flag

docker compose run --add-host=host.docker.internal:host-gateway frontend

Option 3: Use network_mode: host

frontend:
  network_mode: "host"
  # Then remove ports and depends_on conditions

Development Workflow

Running Frontend Locally (npm dev mode)

If you want to develop the frontend with hot-reloading:

# Stop the Docker frontend
docker compose stop frontend

# Start the local dev server
cd frontend
npm run dev

# This uses localhost:8000 automatically

The dev server will hot-reload as you edit code.

Running Frontend in Docker (production build)

docker compose up frontend

# Uses host.docker.internal:8000
# Production-optimized build, no hot-reload

Troubleshooting

"Cannot connect to API" Error

Check 1: Is the API running?

curl http://localhost:8000/health

Check 2: Is it listening on port 8000?

lsof -i :8000
# Should show: uvicorn  LISTEN

Check 3: Is Docker container healthy?

docker compose ps
# Should show: frontend  Up (healthy)

Check 4: Test from inside container

docker exec $(docker ps -q -f name=payguard-frontend) \
  wget --verbose http://host.docker.internal:8000
# Should succeed with 200 status

API is running but frontend still fails

Try these in order:

  1. Rebuild frontend

    docker compose up --build frontend
    
  2. Check logs

    docker compose logs -f frontend
    # Look for connection errors
    
  3. On Linux, use manual gateway

    # Edit docker-compose.yml
    # Change VITE_API_URL to http://172.17.0.1:8000
    docker compose up --build
    
  4. Hardcode the URL (Debug only)

    # Edit frontend/.env file
    VITE_API_URL=http://host.docker.internal:8000
    docker compose up --build
    

Advanced Configuration

Custom API URL

If you need a different API location:

Option 1: Environment variable

# Create frontend/.env (or .env.local)
VITE_API_URL=http://custom-api.example.com:8000

# Then restart
docker compose up --build frontend

Option 2: Modify docker-compose.yml

frontend:
  environment:
    - VITE_API_URL=http://api.example.com:8000

Option 3: Modify App.tsx (hardcoding - not recommended)

const API_BASE_URL = 'http://your-api:8000';

Multi-Machine Setup

If API is on a different machine:

frontend:
  environment:
    - VITE_API_URL=http://192.168.1.100:8000  # Replace with your API machine

Technical Background

Why host.docker.internal Works

Docker Desktop runs a lightweight Linux VM that contains your containers. Docker Desktop includes special DNS resolution:

  • In containers: host.docker.internal → 192.168.65.2 (Docker Desktop VM gateway)
  • In Docker Desktop VM: 192.168.65.2 → your host machine's localhost

This creates a transparent bridge from containers to the host.

Alternative Approaches (Not Used Here)

  1. Docker network with backend container - Would require FastAPI in Docker too
  2. Use external network - More complex setup
  3. Use host network mode - Breaks container isolation
  4. API in Docker, frontend on host - Opposite of current setup

The current approach (host.docker.internal) is the best balance for development:

  • ✅ Frontend uses production Docker build
  • ✅ API/Consumer use local development with hot-reloading
  • ✅ Easy to iterate and debug
  • ✅ No complex networking configuration

Summary

Aspect Solution
Frontend location Docker container (port 3000)
API location Your host machine (port 8000)
Connection method host.docker.internal:8000
Auto-detection Yes (based on NODE_ENV)
Configuration needed None (automatic!)
Works on macOS/Windows Yes ✓
Works on Linux Yes (with one env var)

Quick Reference

# Everything you need

# Terminal 1: Start Docker infrastructure + frontend
docker compose up

# Terminal 2: Start API
source venv/bin/activate
uvicorn api.main:app --reload

# Terminal 3: Start consumer
cd consumer && python pipeline.py

# Browser: Open this URL
http://localhost:3000

Frontend automatically finds API at host.docker.internal:8000.

No additional configuration needed! ✨