Skip to content

Containerization in ConnectSoft Microservice Template

Purpose & Overview

Containerization in the ConnectSoft Microservice Template enables consistent, portable, and scalable deployments using Docker containers. The template provides a production-ready containerization strategy with multi-stage builds, optimized image sizes, secure configurations, and comprehensive orchestration via Docker Compose.

Containerization provides:

  • Consistency: Same application behavior across development, staging, and production
  • Isolation: Application and dependencies packaged together
  • Portability: Run anywhere Docker is supported (local, cloud, Kubernetes)
  • Scalability: Easy horizontal scaling and orchestration
  • Reproducibility: Versioned images ensure consistent deployments
  • Security: Minimal attack surface with optimized base images

Containerization Philosophy

The ConnectSoft template treats containers as first-class deployment artifacts. Every microservice is containerized with optimized multi-stage builds, security best practices, and production-ready configurations. Containers are immutable, versioned, and designed for orchestration platforms.

Architecture Overview

Containerization Layers

Application Container
├── Base Runtime Image (mcr.microsoft.com/dotnet/aspnet:9.0)
│   ├── ASP.NET Core Runtime
│   └── .NET Runtime
├── Published Application
│   ├── Application DLLs
│   ├── Configuration Files
│   └── Dependencies
└── Entry Point
    └── dotnet ConnectSoft.MicroserviceTemplate.Application.dll

Multi-Container Architecture

Docker Compose Stack
├── Application Container
│   └── ConnectSoft.MicroserviceTemplate.Application
├── Database Containers
│   ├── SQL Server (NHibernate)
│   ├── MongoDB (MongoDB Persistence)
│   └── Redis (Caching, SignalR Backplane)
├── Messaging Containers
│   └── RabbitMQ (MassTransit)
├── Observability Containers
│   ├── OpenTelemetry Collector
│   ├── Prometheus (Metrics)
│   ├── Grafana (Visualization)
│   ├── Jaeger (Tracing)
│   ├── Elasticsearch (Logs)
│   └── Kibana (Log Analysis)
└── Infrastructure Containers
    ├── Certificate Generator
    └── MCP Inspector (if enabled)

Dockerfile

Multi-Stage Build Strategy

The Dockerfile uses a multi-stage build to minimize final image size and optimize build caching:

#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.

# Stage 1: Base runtime image
FROM mcr.microsoft.com/dotnet/aspnet:9.0@sha256:b4bea3a52a0a77317fa93c5bbdb076623f81e3e2f201078d89914da71318b5d8 as base
WORKDIR /app
EXPOSE 8081  # HTTP port
EXPOSE 7279  # HTTPS port

# Stage 2: Build stage
FROM mcr.microsoft.com/dotnet/sdk:9.0@sha256:3fcf6f1e809c0553f9feb222369f58749af314af6f063f389cbd2f913b4ad556 AS build

ARG BUILD_CONFIGURATION=Release
WORKDIR app

# Copy dependency files first (optimizes layer caching)
COPY ["Directory.Packages.props", "."]
COPY ["Directory.Build.props", "."]
COPY ["Directory.Packages.props", "./ConnectSoft.MicroserviceTemplate.Application/"]
COPY ["Directory.Build.props", "./ConnectSoft.MicroserviceTemplate.Application/"]
COPY ["nuget.config", "."]
COPY ["Scripts/.", "./Scripts/"]
COPY ["ConnectSoft.MicroserviceTemplate.slnx", "."]

# Copy all .csproj files (enables dependency restore caching)
COPY ["ConnectSoft.MicroserviceTemplate/*.csproj", "./ConnectSoft.MicroserviceTemplate/"]
COPY ["ConnectSoft.MicroserviceTemplate.Application/*.csproj", "./ConnectSoft.MicroserviceTemplate.Application/"]
COPY ["ConnectSoft.MicroserviceTemplate.ApplicationModel/*.csproj", "./ConnectSoft.MicroserviceTemplate.ApplicationModel/"]
# ... copy all other project files ...

# Restore NuGet packages (cached if project files unchanged)
RUN dotnet restore ./ConnectSoft.MicroserviceTemplate.Application/ConnectSoft.MicroserviceTemplate.Application.csproj \
    --configfile "nuget.config"

# Copy source code
COPY ["ConnectSoft.MicroserviceTemplate/.", "./ConnectSoft.MicroserviceTemplate/"]
COPY ["ConnectSoft.MicroserviceTemplate.Application/.", "./ConnectSoft.MicroserviceTemplate.Application/"]
# ... copy all source files ...

