Skip to main content
The application uses two Docker Compose files that work together:
  • docker-compose.yml - Base service definitions and build contexts
  • docker-compose.override.yml - Development environment overrides with ports, volumes, and environment variables

Complete Configuration

docker-compose.yml

Defines all services and their build contexts:
version: '3.4'

services:
  catalogdb:
    image: postgres

  basketdb:
    image: postgres

  distributedcache:
    image: redis

  orderdb:
    image: mcr.microsoft.com/mssql/server

  messagebroker:
    image: rabbitmq:management

  catalog.api:
    image: ${DOCKER_REGISTRY-}catalogapi
    build:
      context: .
      dockerfile: Services/Catalog/Catalog.API/Dockerfile

  basket.api:
    image: ${DOCKER_REGISTRY-}basketapi
    build:
      context: .
      dockerfile: Services/Basket/Basket.API/Dockerfile

  discount.grpc:
    image: ${DOCKER_REGISTRY-}discountgrpc
    build:
      context: .
      dockerfile: Services/Discount/Discount.Grpc/Dockerfile

  ordering.api:
    image: ${DOCKER_REGISTRY-}orderingapi
    build:
      context: .
      dockerfile: Services/Ordering/Ordering.API/Dockerfile

  yarpapigateway:
    image: ${DOCKER_REGISTRY-}yarpapigateway
    build:
      context: .
      dockerfile: ApiGateways/YarpApiGateway/Dockerfile

  shopping.web:
    image: ${DOCKER_REGISTRY-}shoppingweb
    build:
      context: .
      dockerfile: WebApps/Shopping.Web/Dockerfile

volumes:
  postgres_catalog:
  postgres_basket:

docker-compose.override.yml

Provides development-specific configuration:
version: '3.4'

services:
  catalogdb:
    container_name: catalogdb
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=CatalogDb
    restart: always
    ports:
        - "5432:5432"
    volumes:
      - postgres_catalog:/var/lib/postgresql/data/ 
      
  basketdb:
    container_name: basketdb
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=BasketDb
    restart: always
    ports:
        - "5433:5432"
    volumes:
      - postgres_basket:/var/lib/postgresql/data/ 

  distributedcache:
    container_name: distributedcache
    restart: always
    ports:
      - "6379:6379"

  orderdb:
    container_name: orderdb
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=SwN12345678
    restart: always
    ports:
      - "1433:1433"

  messagebroker:
    container_name: messagebroker
    hostname: ecommerce-mq
    environment:
      - RABBITMQ_DEFAULT_USER=guest
      - RABBITMQ_DEFAULT_PASS=guest
    restart: always
    ports:
      - "5672:5672"
      - "15672:15672"

  catalog.api:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_HTTP_PORTS=8080
      - ASPNETCORE_HTTPS_PORTS=8081
      - ConnectionStrings__Database=Server=catalogdb;Port=5432;Database=CatalogDb;User Id=postgres;Password=postgres;Include Error Detail=true
    depends_on:
      - catalogdb
    ports:
      - "6000:8080"
      - "6060:8081"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro

  basket.api:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_HTTP_PORTS=8080
      - ASPNETCORE_HTTPS_PORTS=8081
      - ConnectionStrings__Database=Server=basketdb;Port=5432;Database=BasketDb;User Id=postgres;Password=postgres;Include Error Detail=true
      - ConnectionStrings__Redis=distributedcache:6379
      - GrpcSettings__DiscountUrl=https://discount.grpc:8081
      - MessageBroker__Host=amqp://ecommerce-mq:5672
      - MessageBroker__UserName=guest
      - MessageBroker__Password=guest
    depends_on:
      - basketdb
      - distributedcache
      - discount.grpc
      - messagebroker
    ports:
      - "6001:8080"
      - "6061:8081"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro

  discount.grpc:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_HTTP_PORTS=8080
      - ASPNETCORE_HTTPS_PORTS=8081
      - ConnectionStrings__Database=Data Source=discountdb
    ports:
      - "6002:8080"
      - "6062:8081"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro

  ordering.api:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_HTTP_PORTS=8080
      - ASPNETCORE_HTTPS_PORTS=8081
      - ConnectionStrings__Database=Server=orderdb;Database=OrderDb;User Id=sa;Password=SwN12345678;Encrypt=False;TrustServerCertificate=True
      - MessageBroker__Host=amqp://ecommerce-mq:5672
      - MessageBroker__UserName=guest
      - MessageBroker__Password=guest
      - FeatureManagement__OrderFullfilment=false
    depends_on:
      - orderdb
      - messagebroker
    ports:
      - "6003:8080"
      - "6063:8081"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro

  yarpapigateway:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_HTTP_PORTS=8080
      - ASPNETCORE_HTTPS_PORTS=8081
    depends_on:
      - catalog.api
      - basket.api
      - ordering.api
    ports:
      - "6004:8080"
      - "6064:8081"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro

  shopping.web:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_HTTP_PORTS=8080
      - ASPNETCORE_HTTPS_PORTS=8081
      - ApiSettings__GatewayAddress=http://yarpapigateway:8080
    depends_on:
      - yarpapigateway
    ports:
      - "6005:8080"
      - "6065:8081"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro

Service Dependencies

The depends_on configuration ensures services start in the correct order:
catalog.api:
  depends_on:
    - catalogdb

basket.api:
  depends_on:
    - basketdb
    - distributedcache
    - discount.grpc
    - messagebroker

ordering.api:
  depends_on:
    - orderdb
    - messagebroker

yarpapigateway:
  depends_on:
    - catalog.api
    - basket.api
    - ordering.api

shopping.web:
  depends_on:
    - yarpapigateway

Docker Registry Support

The ${DOCKER_REGISTRY-} prefix allows pushing images to a custom registry:
# Use default local images
docker-compose build

# Push to custom registry
export DOCKER_REGISTRY=myregistry.azurecr.io/
docker-compose build
docker-compose push

Common Commands

Build and Start

# Build all images
docker-compose build

# Start all services in detached mode
docker-compose up -d

# Build and start together
docker-compose up -d --build

Manage Services

# Stop all services
docker-compose stop

# Start stopped services
docker-compose start

# Restart specific service
docker-compose restart catalog.api

# Remove all containers
docker-compose down

# Remove containers and volumes
docker-compose down -v

Monitoring

# View all logs
docker-compose logs -f

# View logs for specific service
docker-compose logs -f catalog.api

# View last 100 lines
docker-compose logs --tail=100

# Check service status
docker-compose ps

Scaling Services

# Run multiple instances (requires removing container_name)
docker-compose up -d --scale catalog.api=3

Troubleshooting

Port Conflicts

If ports are already in use:
# Modify docker-compose.override.yml to use different host ports
catalog.api:
  ports:
    - "7000:8080"  # Changed from 6000 to 7000

Database Connection Issues

Ensure database containers are fully initialized:
# Check database logs
docker-compose logs catalogdb

# Wait for "database system is ready to accept connections"

Volume Permissions

On Linux, ensure proper permissions:
# Remove problematic volumes
docker-compose down -v

# Recreate with proper permissions
docker-compose up -d

Production Considerations

For production deployments:
  1. Create separate docker-compose.prod.yml with production settings
  2. Use secrets management instead of environment variables
  3. Configure resource limits
  4. Enable health checks
  5. Use specific image tags instead of latest
  6. Configure logging drivers
  7. Set up monitoring and alerting
# Example production overrides
services:
  catalog.api:
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
      restart_policy:
        condition: on-failure
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Build docs developers (and LLMs) love