Skip to main content
Docker Compose provides the simplest way to run all microservices locally without the overhead of Kubernetes. This setup is ideal for:
  • Quick local development and testing
  • Lower resource consumption compared to Kubernetes
  • Simpler debugging with direct container access
  • CI/CD pipelines and integration tests

Prerequisites

  • Environment setup complete
  • Docker Desktop or Docker Engine running
  • .env file copied from .env.example

Quick Start

1

Start all services

From the project root, run:
docker compose up
The first run will build all Docker images, which may take 5-10 minutes depending on your machine.
2

Verify services are running

Check that all containers are healthy:
docker compose ps
You should see all services in the “running” or “healthy” state.
3

Access the application

Open your browser to:
http://localhost:30081
This will load the frontend application routed through Traefik.

Service Architecture

The Docker Compose stack includes the following services:
┌─────────────────────────────────────────────────────────┐
│                     Traefik (30081)                     │
│              Reverse Proxy & API Gateway                │
└────────────────────┬────────────────────────────────────┘

        ┌────────────┼────────────┬────────────┐
        │            │            │            │
    ┌───▼───┐   ┌───▼───┐   ┌───▼───┐   ┌───▼────┐
    │Greeter│   │Gateway│   │ Auth  │   │Frontend│
    │ :8080 │   │ :8082 │   │ :8090 │   │  :80   │
    └───┬───┘   └───┬───┘   └───┬───┘   └────────┘
        │           │           │
    ┌───▼───┐   ┌───▼────────┐ │
    │Caller │   │Custom Lang │ │
    │ :8081 │   │   :3000    │ │
    └───┬───┘   └─────┬──────┘ │
        │             │        │
        └─────────────┴────────┴────────┐

                                   ┌─────▼─────┐
                                   │ PostgreSQL│
                                   │   :5432   │
                                   └───────────┘

Service Details

Infrastructure Services

Traefik

Reverse proxy and API gateway
  • Port: 30081 (HTTP)
  • Purpose: Routes requests to backend services based on path
  • Features: CORS middleware, rate limiting, request retry
  • Health check: http://localhost:30081/ping

PostgreSQL

Database server
  • Port: 5432
  • Credentials: devuser / devpass (from .env)
  • Databases: Automatically creates separate databases for each service
    • greeter_db
    • caller_db
    • gateway_db
    • lang_db
    • auth_db

Go Microservices (connect-go)

Greeter Service

  • Internal port: 8080
  • API path: /greeter.v1.GreeterService
  • Protocol: gRPC (connect-go)
  • Dependencies: Caller service, PostgreSQL
  • Environment variables:
    • CALLER_BASE_URL=http://caller:8081
    • EXTERNAL_API_URL=https://httpbin.org/get
    • DATABASE_URL=postgresql://devuser:devpass@postgres:5432/greeter_db

Caller Service

  • Internal port: 8081
  • Protocol: gRPC (connect-go)
  • Dependencies: PostgreSQL
  • Environment variables:
    • DATABASE_URL=postgresql://devuser:devpass@postgres:5432/caller_db

Gateway Service

  • Internal port: 8082
  • API path: /gateway.v1.GatewayService
  • Protocol: gRPC (connect-go)
  • Dependencies: Custom Lang Service, PostgreSQL
  • Environment variables:
    • CUSTOM_LANG_BASE_URL=http://custom-lang-service:3000
    • DATABASE_URL=postgresql://devuser:devpass@postgres:5432/gateway_db

Node.js Microservices (Express)

Auth Service

  • Internal port: 8090
  • API path: /auth
  • Protocol: HTTP/REST
  • Purpose: JWT authentication (login, signup, token validation)
  • Environment variables:
    • JWT_SECRET=dev-secret
    • DATABASE_URL=postgresql://devuser:devpass@postgres:5432/auth_db

Custom Lang Service

  • Internal port: 3000
  • Protocol: HTTP/REST
  • Purpose: Custom language processing service
  • Environment variables:
    • DATABASE_URL=postgresql://devuser:devpass@postgres:5432/lang_db

Frontend

React Application

  • Internal port: 80 (nginx)
  • Build-time args:
    • VITE_API_BASE_URL=http://localhost:30081
  • Technology: React 19 + TypeScript + Vite
  • API Client: connect-query (connect-rpc + TanStack Query)

Port Reference