# Stage 3: Publish stage
FROM build AS publish
RUN dotnet publish "/app/ConnectSoft.MicroserviceTemplate.Application/ConnectSoft.MicroserviceTemplate.Application.csproj" \
    -c $BUILD_CONFIGURATION \
    -o /app/publish \
    --configfile "/app/nuget.config" \
    --no-restore

# Stage 4: Final runtime image
FROM base AS final
WORKDIR /app

# Copy published application from publish stage
COPY --from=publish /app/publish .

# Set entry point
ENTRYPOINT ["dotnet", "ConnectSoft.MicroserviceTemplate.Application.dll"]

Build Stage Breakdown

Stage 1: Base Runtime Image

FROM mcr.microsoft.com/dotnet/aspnet:9.0@sha256:... as base
WORKDIR /app
EXPOSE 8081
EXPOSE 7279

Purpose: - Minimal runtime image with only ASP.NET Core runtime - No SDK tools (reduces image size) - Pinned to specific SHA256 digest for reproducibility - Exposes HTTP (8081) and HTTPS (7279) ports

Stage 2: Build Stage

FROM mcr.microsoft.com/dotnet/sdk:9.0@sha256:... AS build

Purpose: - Full SDK for building and restoring packages - Copies dependency files first (optimizes Docker layer caching) - Restores NuGet packages (cached if project files unchanged) - Copies source code and builds application

Layer Caching Strategy: 1. Copy dependency files (Directory.Packages.props, nuget.config, .csproj files) 2. Restore packages (this layer is cached if dependency files unchanged) 3. Copy source code (this layer invalidates on code changes) 4. Build application

Stage 3: Publish Stage

FROM build AS publish
RUN dotnet publish ...

Purpose: - Publishes application with optimizations - Uses --no-restore for faster builds (packages already restored) - Outputs to /app/publish directory

Stage 4: Final Runtime Image

FROM base AS final
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ConnectSoft.MicroserviceTemplate.Application.dll"]

Purpose: - Minimal final image (only runtime, not SDK) - Copies only published output - Sets entry point to run application

Benefits of Multi-Stage Builds

  1. Smaller Image Size: Final image contains only runtime, not SDK (~200MB vs ~800MB)
  2. Better Caching: Dependency files copied separately from source code
  3. Security: No build tools in production image
  4. Reproducibility: Pinned base image SHA256 digests

Docker Compose

docker-compose.yml

The main Docker Compose file defines the complete application stack:

