Skip to main content

Overview

RealtimeChat includes full Docker support with a multi-stage Dockerfile for optimized production builds and a docker-compose configuration for easy deployment. This guide walks you through deploying the application using Docker.

Dockerfile Architecture

The application uses a multi-stage Dockerfile based on .NET 9.0 that optimizes for both build time and image size:
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
USER $APP_UID
WORKDIR /app
EXPOSE 8080
EXPOSE 8081

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["RealtimeChat.API/RealtimeChat.API.csproj", "RealtimeChat.API/"]
RUN dotnet restore "RealtimeChat.API/RealtimeChat.API.csproj"
COPY . .
WORKDIR "/src/RealtimeChat.API"
RUN dotnet build "RealtimeChat.API.csproj" -c $BUILD_CONFIGURATION -o /app/build

FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "RealtimeChat.API.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINTRY ["dotnet", "RealtimeChat.API.dll"]

Dockerfile Stages

Uses the .NET 9.0 ASP.NET runtime image for minimal size. Exposes ports 8080 (HTTP) and 8081 (HTTPS).
Uses the .NET SDK to restore dependencies and build the application. Supports configurable build configuration via BUILD_CONFIGURATION argument.
Creates an optimized release build with framework-dependent deployment (no AppHost).
Copies the published application from the publish stage into the minimal runtime image.

Docker Compose Configuration

The docker-compose.yml file provides a simple deployment configuration:
services:
  realtimechat:
    build:
      context: .
      dockerfile: RealtimeChat.API/Dockerfile
    image: localhost:5000/realtimechat:latest
    environment:
      - ASPNETCORE_ENVIRONMENT=Docker
    extra_hosts:
      - "host.docker.internal:host-gateway"
    restart: unless-stopped
The extra_hosts configuration allows the container to access services running on the host machine (like a PostgreSQL database) via host.docker.internal.

Step-by-Step Deployment

1

Prepare the Environment

Ensure you have Docker and Docker Compose installed on your system. Verify PostgreSQL is running and accessible.
docker --version
docker-compose --version
2

Configure Database Connection

The Docker environment uses appsettings.Docker.json which connects to PostgreSQL on the host:
{
  "ConnectionStrings": {
    "DefaultConnectionString": "Server=host.docker.internal;Port=5432;Username=postgres;Password=postgres;Database=realtime_chat_db;Include Error Detail=true;"
  }
}
Update the connection string if your PostgreSQL instance uses different credentials.
3

Build the Docker Image

Build the Docker image using docker-compose:
docker-compose build
Or build manually:
docker build -t realtimechat:latest -f Applications/RealtimeChat.API/Dockerfile .
4

Run Database Migrations

Before starting the application, ensure your database schema is up to date:
dotnet ef database update --project Infrastructure/RealtimeChat.Infrastructure.DB
See the Database Setup guide for detailed migration instructions.
5

Start the Application

Launch the application using docker-compose:
docker-compose up -d
The -d flag runs the container in detached mode.
6

Verify Deployment

Check that the container is running:
docker-compose ps
View logs:
docker-compose logs -f realtimechat

Environment Variables

The application can be configured using environment variables in the docker-compose file:
VariableDescriptionDefault
ASPNETCORE_ENVIRONMENTApplication environment (Docker, Production)Docker
ConnectionStrings__DefaultConnectionStringDatabase connection stringSee appsettings
AllowCorsAddressAllowed CORS originhttp://localhost:5173
services:
  realtimechat:
    build:
      context: .
      dockerfile: RealtimeChat.API/Dockerfile
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ConnectionStrings__DefaultConnectionString=Server=db;Port=5432;Database=realtime_chat_db;Username=admin;Password=secure_password
      - AllowCorsAddress=https://your-frontend.com
    ports:
      - "8080:8080"
    restart: unless-stopped

Networking and Ports

Exposed Ports

  • 8080: HTTP endpoint
  • 8081: HTTPS endpoint (requires certificate configuration)

Accessing the Host Network

The Docker configuration includes host.docker.internal mapping, which allows the container to connect to services on the host machine:
extra_hosts:
  - "host.docker.internal:host-gateway"
This is particularly useful for development when PostgreSQL runs on the host machine.
In production, consider running PostgreSQL in a separate container and using Docker networks for communication instead of host.docker.internal.

Production Considerations

Multi-Container Setup

For production deployments, run PostgreSQL in a separate container:
version: '3.8'

services:
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: realtime_chat_db
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: always

  realtimechat:
    build:
      context: .
      dockerfile: RealtimeChat.API/Dockerfile
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ConnectionStrings__DefaultConnectionString=Server=db;Port=5432;Database=realtime_chat_db;Username=admin;Password=${DB_PASSWORD}
    depends_on:
      - db
    ports:
      - "8080:8080"
    restart: always

volumes:
  postgres_data:

Security Best Practices

1

Use Secrets

Never hardcode passwords in docker-compose files. Use environment variables or Docker secrets:
export DB_PASSWORD=your_secure_password
docker-compose up -d
2

Enable HTTPS

Configure HTTPS certificates for production:
volumes:
  - ./certs:/https:ro
environment:
  - ASPNETCORE_URLS=https://+:8081;http://+:8080
  - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/cert.pfx
  - ASPNETCORE_Kestrel__Certificates__Default__Password=${CERT_PASSWORD}
3

Limit Resources

Set resource limits to prevent container resource exhaustion:
deploy:
  resources:
    limits:
      cpus: '2'
      memory: 2G
    reservations:
      cpus: '1'
      memory: 512M
4

Run as Non-Root

The Dockerfile already uses a non-root user ($APP_UID), but verify this in production.

Health Checks

Add health checks to monitor container status:
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 40s

Logging

Configure logging drivers for centralized log management:
logging:
  driver: "json-file"
  options:
    max-size: "10m"
    max-file: "3"

Troubleshooting

Check logs for errors:
docker-compose logs realtimechat
Common issues:
  • Database connection string is incorrect
  • PostgreSQL is not accessible
  • Migrations have not been run
Verify network connectivity:
docker-compose exec realtimechat ping host.docker.internal
Test database connection from the container:
docker-compose exec realtimechat dotnet ef database update
If port 8080 is already in use, remap the port in docker-compose.yml:
ports:
  - "9090:8080"

Next Steps

Build docs developers (and LLMs) love