10 KiB
Docker Setup Documentation
Overview
This document describes the production-ready Docker setup for the Feuerwehr Dashboard application, including multi-stage builds, security best practices, and deployment instructions.
Architecture
The application consists of three Docker containers:
- PostgreSQL Database - postgres:16-alpine
- Backend API - Node.js application (TypeScript compiled to JavaScript)
- Frontend - React SPA served by Nginx
All containers are connected via a Docker bridge network and orchestrated using Docker Compose.
Files Structure
feuerwehr_dashboard/
├── docker-compose.yml # Production orchestration
├── docker-compose.dev.yml # Development orchestration
├── docker-test.sh # Docker build testing script
├── backend/
│ ├── Dockerfile # Multi-stage backend build
│ └── .dockerignore # Backend Docker ignore rules
└── frontend/
├── Dockerfile # Multi-stage frontend build
├── nginx.conf # Nginx configuration for SPA
└── .dockerignore # Frontend Docker ignore rules
Backend Dockerfile
Location: /backend/Dockerfile
Features
- Multi-stage build - Separate build and production stages
- Build stage:
- Based on node:20-alpine
- Installs all dependencies (including devDependencies)
- Compiles TypeScript to JavaScript
- Prunes devDependencies after build
- Production stage:
- Based on node:20-alpine
- Installs wget for health checks
- Creates non-root user (nodejs:1001)
- Copies only production node_modules
- Copies compiled JavaScript
- Copies database migrations to dist/
- Runs as non-root user
- Exposes port 3000
- Includes health check
Build Command
cd backend
docker build -t feuerwehr-backend:latest .
Image Size
Expected final image size: ~150-200 MB (alpine-based)
Security Features
- Non-root user execution
- Minimal base image (Alpine Linux)
- Only production dependencies included
- No source code in final image
Frontend Dockerfile
Location: /frontend/Dockerfile
Features
- Multi-stage build - Build stage with Node.js + production stage with Nginx
- Build stage:
- Based on node:20-alpine
- Installs dependencies
- Runs Vite build
- Accepts build arguments for environment variables
- Production stage:
- Based on nginx:alpine
- Installs wget for health checks
- Copies custom nginx.conf
- Copies built static assets
- Configures non-root nginx user
- Exposes port 80
- Includes health check
Build Command
cd frontend
docker build \
--build-arg VITE_API_URL=http://localhost:3000 \
--build-arg VITE_APP_NAME="Feuerwehr Dashboard" \
--build-arg VITE_APP_VERSION="1.0.0" \
-t feuerwehr-frontend:latest .
Build Arguments
VITE_API_URL- Backend API URL (default: http://localhost:3000)VITE_APP_NAME- Application name (default: "Feuerwehr Dashboard")VITE_APP_VERSION- Application version (default: "1.0.0")
Image Size
Expected final image size: ~50-80 MB (alpine + static assets)
Security Features
- Non-root nginx execution
- Minimal base image (Alpine Linux)
- Security headers configured
- No source code in final image
Nginx Configuration
Location: /frontend/nginx.conf
Features
-
SPA Routing
- All routes fall back to index.html
- Proper handling of client-side routing
-
Performance
- Gzip compression enabled
- Static asset caching (1 year)
- No caching for index.html
-
Security Headers
- X-Frame-Options: SAMEORIGIN
- X-Content-Type-Options: nosniff
- X-XSS-Protection: 1; mode=block
- Referrer-Policy: strict-origin-when-cross-origin
-
Health Check
- Endpoint: /health
- Returns 200 with "healthy" text
-
Error Handling
- 404 errors redirect to index.html
- Custom 50x error page
Docker Compose
Location: /docker-compose.yml
Services
PostgreSQL
postgres:
image: postgres:16-alpine
ports: 5432:5432
volumes: postgres_data_prod
health_check: pg_isready
restart: unless-stopped
Backend
backend:
build: ./backend
ports: 3000:3000
depends_on: postgres (healthy)
health_check: wget localhost:3000/health
restart: unless-stopped
Frontend
frontend:
build: ./frontend
build_args: VITE_API_URL
ports: 80:80
depends_on: backend (healthy)
health_check: wget localhost:80/health
restart: unless-stopped
Environment Variables
Create a .env file based on .env.example:
Required:
POSTGRES_PASSWORD- Database passwordJWT_SECRET- JWT signing secret
Optional:
POSTGRES_DB- Database name (default: feuerwehr_prod)POSTGRES_USER- Database user (default: prod_user)POSTGRES_PORT- Database port (default: 5432)BACKEND_PORT- Backend port (default: 3000)FRONTEND_PORT- Frontend port (default: 80)CORS_ORIGIN- CORS origin (default: http://localhost:80)VITE_API_URL- Frontend API URL (default: http://localhost:3000)
Networks
feuerwehr_network- Bridge network connecting all services
Volumes
postgres_data_prod- Persistent PostgreSQL data
Usage
1. Test Docker Builds
Run the test script to verify Docker builds work:
./docker-test.sh
This script will:
- Check Docker availability
- Build backend image
- Build frontend image
- Report success/failure
- Optionally cleanup test images
2. Configure Environment
Copy and configure environment file:
cp .env.example .env
# Edit .env and set required variables
3. Build and Start Services
# Build and start all services
docker-compose up -d
# View logs
docker-compose logs -f
# Check service status
docker-compose ps
4. Access Application
- Frontend: http://localhost:80
- Backend API: http://localhost:3000
- Database: localhost:5432
5. Stop Services
# Stop all services
docker-compose down
# Stop and remove volumes
docker-compose down -v
Health Checks
All services include health checks:
PostgreSQL
- Command:
pg_isready -U $USER -d $DB - Interval: 10s
- Retries: 5
Backend
- Command:
wget localhost:3000/health - Interval: 30s
- Retries: 3
- Start Period: 40s
Frontend
- Command:
wget localhost:80/health - Interval: 30s
- Retries: 3
- Start Period: 30s
Troubleshooting
Backend Build Fails
- Check TypeScript compilation:
cd backend
npm install
npm run build
- Verify all source files exist
- Check tsconfig.json configuration
Frontend Build Fails
- Check Vite build:
cd frontend
npm install
npm run build
- Verify all dependencies are installed
- Check build arguments are correct
Container Won't Start
- Check logs:
docker-compose logs [service-name]
- Verify environment variables in .env
- Check health check status:
docker-compose ps
Database Connection Issues
- Verify DATABASE_URL format:
postgresql://user:password@postgres:5432/database
- Check postgres service is healthy:
docker-compose ps postgres
- Verify network connectivity:
docker-compose exec backend ping postgres
Security Considerations
Production Checklist
- Set strong POSTGRES_PASSWORD
- Set strong JWT_SECRET (min 32 characters)
- Use HTTPS in production
- Configure proper CORS_ORIGIN
- Enable firewall rules
- Regular security updates
- Monitor container logs
- Backup database regularly
Image Security
- Alpine Linux base (minimal attack surface)
- Non-root user execution
- No unnecessary packages
- Security headers enabled
- Health checks configured
Maintenance
Update Dependencies
# Rebuild images with latest dependencies
docker-compose build --no-cache
docker-compose up -d
Backup Database
# Create backup
docker-compose exec postgres pg_dump -U $USER $DB > backup.sql
# Restore backup
docker-compose exec -T postgres psql -U $USER $DB < backup.sql
View Logs
# All services
docker-compose logs -f
# Specific service
docker-compose logs -f backend
# Last 100 lines
docker-compose logs --tail=100 backend
Restart Services
# Restart all
docker-compose restart
# Restart specific service
docker-compose restart backend
Performance Optimization
Build Cache
Docker uses layer caching. To optimize:
- Copy package files first
- Install dependencies
- Copy source code last
This ensures dependency installation is cached.
Image Size
Current image sizes:
- Backend: ~150-200 MB
- Frontend: ~50-80 MB
- Total: ~200-280 MB
Resource Limits
Add resource limits in docker-compose.yml:
services:
backend:
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
Monitoring
Container Stats
docker stats
Health Status
docker-compose ps
Resource Usage
docker system df
Production Deployment
Using Docker Compose
# On production server
git clone <repository>
cd feuerwehr_dashboard
cp .env.example .env
# Configure .env with production values
docker-compose up -d
Using Container Registry
# Build and tag images
docker build -t registry.example.com/feuerwehr-backend:v1.0.0 backend/
docker build -t registry.example.com/feuerwehr-frontend:v1.0.0 frontend/
# Push to registry
docker push registry.example.com/feuerwehr-backend:v1.0.0
docker push registry.example.com/feuerwehr-frontend:v1.0.0
# Pull and run on production
docker pull registry.example.com/feuerwehr-backend:v1.0.0
docker pull registry.example.com/feuerwehr-frontend:v1.0.0
docker-compose up -d