services:
  # Application Service
  connectsoft.microservicetemplate.application:
    image: ${DOCKER_REGISTRY-}connectsoft.microservicetemplate.application
    container_name: connectsoft.microservicetemplate.application
    build:
      context: .
      dockerfile: ../ConnectSoft.MicroserviceTemplate.Application/Dockerfile
    depends_on:
      - sql
      - redis
      - mongo
      - otel-collector
      - rabbitmq
      - prometheus
      - grafana
      - elasticsearch
      - kibana
    networks:
      - backend

  # Certificate Generator
  certgen:
    image: mcr.microsoft.com/dotnet/sdk:8.0
    container_name: certgen
    entrypoint: ["bash","-lc","dotnet dev-certs https -ep /out/aspnetapp.pfx -p 123456"]
    volumes:
      - httpscerts:/out
    restart: "no"

  # SQL Server
  sql:
    image: "mcr.microsoft.com/mssql/server:2022-latest"
    container_name: sql
    ports:
      - "1433:1433"
    environment:
      - ACCEPT_EULA=y
      - MSSQL_SA_PASSWORD=ConnectSoft123!
      - MSSQL_MEMORY_LIMIT_MB=4096
      - MSSQL_PID=Developer
    networks:
      - backend

  # Redis
  redis:
    image: redis
    container_name: redis
    ports:
      - "60003:60002"
    networks:
      - backend

  # MongoDB
  mongo:
    image: mongo:latest
    container_name: mongo
    ports:
      - "60001:27017"
    networks:
      - backend

  # RabbitMQ
  rabbitmq:
    image: rabbitmq:3-management
    container_name: rabbitmq
    command: ["bash","-lc","rabbitmq-plugins enable --offline rabbitmq_prometheus && docker-entrypoint.sh rabbitmq-server"]
    ports:
      - "5672:5672"      # AMQP port
      - "15672:15672"    # Management UI
      - "15692:15692"    # Prometheus metrics
    environment:
      - RABBITMQ_DEFAULT_USER=dimatest
      - RABBITMQ_DEFAULT_PASS=testDima34
      - RABBITMQ_DEFAULT_VHOST=myhost
    networks:
      - backend

  # OpenTelemetry Collector
  otel-collector:
    image: otel/opentelemetry-collector-contrib:latest
    container_name: otel-collector
    command: ["--config=/etc/otel-collector-config.yaml"]
    volumes:
      - ../Scripts/otel-collector/otel-collector-config.yaml:/etc/otel-collector-config.yaml
    ports:
      - "8888:8888"   # Prometheus metrics
      - "8889:8889"   # Prometheus exporter metrics
      - "13133:13133" # Health check
      - "4317:4317"   # OTLP gRPC receiver
      - "4318:4318"   # OTLP HTTP receiver
      - "55679:55679" # zpages extension
    depends_on:
      - prometheus
      - jaeger
      - elasticsearch
    networks:
      - backend

  # Jaeger (Tracing)
  jaeger:
    image: jaegertracing/jaeger:2.10.0
    container_name: "jaeger"
    environment:
      - COLLECTOR_OTLP_ENABLED=true
    ports:
      - "16686:16686"   # Jaeger UI
      - "14250:14250"   # gRPC
      - "14268:14268"   # HTTP collector
    networks:
      - backend

  # Prometheus (Metrics)
  prometheus:
    container_name: prometheus
    build:
      context: ../Scripts/prometheus
    ports:
      - "9091:9090"
    networks:
      - backend

  # Grafana (Visualization)
  grafana:
    container_name: grafana
    build:
      context: ../Scripts/grafana
    environment:
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
      - GF_AUTH_DISABLE_LOGIN_FORM=true
    depends_on:
      - prometheus
      - otel-collector
      - jaeger
      - elasticsearch
    ports:
      - 3000:3000
    networks:
      - backend

  # Elasticsearch (Logs)
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.16.1
    container_name: elasticsearch
    hostname: elasticsearch
    environment:
      - discovery.type=single-node
      - bootstrap.memory_lock=true
      - xpack.security.enabled=false
    ports:
      - "9200:9200"
    networks:
      - backend
    volumes:
      - esdata:/usr/share/elasticsearch/data

  # Kibana (Log Analysis)
  kibana:
    image: docker.elastic.co/kibana/kibana:8.16.1
    container_name: kibana
    hostname: kibana
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
      - XPACK_SECURITY_ENABLED=false
    depends_on:
      - elasticsearch
    networks:
      - backend
    volumes:
      - esconfig:/usr/share/elasticsearch/config

networks:
  backend:
    driver: bridge
    name: connectsoft.microservicetemplate.application-network

volumes:
  esdata:
    driver: local
  esconfig:
    driver: local
  httpscerts: {}

docker-compose.override.yml

Environment-specific overrides for development:

version: '3.8'

