📋 Task Description
Create production-ready Dockerfiles for StarGate.Server (API + Worker) with multi-stage builds, optimized image sizes, security best practices, and health check configurations. Ensure containers are efficient, secure, and ready for production deployment.
🎯 Objectives
- Create multi-stage Dockerfile for StarGate.Server
- Optimize image size with Alpine base
- Implement security best practices (non-root user, minimal surface)
- Configure health checks for container orchestration
- Add proper labeling and metadata
- Optimize layer caching for faster builds
- Include only necessary runtime dependencies
- Configure proper logging to stdout/stderr
- Document Docker build and run procedures
- Test container startup and health checks
- Measure and document image sizes
- Create .dockerignore for build optimization
📦 Deliverables
1. Create .dockerignore
Create .dockerignore:
# Build artifacts
**/bin/
**/obj/
**/out/
# IDE files
.vs/
.vscode/
.idea/
*.user
*.suo
# Test results
**/TestResults/
**/coverage/
**/results/
# OS files
.DS_Store
Thumbs.db
# Git
.git/
.gitignore
.gitattributes
# Documentation
*.md
docs/
# Docker
Dockerfile*
docker-compose*
.dockerignore
# CI/CD
.github/
# Logs
*.log
logs/
# Temporary files
*.tmp
*.temp
2. Create Dockerfile for StarGate.Server
Create src/StarGate.Server/Dockerfile:
# Multi-stage build for StarGate.Server
# This container runs both the API (HTTP endpoints) and the ProcessWorker (background consumer)
# ============================================
# Stage 1: Build
# ============================================
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
WORKDIR /src
# Copy solution and project files first (better layer caching)
COPY ["StarGate.sln", "./"]
COPY ["src/StarGate.Core/StarGate.Core.csproj", "src/StarGate.Core/"]
COPY ["src/StarGate.Infrastructure/StarGate.Infrastructure.csproj", "src/StarGate.Infrastructure/"]
COPY ["src/StarGate.Server/StarGate.Server.csproj", "src/StarGate.Server/"]
# Restore dependencies
RUN dotnet restore "src/StarGate.Server/StarGate.Server.csproj" \
--runtime linux-musl-x64
# Copy source code
COPY src/ ./src/
# Build application
WORKDIR /src/src/StarGate.Server
RUN dotnet build "StarGate.Server.csproj" \
-c Release \
--runtime linux-musl-x64 \
--self-contained false \
--no-restore
# ============================================
# Stage 2: Publish
# ============================================
FROM build AS publish
RUN dotnet publish "StarGate.Server.csproj" \
-c Release \
--runtime linux-musl-x64 \
--self-contained false \
--no-build \
-o /app/publish \
/p:PublishTrimmed=false \
/p:PublishSingleFile=false
# ============================================
# Stage 3: Runtime
# ============================================
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS runtime
# Install dependencies for health checks
RUN apk add --no-cache curl
# Create non-root user
RUN addgroup -g 1000 stargate && \
adduser -u 1000 -G stargate -s /bin/sh -D stargate
# Set working directory
WORKDIR /app
# Copy published application
COPY --from=publish --chown=stargate:stargate /app/publish .
# Create logs directory
RUN mkdir -p /app/logs && chown stargate:stargate /app/logs
# Switch to non-root user
USER stargate
# Expose ports
EXPOSE 8080
EXPOSE 8081
# Environment variables (can be overridden)
ENV ASPNETCORE_URLS=http://+:8080 \
ASPNETCORE_ENVIRONMENT=Production \
DOTNET_RUNNING_IN_CONTAINER=true \
DOTNET_EnableDiagnostics=0
# Health check configuration
# Checks the /health endpoint every 30 seconds
# Container is unhealthy after 3 consecutive failures
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# Labels for metadata
LABEL maintainer="StarGate Team" \
version="1.0" \
description="StarGate Process Orchestration Server - API + Worker" \
org.opencontainers.image.source="https://github.com/artcava/StarGate" \
org.opencontainers.image.description="Async process orchestration platform" \
org.opencontainers.image.licenses="MIT"
# Entry point
ENTRYPOINT ["dotnet", "StarGate.Server.dll"]
3. Create Optimized Dockerfile Variant
Create src/StarGate.Server/Dockerfile.optimized:
# Ultra-optimized variant using self-contained deployment
# Results in slightly larger image but no .NET runtime dependency
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
WORKDIR /src
COPY ["StarGate.sln", "./"]
COPY ["src/StarGate.Core/StarGate.Core.csproj", "src/StarGate.Core/"]
COPY ["src/StarGate.Infrastructure/StarGate.Infrastructure.csproj", "src/StarGate.Infrastructure/"]
COPY ["src/StarGate.Server/StarGate.Server.csproj", "src/StarGate.Server/"]
RUN dotnet restore "src/StarGate.Server/StarGate.Server.csproj" \
--runtime linux-musl-x64
COPY src/ ./src/
WORKDIR /src/src/StarGate.Server
# Self-contained publish with trimming
RUN dotnet publish "StarGate.Server.csproj" \
-c Release \
--runtime linux-musl-x64 \
--self-contained true \
-o /app/publish \
/p:PublishTrimmed=true \
/p:PublishSingleFile=true \
/p:EnableCompressionInSingleFile=true \
/p:DebugType=None \
/p:DebugSymbols=false
# Ultra-minimal runtime (no .NET runtime needed)
FROM alpine:3.19 AS runtime
RUN apk add --no-cache \
curl \
icu-libs \
libgcc \
libstdc++ \
zlib
RUN addgroup -g 1000 stargate && \
adduser -u 1000 -G stargate -s /bin/sh -D stargate
WORKDIR /app
COPY --from=build --chown=stargate:stargate /app/publish .
RUN mkdir -p /app/logs && chown stargate:stargate /app/logs
USER stargate
EXPOSE 8080
EXPOSE 8081
ENV ASPNETCORE_URLS=http://+:8080 \
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
LABEL maintainer="StarGate Team" \
version="1.0-optimized" \
description="StarGate Server - Optimized Self-Contained"
ENTRYPOINT ["./StarGate.Server"]
4. Create Build Script
Create scripts/build-docker.sh:
#!/bin/bash
# Script to build Docker images for StarGate
set -e
DOCKER_REGISTRY="${DOCKER_REGISTRY:-stargate}"
VERSION="${VERSION:-latest}"
BUILD_TYPE="${BUILD_TYPE:-standard}" # standard or optimized
echo "============================================"
echo "Building StarGate Docker Images"
echo "============================================"
echo "Registry: $DOCKER_REGISTRY"
echo "Version: $VERSION"
echo "Build Type: $BUILD_TYPE"
echo "============================================"
# Determine Dockerfile to use
if [ "$BUILD_TYPE" = "optimized" ]; then
DOCKERFILE="src/StarGate.Server/Dockerfile.optimized"
else
DOCKERFILE="src/StarGate.Server/Dockerfile"
fi
echo "Using Dockerfile: $DOCKERFILE"
echo ""
# Build image
echo "Building image..."
docker build \
-f "$DOCKERFILE" \
-t "$DOCKER_REGISTRY/stargate-server:$VERSION" \
-t "$DOCKER_REGISTRY/stargate-server:latest" \
--build-arg BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
--build-arg VCS_REF="$(git rev-parse --short HEAD)" \
.
echo ""
echo "============================================"
echo "Build Complete!"
echo "============================================"
# Show image size
docker images | grep stargate-server | head -n 1
echo ""
echo "To run the container:"
echo " docker run -p 8080:8080 $DOCKER_REGISTRY/stargate-server:$VERSION"
echo ""
echo "To push to registry:"
echo " docker push $DOCKER_REGISTRY/stargate-server:$VERSION"
echo ""
Make executable:
chmod +x scripts/build-docker.sh
5. Create Docker Test Script
Create scripts/test-docker.sh:
#!/bin/bash
# Script to test Docker container
set -e
IMAGE="${1:-stargate/stargate-server:latest}"
CONTAINER_NAME="stargate-server-test"
echo "Testing Docker image: $IMAGE"
echo ""
# Cleanup existing container
docker rm -f $CONTAINER_NAME 2>/dev/null || true
# Start container
echo "Starting container..."
docker run -d \
--name $CONTAINER_NAME \
-p 8080:8080 \
-e ASPNETCORE_ENVIRONMENT=Development \
$IMAGE
echo "Waiting for container to be ready..."
sleep 5
# Check if container is running
if ! docker ps | grep -q $CONTAINER_NAME; then
echo "❌ Container failed to start"
docker logs $CONTAINER_NAME
docker rm -f $CONTAINER_NAME
exit 1
fi
echo "✓ Container is running"
echo ""
# Test health endpoint
echo "Testing health endpoint..."
for i in {1..10}; do
if curl -sf http://localhost:8080/health > /dev/null; then
echo "✓ Health check passed"
HEALTH_OK=true
break
fi
echo "Attempt $i/10 failed, waiting..."
sleep 2
done
if [ "$HEALTH_OK" != "true" ]; then
echo "❌ Health check failed"
docker logs $CONTAINER_NAME
docker rm -f $CONTAINER_NAME
exit 1
fi
echo ""
# Check Docker health status
echo "Checking Docker health status..."
sleep 35 # Wait for health check to run
HEALTH_STATUS=$(docker inspect --format='{{.State.Health.Status}}' $CONTAINER_NAME)
echo "Health status: $HEALTH_STATUS"
if [ "$HEALTH_STATUS" != "healthy" ]; then
echo "⚠️ Warning: Container not marked as healthy yet"
fi
echo ""
# Show container info
echo "Container information:"
docker stats --no-stream $CONTAINER_NAME
echo ""
echo "Container logs:"
docker logs --tail 20 $CONTAINER_NAME
echo ""
echo "============================================"
echo "Test Summary"
echo "============================================"
echo "✓ Container started successfully"
echo "✓ Health endpoint responding"
echo "✓ Container is $HEALTH_STATUS"
echo ""
echo "To view logs: docker logs -f $CONTAINER_NAME"
echo "To stop: docker rm -f $CONTAINER_NAME"
echo "============================================"
# Cleanup (optional)
read -p "Stop and remove container? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
docker rm -f $CONTAINER_NAME
echo "Container removed"
fi
Make executable:
chmod +x scripts/test-docker.sh
6. Create Size Comparison Script
Create scripts/compare-image-sizes.sh:
#!/bin/bash
# Compare image sizes between standard and optimized builds
echo "Building standard image..."
BUILD_TYPE=standard ./scripts/build-docker.sh > /dev/null 2>&1
echo "Building optimized image..."
BUILD_TYPE=optimized ./scripts/build-docker.sh > /dev/null 2>&1
echo ""
echo "============================================"
echo "Image Size Comparison"
echo "============================================"
docker images | grep stargate-server | awk '{printf "%-50s %10s\n", $1":"$2, $7" "$8}'
echo "============================================"
Make executable:
chmod +x scripts/compare-image-sizes.sh
7. Update Program.cs for Container Support
Update src/StarGate.Server/Program.cs:
// Configure Kestrel for container environment
builder.WebHost.ConfigureKestrel(options =>
{
// Listen on all interfaces (required for Docker)
options.ListenAnyIP(8080); // HTTP
// Optional: Add metrics endpoint on different port
options.ListenAnyIP(8081, listenOptions =>
{
// Metrics endpoint (can be used for Prometheus)
});
});
// Configure logging for container
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddJsonConsole(options =>
{
options.IncludeScopes = true;
options.TimestampFormat = "yyyy-MM-dd HH:mm:ss ";
options.JsonWriterOptions = new System.Text.Json.JsonWriterOptions
{
Indented = false
};
});
// Health checks endpoint
app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
context.Response.ContentType = "application/json";
var result = JsonSerializer.Serialize(new
{
status = report.Status.ToString(),
checks = report.Entries.Select(e => new
{
name = e.Key,
status = e.Value.Status.ToString(),
description = e.Value.Description,
duration = e.Value.Duration.TotalMilliseconds
}),
totalDuration = report.TotalDuration.TotalMilliseconds
});
await context.Response.WriteAsync(result);
}
});
// Liveness probe (simpler than full health check)
app.MapGet("/health/live", () => Results.Ok(new { status = "alive" }));
// Readiness probe (checks dependencies)
app.MapGet("/health/ready", async (IServiceProvider services) =>
{
// Check critical dependencies
try
{
var db = services.GetRequiredService<IMongoDatabase>();
await db.ListCollectionNamesAsync().FirstOrDefaultAsync();
return Results.Ok(new { status = "ready" });
}
catch
{
return Results.StatusCode(503); // Service Unavailable
}
});
8. Create Documentation
Create docs/DOCKER.md:
# Docker Deployment Guide
## Building Images
### Standard Build
```bash
./scripts/build-docker.sh
Optimized Build
BUILD_TYPE=optimized ./scripts/build-docker.sh
With Custom Version
VERSION=1.0.0 ./scripts/build-docker.sh
Running Containers
Basic Run
docker run -p 8080:8080 stargate/stargate-server:latest
With Environment Variables
docker run -p 8080:8080 \
-e MongoDB__ConnectionString="mongodb://mongo:27017" \
-e MongoDB__DatabaseName="stargate" \
-e Redis__ConnectionString="redis:6379" \
-e RabbitMQ__HostName="rabbitmq" \
stargate/stargate-server:latest
With Volume Mounts
docker run -p 8080:8080 \
-v $(pwd)/logs:/app/logs \
-v $(pwd)/appsettings.Production.json:/app/appsettings.Production.json:ro \
stargate/stargate-server:latest
Health Checks
Endpoints
/health - Full health check (all dependencies)
/health/live - Liveness probe (container alive)
/health/ready - Readiness probe (ready to serve traffic)
Testing Health
curl http://localhost:8080/health
curl http://localhost:8080/health/live
curl http://localhost:8080/health/ready
Docker Health Status
docker inspect --format='{{.State.Health.Status}}' <container-id>
Image Sizes
Standard Build
- Base:
mcr.microsoft.com/dotnet/aspnet:8.0-alpine
- Size: ~180-200 MB
- Includes: .NET 8 runtime
Optimized Build
- Base:
alpine:3.19
- Size: ~90-110 MB
- Includes: Self-contained app
Security
Non-Root User
Containers run as user stargate (UID 1000)
Minimal Attack Surface
- Alpine base (minimal packages)
- No unnecessary tools
- Read-only filesystem recommended
Scanning
docker scan stargate/stargate-server:latest
Troubleshooting
View Logs
docker logs -f <container-id>
Interactive Shell
docker exec -it <container-id> /bin/sh
Resource Usage
docker stats <container-id>
Best Practices
- Use specific versions in production (not
latest)
- Set resource limits to prevent resource exhaustion
- Use health checks for orchestration
- Mount logs volume for persistence
- Use secrets management for sensitive data
- Scan images regularly for vulnerabilities
- Update base images regularly
## ✅ Acceptance Criteria
- [ ] Dockerfile created with multi-stage build
- [ ] Alpine base image used for minimal size
- [ ] Non-root user configured
- [ ] Health checks implemented in Dockerfile
- [ ] Optimized Dockerfile variant created
- [ ] .dockerignore configured
- [ ] Build script created and tested
- [ ] Test script created and tested
- [ ] Size comparison script created
- [ ] Image size <200MB (standard), <120MB (optimized)
- [ ] Container starts successfully
- [ ] Health checks pass
- [ ] Logs output to stdout/stderr
- [ ] Documentation complete
- [ ] Code follows CODING-CONVENTIONS.md
## 📝 Testing Instructions
```bash
# Build standard image
./scripts/build-docker.sh
# Test container
./scripts/test-docker.sh
# Compare image sizes
./scripts/compare-image-sizes.sh
# Manual testing
# 1. Build image
docker build -t stargate-server -f src/StarGate.Server/Dockerfile .
# 2. Run container
docker run -d --name stargate-test -p 8080:8080 stargate-server
# 3. Check health
curl http://localhost:8080/health
# 4. Check Docker health status
docker inspect --format='{{.State.Health.Status}}' stargate-test
# 5. View logs
docker logs stargate-test
# 6. Check resource usage
docker stats --no-stream stargate-test
# 7. Test optimized variant
docker build -t stargate-server-opt -f src/StarGate.Server/Dockerfile.optimized .
docker run -d --name stargate-test-opt -p 8081:8080 stargate-server-opt
# 8. Compare sizes
docker images | grep stargate-server
# Cleanup
docker rm -f stargate-test stargate-test-opt
📚 References
🏷️ Labels
phase-10 containerization sprint-10.1 docker dockerfile
⏱️ Estimated Effort
8-12 hours
🔗 Dependencies
- Phase 5: API Gateway (must be functional)
- Phase 7: ProcessWorker (must be functional)
- All core functionality implemented
🔗 Related Issues
Part of Phase 10: Containerization - Sprint 10.1: Docker
📌 Important Notes
Multi-Stage Build Benefits
Smaller Images:
- Build artifacts excluded
- Only runtime files included
- SDK not in final image
Better Caching:
- Dependencies layer cached separately
- Source changes don't invalidate dependency layer
- Faster rebuilds
Security:
- Minimal attack surface
- No build tools in production
- Reduced vulnerability exposure
Standard vs Optimized
Standard (Recommended):
- Uses .NET runtime image
- Faster builds
- Better for development
- Easier debugging
- ~200MB image size
Optimized (Production):
- Self-contained deployment
- No runtime dependency
- Smaller image (~100MB)
- Slightly slower builds
- Best for production
Health Check Strategy
Three Endpoints:
-
/health - Full health check
- Checks all dependencies
- Used by monitoring
- Returns detailed status
-
/health/live - Liveness probe
- Just checks if container is alive
- Used by Kubernetes liveness
- Fast response
-
/health/ready - Readiness probe
- Checks if ready to serve traffic
- Used by Kubernetes readiness
- Checks database connection
Non-Root User Security
Why:
- Principle of least privilege
- Limits damage if compromised
- Required by some orchestrators
- Security best practice
Implementation:
RUN addgroup -g 1000 stargate && \
adduser -u 1000 -G stargate -s /bin/sh -D stargate
USER stargate
Image Size Optimization
Techniques Used:
- Alpine base (~5MB vs ~100MB for Debian)
- Multi-stage build (build artifacts excluded)
- Layer optimization (combine RUN commands)
- No cache (
apk add --no-cache)
- Trimming (remove unused code)
- Single file (one executable)
Typical Sizes:
- SDK image: ~700MB
- Runtime image: ~200MB
- Alpine runtime: ~180MB
- Optimized: ~100MB
Logging in Containers
Best Practices:
- Log to stdout/stderr (not files)
- Structured logging (JSON)
- Include timestamps
- Container orchestrator collects logs
Configuration:
builder.Logging.AddJsonConsole();
Port Configuration
Port 8080: Main API
Port 8081: Metrics/Admin
Why not 80:
- Non-root users can't bind to ports <1024
- 8080 is convention for HTTP in containers
Environment Variables
Common:
ASPNETCORE_ENVIRONMENT
ASPNETCORE_URLS
MongoDB__ConnectionString
Redis__ConnectionString
RabbitMQ__HostName
Override Example:
docker run -e ASPNETCORE_ENVIRONMENT=Production ...
Build Arguments
Metadata:
ARG BUILD_DATE
ARG VCS_REF
LABEL build-date=$BUILD_DATE \
vcs-ref=$VCS_REF
Usage:
docker build \
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
--build-arg VCS_REF=$(git rev-parse --short HEAD) \
.
Volume Mounts
Logs:
Configuration:
-v $(pwd)/appsettings.json:/app/appsettings.json:ro
Data:
-v stargate-data:/app/data
Resource Limits
Recommended:
docker run \
--memory=512m \
--memory-reservation=256m \
--cpus=1.0 \
...
Troubleshooting
Container Won't Start:
docker logs <container-id>
docker inspect <container-id>
Health Check Failing:
docker exec <container-id> curl http://localhost:8080/health
Performance Issues:
docker stats <container-id>
Network Issues:
docker network inspect bridge
📋 Task Description
Create production-ready Dockerfiles for StarGate.Server (API + Worker) with multi-stage builds, optimized image sizes, security best practices, and health check configurations. Ensure containers are efficient, secure, and ready for production deployment.
🎯 Objectives
📦 Deliverables
1. Create .dockerignore
Create
.dockerignore:2. Create Dockerfile for StarGate.Server
Create
src/StarGate.Server/Dockerfile:3. Create Optimized Dockerfile Variant
Create
src/StarGate.Server/Dockerfile.optimized:4. Create Build Script
Create
scripts/build-docker.sh:Make executable:
5. Create Docker Test Script
Create
scripts/test-docker.sh:Make executable:
6. Create Size Comparison Script
Create
scripts/compare-image-sizes.sh:Make executable:
7. Update Program.cs for Container Support
Update
src/StarGate.Server/Program.cs:8. Create Documentation
Create
docs/DOCKER.md:Optimized Build
With Custom Version
Running Containers
Basic Run
With Environment Variables
With Volume Mounts
Health Checks
Endpoints
/health- Full health check (all dependencies)/health/live- Liveness probe (container alive)/health/ready- Readiness probe (ready to serve traffic)Testing Health
Docker Health Status
Image Sizes
Standard Build
mcr.microsoft.com/dotnet/aspnet:8.0-alpineOptimized Build
alpine:3.19Security
Non-Root User
Containers run as user
stargate(UID 1000)Minimal Attack Surface
Scanning
Troubleshooting
View Logs
Interactive Shell
Resource Usage
Best Practices
latest)📚 References
🏷️ Labels
phase-10containerizationsprint-10.1dockerdockerfile⏱️ Estimated Effort
8-12 hours
🔗 Dependencies
🔗 Related Issues
Part of Phase 10: Containerization - Sprint 10.1: Docker
📌 Important Notes
Multi-Stage Build Benefits
Smaller Images:
Better Caching:
Security:
Standard vs Optimized
Standard (Recommended):
Optimized (Production):
Health Check Strategy
Three Endpoints:
/health- Full health check/health/live- Liveness probe/health/ready- Readiness probeNon-Root User Security
Why:
Implementation:
Image Size Optimization
Techniques Used:
apk add --no-cache)Typical Sizes:
Logging in Containers
Best Practices:
Configuration:
Port Configuration
Port 8080: Main API
Port 8081: Metrics/Admin
Why not 80:
Environment Variables
Common:
ASPNETCORE_ENVIRONMENTASPNETCORE_URLSMongoDB__ConnectionStringRedis__ConnectionStringRabbitMQ__HostNameOverride Example:
Build Arguments
Metadata:
Usage:
Volume Mounts
Logs:
-v $(pwd)/logs:/app/logsConfiguration:
-v $(pwd)/appsettings.json:/app/appsettings.json:roData:
Resource Limits
Recommended:
Troubleshooting
Container Won't Start:
Health Check Failing:
Performance Issues:
Network Issues: