9.9 KiB
Docker guide
This document covers Docker deployment, configuration, and troubleshooting for The Biergarten App.
Overview
The project uses Docker Compose to orchestrate multiple services:
- SQL Server 2022 database
- Database migrations runner (DbUp)
- Database seeder
- .NET API
- React Router website (SSR frontend)
- Test runners
See the deployment diagram for visual representation.
Docker Compose environments
1. Development (docker-compose.dev.yaml)
Purpose: Local development with persistent data
Features:
- Persistent SQL Server volume
- NuGet package cache volume (speeds up rebuilds)
- Swagger UI enabled
- Seed data included
- Local Mailpit SMTP server for dev email testing
- Local SeaweedFS S3-compatible object storage for photo uploads
CLEAR_DATABASE=true(drops and recreates schema)
Services:
sqlserver # SQL Server 2022 (port 1434)
database.migrations # DbUp migrations
database.seed # Seed initial data
api.core # Web API (ports 8080, 8081)
frontend # React Router website (port 3000)
mailpit # Local dev SMTP server + UI (port 8025)
seaweedfs # S3-compatible object storage (ports 8333, 8888, 9333, 23646)
Start Development Environment:
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml up -d
Access:
- Website: http://localhost:3000
- API Swagger: http://localhost:8080/swagger
- Health Check: http://localhost:8080/health
- Mailpit UI: http://localhost:8025
- SQL Server: localhost:1434 (sa credentials from .env.dev)
- SeaweedFS S3 API: http://localhost:8333 (credentials from
SEAWEEDFS_ACCESS_KEY_ID/SEAWEEDFS_SECRET_ACCESS_KEYin .env.dev) - SeaweedFS Filer UI: http://localhost:8888
- SeaweedFS Master UI: http://localhost:9333
Stop Environment:
# Stop services (keep volumes)
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml down
# Stop and remove volumes (fresh start)
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml down -v
2. Testing (docker-compose.test.yaml)
Purpose: Automated CI/CD testing in an isolated environment
Features:
- Fresh database each run
- All test suites execute in parallel
- Test results exported to
./test-results/ - Containers auto-exit after completion
- Fully isolated testnet network
Services:
sqlserver # Test database
database.migrations # Fresh schema
database.seed # Test data
api.specs # Reqnroll BDD tests
unit.tests # All Features.*.Tests unit test projects
frontend.tests # Storybook Vitest + Playwright suites (own Playwright image)
Run Tests:
# Run all tests
docker compose --env-file web/.env.test -f web/docker-compose.test.yaml up --abort-on-container-exit
# View results
ls -la test-results/
cat test-results/api-specs/results.trx
cat test-results/Features.Users.Tests.trx
# Clean up
docker compose --env-file web/.env.test -f web/docker-compose.test.yaml down -v
3. Production (docker-compose.prod.yaml)
Purpose: Production-ready deployment
Features:
- Production logging levels
- No database clearing
- Optimized build configurations
- Health checks enabled
- Restart policies (unless-stopped)
- Security hardening
- Only
frontendpublishes a port to the host;sqlserverandapi.coreare reachable only from other containers onprodnet
Services:
sqlserver # Production SQL Server (internal only)
database.migrations # Schema updates only
api.core # Production API (internal only, no published ports)
frontend # Production website (port 3000, the only public binding)
Deploy Production:
docker compose --env-file web/.env.prod -f web/docker-compose.prod.yaml up -d
4. Database only (docker-compose.db.yaml)
Purpose: Run just the database for local backend development outside Docker
(for example, dotnet run against a containerized SQL Server)
Services:
sqlserver # SQL Server 2022 (port 1434)
database.migrations # DbUp migrations
database.seed # Seed initial data
No api.core or mailpit container. Uses .env.dev, same as development.
Start:
docker compose --env-file web/.env.dev -f web/docker-compose.db.yaml up -d
5. Minimal (docker-compose.min.yaml)
Purpose: Local dev where the API runs outside Docker but the database and mail capture still run in containers
Services:
sqlserver # SQL Server 2022 (port 1433)
database.migrations # DbUp migrations
database.seed # Seed initial data
mailpit # Local dev SMTP server + UI (port 8025)
No api.core container. Uses .env.local instead of .env.dev, and exposes
SQL Server on the standard port 1433 (docker-compose.db.yaml and
docker-compose.dev.yaml both use 1434).
Start:
docker compose --env-file web/.env.local -f web/docker-compose.min.yaml up -d
Service dependencies
Docker Compose manages startup order using health checks:
graph TD
A[sqlserver: health check passes] --> B[database.migrations: completes successfully]
B --> C[database.seed: completes successfully]
C --> D[api.core / tests: start when ready]
E[seaweedfs: container started] --> D
In development, api.core also depends on seaweedfs (condition: service_started, not a health check) alongside database.seed.
Health Check Example (SQL Server):
healthcheck:
test:
[
"CMD-SHELL",
"sqlcmd -S localhost -U sa -P '${DB_PASSWORD}' -C -Q 'SELECT 1'",
]
interval: 10s
timeout: 5s
retries: 12
start_period: 30s
Dependency Configuration:
api.core:
depends_on:
database.seed:
condition: service_completed_successfully
Volumes
Persistent volumes
Development:
sqlserverdata-dev- Database files persist between restartsnuget-cache-dev- NuGet package cache (speeds up builds)seaweedfsdata-dev- SeaweedFS object storage data persists between restarts
Testing:
sqlserverdata-test- Temporary, typically removed after tests
Production:
sqlserverdata-prod- Production database filesnuget-cache-prod- Production NuGet cache
Mounted volumes
Test Results:
volumes:
- ./test-results:/app/test-results
Test results are written to host filesystem for CI/CD integration.
Networks
Each environment uses isolated bridge networks:
devnet- Development networktestnet- Testing network (fully isolated)prodnet- Production network
Environment variables
All containers are configured via environment variables from .env files:
env_file: ".env.dev" # or .env.test, .env.prod
environment:
ASPNETCORE_ENVIRONMENT: "Development"
DOTNET_RUNNING_IN_CONTAINER: "true"
DB_SERVER: "${DB_SERVER}"
DB_NAME: "${DB_NAME}"
DB_USER: "${DB_USER}"
DB_PASSWORD: "${DB_PASSWORD}"
ACCESS_TOKEN_SECRET: "${ACCESS_TOKEN_SECRET}"
REFRESH_TOKEN_SECRET: "${REFRESH_TOKEN_SECRET}"
CONFIRMATION_TOKEN_SECRET: "${CONFIRMATION_TOKEN_SECRET}"
SEAWEEDFS_SERVICE_URL: "${SEAWEEDFS_SERVICE_URL}"
SEAWEEDFS_ACCESS_KEY_ID: "${SEAWEEDFS_ACCESS_KEY_ID}"
SEAWEEDFS_SECRET_ACCESS_KEY: "${SEAWEEDFS_SECRET_ACCESS_KEY}"
SEAWEEDFS_BUCKET: "${SEAWEEDFS_BUCKET}"
For complete list, see Environment Variables.
Common commands
View services
# Running services
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml ps
# All containers (including stopped)
docker ps -a
View logs
# All services
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml logs -f
# Specific service
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml logs -f api.core
# Last 100 lines
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml logs --tail=100 api.core
Execute commands in container
# Interactive shell
docker exec -it dev-env-api-core bash
# Run command (replace <db-password> with the value of DB_PASSWORD from .env.dev)
docker exec dev-env-sqlserver /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P '<db-password>' -C
Restart services
# Restart all services
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml restart
# Restart specific service
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml restart api.core
# Rebuild and restart
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml up -d --build api.core
Build images
# Build all images
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml build
# Build specific service
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml build api.core
# Build without cache
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml build --no-cache
Clean up
# Stop and remove containers
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml down
# Remove containers and volumes
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml down -v
# Remove containers, volumes, and images
docker compose --env-file web/.env.dev -f web/docker-compose.dev.yaml down -v --rmi all
# System-wide cleanup
docker system prune -af --volumes
Dockerfile structure
Multi-stage build
# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY ["Project/Project.csproj", "Project/"]
RUN dotnet restore
COPY . .
RUN dotnet build -c Release
# Stage 2: Runtime
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
WORKDIR /app
COPY --from=build /app/build .
ENTRYPOINT ["dotnet", "Project.dll"]