services:
  connectsoft.microservicetemplate.application:
    environment:
      - ASPNETCORE_ENVIRONMENT=Docker
      - ASPNETCORE_URLS=http://+:8081;https://+:7279
      - ASPNETCORE_Kestrel__Certificates__Default__Password=123456
      - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx
    # Wait until the cert is present, then start the app
    entrypoint:
      - /bin/sh
      - -lc
      - |
        until [ -f /https/aspnetapp.pfx ]; do
          echo "waiting for /https/aspnetapp.pfx...";
          sleep 0.5;
        done;
        exec dotnet ConnectSoft.MicroserviceTemplate.Application.dll
    ports:
      - "8081:8081"  # HTTP
      - "7279:7279"  # HTTPS
    healthcheck:
      test: ["CMD-SHELL", "curl -f https://127.0.0.1:7279/alive || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 20s
    depends_on:
      - certgen
    volumes:
      - httpscerts:/https:ro

Key Features: - Environment Variables: Sets ASPNETCORE_ENVIRONMENT=Docker - Port Mapping: Exposes HTTP (8081) and HTTPS (7279) ports - Certificate Wait: Waits for certificate to be generated before starting - Health Check: Configures container health check using /alive endpoint - Volume Mount: Mounts certificate volume as read-only

.dockerignore

The .dockerignore file excludes unnecessary files from the Docker build context:

**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

Benefits: - Faster Builds: Smaller build context transferred to Docker daemon - Security: Prevents sensitive files (.env, secrets.dev.yaml) from being included - Smaller Images: Build artifacts (bin/, obj/) excluded

Container Configuration

Environment Detection

The application detects containerized environments via:

  1. Environment Variable: ASPNETCORE_ENVIRONMENT=Docker
  2. Container Detection: DOTNET_RUNNING_IN_CONTAINER (automatically set by .NET)
  3. Configuration File: appsettings.Docker.json loaded when environment is Docker

appsettings.Docker.json

Container-specific configuration:

{
  "Microservice": {
    "MicroserviceName": "ConnectSoft.MicroserviceTemplate",
    "StartupWarmupSeconds": 20
  },
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://+:8081"
      },
      "Https": {
        "Url": "https://+:7279",
        "Certificate": {
          "Path": "/https/aspnetapp.pfx",
          "Password": "123456"
        }
      }
    }
  },
  "ConnectionStrings": {
    "ConnectSoft.MicroserviceTemplateSqlServer": "Server=sql,1433;Database=MICROSERVICE_DATABASE;User Id=sa;Password=ConnectSoft123!;MultipleActiveResultSets=true;Encrypt=false;TrustServerCertificate=true;",
    "ConnectSoft.MicroserviceTemplateMongoDb": "mongodb://mongo:27017?uuidRepresentation=standard",
    "ConnectSoft.MicroserviceTemplateRedisCache": "redis:6379,ConnectRetry=3,KeepAlive=180,name=ConnectSoft.MicroserviceTemplateRedisCache"
  },
  "Serilog": {
    "WriteTo": [
      {
        "Name": "Seq",
        "Args": {
          "serverUrl": "http://host.docker.internal:5341"
        }
      },
      {
        "Name": "OpenTelemetry",
        "Args": {
          "endpoint": "http://otel-collector:4317/"
        }
      }
    ]
  }
}

Key Container-Specific Settings: - Service Names: Uses container names (e.g., sql, redis, mongo) instead of localhost - Certificate Path: Points to mounted certificate volume (/https/aspnetapp.pfx) - Logging: Uses container names for observability services (otel-collector, host.docker.internal)

HTTPS Certificates

Certificate Generation

Certificates are generated in a separate container and shared via volume:

certgen:
  image: mcr.microsoft.com/dotnet/sdk:8.0
  container_name: certgen
  entrypoint: ["bash","-lc","dotnet dev-certs https -ep /out/aspnetapp.pfx -p 123456"]
  volumes:
    - httpscerts:/out
  restart: "no"

Process: 1. certgen container generates development HTTPS certificate 2. Certificate saved to httpscerts volume at /out/aspnetapp.pfx 3. Application container mounts volume as read-only at /https

Certificate Mounting

connectsoft.microservicetemplate.application:
  volumes:
    - httpscerts:/https:ro
  entrypoint:
    - /bin/sh
    - -lc
    - |
      until [ -f /https/aspnetapp.pfx ]; do
        echo "waiting for /https/aspnetapp.pfx...";
        sleep 0.5;
      done;
      exec dotnet ConnectSoft.MicroserviceTemplate.Application.dll

Features: - Read-Only Mount: Certificate volume mounted as read-only (:ro) - Wait Logic: Application waits for certificate before starting - Kestrel Configuration: Certificate path configured in appsettings.Docker.json

Kestrel Certificate Configuration

{
  "Kestrel": {
    "Endpoints": {
      "Https": {
        "Url": "https://+:7279",
        "Certificate": {
          "Path": "/https/aspnetapp.pfx",
          "Password": "123456"
        }
      }
    }
  }
}

Health Checks

Container Health Check

Docker Compose health check configuration:

healthcheck:
  test: ["CMD-SHELL", "curl -f https://127.0.0.1:7279/alive || exit 1"]
  interval: 10s
  timeout: 5s
  retries: 3
  start_period: 20s

Configuration: - Test: Uses curl to check /alive endpoint (liveness probe) - Interval: Check every 10 seconds - Timeout: 5 seconds per check - Retries: 3 consecutive failures mark container unhealthy - Start Period: 20 seconds grace period before health checks start

Health Check Endpoints

The application exposes health check endpoints:

  • /health: Overall health check (readiness probe)
  • /live: Liveness probe (container is running)
  • /ready: Readiness probe (application is ready to accept traffic)

See Health Checks for detailed documentation.

Networking

Docker Network

