🚀 Docker Production Optimizations: - Chiseled Ubuntu base image for minimal attack surface - Non-root user execution with security hardening - Read-only filesystem with targeted writable volumes - Resource limits (1GB RAM, 1 CPU) with health checks - Multi-stage builds optimized for caching - Zero-downtime deployment automation 🔍 Comprehensive Monitoring Stack: - Prometheus metrics collection with custom rules - Grafana dashboards for application visualization - AlertManager with email notifications for critical events - Fluentd centralized logging with retention policies - Node Exporter + cAdvisor for system/container metrics - Health check endpoint (/health) for container orchestration 📋 Production Deployment Ready: - Complete deployment scripts with backup strategy - Environment templates for secure configuration - Performance monitoring and alerting rules - Enterprise-grade security and observability 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
151 lines
5.0 KiB
YAML
151 lines
5.0 KiB
YAML
version: '3.8'
|
|
|
|
services:
|
|
littleshop:
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile
|
|
target: final
|
|
image: littleshop:production
|
|
container_name: littleshop_prod
|
|
restart: unless-stopped
|
|
|
|
# Resource limits for production
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
memory: 1G
|
|
cpus: '1.0'
|
|
reservations:
|
|
memory: 512M
|
|
cpus: '0.5'
|
|
|
|
# Security configuration
|
|
security_opt:
|
|
- no-new-privileges:true
|
|
read_only: true
|
|
|
|
# Temporary file systems for read-only container
|
|
tmpfs:
|
|
- /tmp:noexec,nosuid,size=100m
|
|
- /var/tmp:noexec,nosuid,size=50m
|
|
|
|
environment:
|
|
- ASPNETCORE_ENVIRONMENT=Production
|
|
- ASPNETCORE_URLS=http://+:8080
|
|
- DOTNET_ENVIRONMENT=Production
|
|
- DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
|
|
- DOTNET_RUNNING_IN_CONTAINER=true
|
|
- DOTNET_USE_POLLING_FILE_WATCHER=true
|
|
- ASPNETCORE_FORWARDEDHEADERS_ENABLED=true
|
|
|
|
# Application secrets (from environment or .env file)
|
|
- JWT_SECRET_KEY=${JWT_SECRET_KEY}
|
|
- BTCPAY_SERVER_URL=${BTCPAY_SERVER_URL}
|
|
- BTCPAY_STORE_ID=${BTCPAY_STORE_ID}
|
|
- BTCPAY_API_KEY=${BTCPAY_API_KEY}
|
|
- BTCPAY_WEBHOOK_SECRET=${BTCPAY_WEBHOOK_SECRET}
|
|
|
|
volumes:
|
|
# Persistent data volumes (writable)
|
|
- littleshop_data:/app/data:rw
|
|
- littleshop_uploads:/app/wwwroot/uploads:rw
|
|
- littleshop_logs:/app/logs:rw
|
|
|
|
networks:
|
|
- traefik
|
|
- littleshop_internal
|
|
|
|
# Health check
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 60s
|
|
|
|
labels:
|
|
# Traefik configuration for production
|
|
- "traefik.enable=true"
|
|
- "traefik.docker.network=traefik"
|
|
|
|
# HTTP to HTTPS redirect
|
|
- "traefik.http.routers.littleshop-http.rule=Host(`littleshop.silverlabs.uk`)"
|
|
- "traefik.http.routers.littleshop-http.entrypoints=web"
|
|
- "traefik.http.routers.littleshop-http.middlewares=littleshop-redirect"
|
|
- "traefik.http.middlewares.littleshop-redirect.redirectscheme.scheme=https"
|
|
- "traefik.http.middlewares.littleshop-redirect.redirectscheme.permanent=true"
|
|
|
|
# HTTPS Router with security headers
|
|
- "traefik.http.routers.littleshop.rule=Host(`littleshop.silverlabs.uk`)"
|
|
- "traefik.http.routers.littleshop.entrypoints=websecure"
|
|
- "traefik.http.routers.littleshop.tls=true"
|
|
- "traefik.http.routers.littleshop.tls.certresolver=letsencrypt"
|
|
- "traefik.http.routers.littleshop.middlewares=littleshop-security,littleshop-headers"
|
|
|
|
# Service configuration
|
|
- "traefik.http.services.littleshop.loadbalancer.server.port=8080"
|
|
- "traefik.http.services.littleshop.loadbalancer.healthcheck.path=/health"
|
|
- "traefik.http.services.littleshop.loadbalancer.healthcheck.interval=30s"
|
|
|
|
# Security headers middleware
|
|
- "traefik.http.middlewares.littleshop-security.headers.frameDeny=true"
|
|
- "traefik.http.middlewares.littleshop-security.headers.sslRedirect=true"
|
|
- "traefik.http.middlewares.littleshop-security.headers.browserXssFilter=true"
|
|
- "traefik.http.middlewares.littleshop-security.headers.contentTypeNosniff=true"
|
|
- "traefik.http.middlewares.littleshop-security.headers.forceSTSHeader=true"
|
|
- "traefik.http.middlewares.littleshop-security.headers.stsIncludeSubdomains=true"
|
|
- "traefik.http.middlewares.littleshop-security.headers.stsPreload=true"
|
|
- "traefik.http.middlewares.littleshop-security.headers.stsSeconds=31536000"
|
|
- "traefik.http.middlewares.littleshop-security.headers.referrerPolicy=strict-origin-when-cross-origin"
|
|
- "traefik.http.middlewares.littleshop-security.headers.permissionsPolicy=camera=(), microphone=(), geolocation=()"
|
|
|
|
# Custom headers for forwarded requests
|
|
- "traefik.http.middlewares.littleshop-headers.headers.customrequestheaders.X-Forwarded-Proto=https"
|
|
- "traefik.http.middlewares.littleshop-headers.headers.customrequestheaders.X-Forwarded-Host=littleshop.silverlabs.uk"
|
|
- "traefik.http.middlewares.littleshop-headers.headers.customrequestheaders.X-Real-IP="
|
|
|
|
# Log aggregation service (optional)
|
|
fluentd:
|
|
image: fluent/fluentd:v1.16-1
|
|
container_name: littleshop_logs
|
|
restart: unless-stopped
|
|
volumes:
|
|
- littleshop_logs:/fluentd/log:ro
|
|
- ./docker/fluentd.conf:/fluentd/etc/fluent.conf:ro
|
|
networks:
|
|
- littleshop_internal
|
|
depends_on:
|
|
- littleshop
|
|
|
|
# Optimized volume configuration
|
|
volumes:
|
|
littleshop_data:
|
|
driver: local
|
|
driver_opts:
|
|
type: none
|
|
o: bind
|
|
device: /opt/littleshop/data
|
|
littleshop_uploads:
|
|
driver: local
|
|
driver_opts:
|
|
type: none
|
|
o: bind
|
|
device: /opt/littleshop/uploads
|
|
littleshop_logs:
|
|
driver: local
|
|
driver_opts:
|
|
type: none
|
|
o: bind
|
|
device: /opt/littleshop/logs
|
|
|
|
# Network isolation
|
|
networks:
|
|
traefik:
|
|
external: true
|
|
littleshop_internal:
|
|
driver: bridge
|
|
internal: true
|
|
ipam:
|
|
config:
|
|
- subnet: 172.20.0.0/24 |