ServiceInternal PortExternal PortAccess URL
Traefik8030081http://localhost:30081
PostgreSQL54325432localhost:5432
Greeter8080-Via Traefik
Caller8081-Internal only
Gateway8082-Via Traefik
Auth8090-Via Traefik
Custom Lang3000-Internal only
Frontend80-Via Traefik
Only Traefik and PostgreSQL expose ports to the host. All other services communicate through Docker’s internal network.

Common Operations

Start services in background

docker compose up -d

View logs

docker compose logs -f

Restart a service

# Restart after code changes
docker compose restart greeter

# Rebuild and restart
docker compose up -d --build greeter

Stop services

# Stop all services
docker compose stop

# Stop and remove containers
docker compose down

# Stop and remove containers + volumes
docker compose down -v
Using docker compose down -v will delete all database data. Only use this when you want a clean slate.

Execute commands in containers

docker compose exec postgres psql -U devuser -d greeter_db

Traefik Routing

Traefik routes requests based on path prefixes:

API Services (gRPC)

Connect-go services use the pattern /package.version.ServiceName/Method:
# Greeter service
curl -X POST http://localhost:30081/greeter.v1.GreeterService/Greet \
  -H "Content-Type: application/json" \
  -d '{"name": "World"}'

# Gateway service
curl -X POST http://localhost:30081/gateway.v1.GatewayService/InvokeCustom \
  -H "Content-Type: application/json" \
  -d '{"name": "Test"}'

Auth Service (REST)

# Signup
curl -X POST http://localhost:30081/auth/signup \
  -H "Content-Type: application/json" \
  -d '{"username": "testuser", "password": "testpass"}'

# Login
curl -X POST http://localhost:30081/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "testuser", "password": "testpass"}'

Frontend

All other paths route to the React frontend:
http://localhost:30081/

Middleware Configuration

Traefik applies the following middleware automatically:
MiddlewareServicesPurpose
cors@fileAllCORS headers for browser requests
auth@filegreeter, gatewayJWT token validation
rate-limit@fileAllRate limiting (configurable in deploy/traefik/dynamic/)
retry@filegreeterAutomatic retry on failure

Debugging

Check service health

# View container status
docker compose ps

# Check specific service logs
docker compose logs greeter --tail=50

# Follow logs in real-time
docker compose logs -f greeter gateway

Test gRPC endpoints directly

If you have the devenv shell active, you can use grpcurl:
# List available services (note: won't work through Traefik)
docker compose exec greeter grpcurl -plaintext localhost:8080 list

# Call a method directly
docker compose exec greeter grpcurl -plaintext -d '{"name": "Test"}' \
  localhost:8080 greeter.v1.GreeterService/Greet

Inspect database

# Connect to PostgreSQL
docker compose exec postgres psql -U devuser -d greeter_db

# List all databases
docker compose exec postgres psql -U devuser -c '\l'

# Query a table
docker compose exec postgres psql -U devuser -d greeter_db -c 'SELECT * FROM users;'

Reset everything

If you encounter issues, try a clean restart:
# Stop and remove all containers and volumes
docker compose down -v

# Rebuild from scratch
docker compose build --no-cache

# Start fresh
docker compose up

Development Workflow

1

Make code changes

Edit source files in services/, node-services/, or frontend/
2

Rebuild the service

docker compose up -d --build <service-name>
3

Test the changes

Use curl, the frontend, or automated tests to verify functionality
4

Check logs

docker compose logs -f <service-name>
For faster iteration on the frontend, use the local development server instead of rebuilding the Docker container.

Troubleshooting

Service won’t start

Check dependencies:
# Ensure PostgreSQL is healthy
docker compose ps postgres

# View startup logs
docker compose logs <service-name>

Port already in use

If port 30081 or 5432 is already in use:
# Find what's using the port
lsof -i :30081

# Kill the process or change ports in docker-compose.yml

Database connection errors

Verify PostgreSQL is healthy and accepting connections:
docker compose exec postgres pg_isready -U devuser

Traefik routing issues

Check Traefik configuration:
# View Traefik logs
docker compose logs traefik

# Inspect container labels
docker inspect <service-container-name> | grep traefik

Next Steps

Kubernetes + Tilt

Advanced local development with full observability

Frontend Development

Run frontend with hot reload

Build docs developers (and LLMs) love