All services run on a shared bridge network:

networks:
  backend:
    driver: bridge
    name: connectsoft.microservicetemplate.application-network

Benefits: - Service Discovery: Containers can communicate using service names - Isolation: Services isolated from other Docker networks - DNS Resolution: Automatic DNS resolution for service names

Service Communication

Containers communicate using service names:

ConnectionStrings:
  ConnectSoft.MicroserviceTemplateSqlServer: "Server=sql,1433;..."
  ConnectSoft.MicroserviceTemplateMongoDb: "mongodb://mongo:27017..."
  ConnectSoft.MicroserviceTemplateRedisCache: "redis:6379,..."

Service Name Resolution: - sql → SQL Server container - mongo → MongoDB container - redis → Redis container - rabbitmq → RabbitMQ container - otel-collector → OpenTelemetry Collector container

Volumes

Persistent Volumes

volumes:
  esdata:
    driver: local
  esconfig:
    driver: local
  httpscerts: {}

Volume Types: - Named Volumes: esdata, esconfig for persistent data - Anonymous Volumes: httpscerts for temporary certificate storage

Use Cases: - Elasticsearch Data: Persistent storage for Elasticsearch indices - Certificates: Shared certificate storage between containers - Configuration: Shared configuration files (if needed)

Building and Running

Building the Image

# Build from Dockerfile
docker build -f ConnectSoft.MicroserviceTemplate.Application/Dockerfile -t connectsoft.microservicetemplate.application .

# Build using Docker Compose
docker-compose build

# Build without cache
docker-compose build --no-cache

Running with Docker Compose

# Start all services
docker-compose up

# Start in detached mode
docker-compose up -d

# Start specific services
docker-compose up sql redis mongo

# Stop all services
docker-compose down

# Stop and remove volumes
docker-compose down -v

Viewing Logs

# View all logs
docker-compose logs

# View logs for specific service
docker-compose logs connectsoft.microservicetemplate.application

# Follow logs
docker-compose logs -f

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

Container Management

# List running containers
docker-compose ps

# Execute command in container
docker-compose exec connectsoft.microservicetemplate.application bash

# Inspect container
docker inspect connectsoft.microservicetemplate.application

# View container logs
docker logs connectsoft.microservicetemplate.application

# Stop container
docker-compose stop connectsoft.microservicetemplate.application

# Restart container
docker-compose restart connectsoft.microservicetemplate.application

Best Practices

Do's

  1. Use Multi-Stage Builds

    # ✅ GOOD - Multi-stage build
    FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
    # ... build steps ...
    FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final
    COPY --from=build /app/publish .
    

  2. Pin Base Image Versions

    # ✅ GOOD - Pinned SHA256
    FROM mcr.microsoft.com/dotnet/aspnet:9.0@sha256:b4bea3a52a0a77317fa93c5bbdb076623f81e3e2f201078d89914da71318b5d8
    

  3. Optimize Layer Caching

    # ✅ GOOD - Copy dependency files first
    COPY ["Directory.Packages.props", "."]
    COPY ["*.csproj", "./"]
    RUN dotnet restore
    COPY [".", "."]
    RUN dotnet build
    

  4. Use .dockerignore

    # ✅ GOOD - Exclude build artifacts
    **/bin
    **/obj
    **/.git
    

  5. Configure Health Checks

    # ✅ GOOD - Health check configured
    healthcheck:
      test: ["CMD-SHELL", "curl -f https://127.0.0.1:7279/alive || exit 1"]
      interval: 10s
    

  6. Use Named Networks

    # ✅ GOOD - Named network for service discovery
    networks:
      - backend
    

Don'ts

  1. Don't Include Secrets in Images

    # ❌ BAD - Secrets in image
    ENV DB_PASSWORD=SecretPassword123
    
    # ✅ GOOD - Use environment variables or secrets
    # Pass via docker-compose or Kubernetes secrets
    

  2. Don't Use Latest Tags in Production

    # ❌ BAD - Latest tag
    FROM mcr.microsoft.com/dotnet/aspnet:latest
    
    # ✅ GOOD - Specific version
    FROM mcr.microsoft.com/dotnet/aspnet:9.0@sha256:...
    

  3. Don't Run as Root

    # ❌ BAD - Running as root
    # (Default in many base images)
    
    # ✅ GOOD - Create non-root user
    RUN adduser -D -g '' appuser
    USER appuser
    

  4. Don't Expose Unnecessary Ports

    # ❌ BAD - Exposing debug ports
    ports:
      - "8081:8081"
      - "5000:5000"  # Unnecessary
    
    # ✅ GOOD - Only expose required ports
    ports:
      - "8081:8081"
      - "7279:7279"
    

  5. Don't Copy Everything

    # ❌ BAD - Copying entire directory
    COPY . .
    
    # ✅ GOOD - Copy specific files
    COPY ["Directory.Packages.props", "."]
    COPY ["*.csproj", "./"]
    

Troubleshooting

Issue: Container Fails to Start

Symptom: Container exits immediately after starting.

Solutions: 1. Check container logs: docker logs <container-name> 2. Verify environment variables: docker inspect <container-name> 3. Check health check: docker inspect <container-name> | grep -A 10 Health 4. Verify dependencies: Ensure dependent services are running

Issue: Cannot Connect to Database

Symptom: Application cannot connect to SQL Server/MongoDB/Redis.

Solutions: 1. Verify service names match connection strings 2. Check network connectivity: docker-compose exec <service> ping <target-service> 3. Verify ports are exposed: docker-compose ps 4. Check service logs: docker-compose logs <service>

Issue: Certificate Not Found

Symptom: HTTPS endpoint fails with certificate error.

Solutions: 1. Verify certificate generation: docker-compose logs certgen 2. Check volume mount: docker inspect <container> | grep -A 5 Mounts 3. Verify certificate path in appsettings.Docker.json 4. Check wait logic in entrypoint script

Issue: Build Takes Too Long

Symptom: Docker build is slow.

Solutions: 1. Use .dockerignore to exclude unnecessary files 2. Optimize layer caching (copy dependency files first) 3. Use BuildKit: DOCKER_BUILDKIT=1 docker build ... 4. Use multi-stage builds to reduce final image size

Issue: Out of Memory

Symptom: Containers are killed or fail with memory errors.

Solutions: 1. Set memory limits: docker-compose.ymldeploy.resources.limits.memory 2. Optimize image size (use multi-stage builds) 3. Check service memory usage: docker stats 4. Adjust service memory limits individually

Security Considerations

Image Security

  1. Base Image Scanning: Regularly scan base images for vulnerabilities
  2. Minimal Base Images: Use minimal runtime images (not SDK)
  3. No Secrets in Images: Never include secrets, passwords, or API keys
  4. Regular Updates: Keep base images updated with security patches

Container Security

  1. Read-Only Volumes: Mount volumes as read-only when possible (:ro)
  2. Non-Root User: Run containers as non-root user (if base image supports)
  3. Resource Limits: Set CPU and memory limits
  4. Network Isolation: Use Docker networks to isolate services

Secrets Management

  1. Environment Variables: Use Docker secrets or environment variables
  2. Secret Managers: Integrate with Azure Key Vault, HashiCorp Vault
  3. Never Commit Secrets: Secrets should never be in source control
  4. Rotation: Regularly rotate secrets and certificates

Production Deployment

Image Tagging

Tag images with version numbers:

docker build -t connectsoft.microservicetemplate.application:1.0.0 .
docker tag connectsoft.microservicetemplate.application:1.0.0 \
    registry.example.com/connectsoft.microservicetemplate.application:1.0.0

Container Registry

Push images to container registry:

docker push registry.example.com/connectsoft.microservicetemplate.application:1.0.0

Kubernetes Deployment

For Kubernetes deployments, see Kubernetes documentation. The containerized application is ready for Kubernetes deployment.

Summary

The ConnectSoft Microservice Template provides:

  • Multi-Stage Dockerfile: Optimized builds with minimal final image size
  • Docker Compose Orchestration: Complete application stack with dependencies
  • Health Checks: Container health monitoring
  • HTTPS Support: Certificate generation and mounting
  • Service Discovery: Docker network-based service communication
  • Environment Configuration: Container-specific settings via appsettings.Docker.json
  • Security Best Practices: Minimal images, no secrets, network isolation
  • Production Ready: Optimized for production deployments

By following these patterns, teams can:

  • Build Efficiently: Optimized layer caching and multi-stage builds
  • Deploy Consistently: Same containerized application across environments
  • Scale Easily: Container orchestration with Kubernetes or Docker Swarm
  • Monitor Effectively: Health checks and observability integration
  • Secure Properly: Security best practices and secrets management

The containerization strategy enables teams to build, ship, and run microservices consistently across any environment that supports Docker.