# 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: 1. **PostgreSQL Database** - postgres:16-alpine 2. **Backend API** - Node.js application (TypeScript compiled to JavaScript) 3. **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 ```bash 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 ```bash 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 1. **SPA Routing** - All routes fall back to index.html - Proper handling of client-side routing 2. **Performance** - Gzip compression enabled - Static asset caching (1 year) - No caching for index.html 3. **Security Headers** - X-Frame-Options: SAMEORIGIN - X-Content-Type-Options: nosniff - X-XSS-Protection: 1; mode=block - Referrer-Policy: strict-origin-when-cross-origin 4. **Health Check** - Endpoint: /health - Returns 200 with "healthy" text 5. **Error Handling** - 404 errors redirect to index.html - Custom 50x error page ## Docker Compose **Location:** `/docker-compose.yml` ### Services #### PostgreSQL ```yaml postgres: image: postgres:16-alpine ports: 5432:5432 volumes: postgres_data_prod health_check: pg_isready restart: unless-stopped ``` #### Backend ```yaml backend: build: ./backend ports: 3000:3000 depends_on: postgres (healthy) health_check: wget localhost:3000/health restart: unless-stopped ``` #### Frontend ```yaml 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 password - `JWT_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: ```bash ./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: ```bash cp .env.example .env # Edit .env and set required variables ``` ### 3. Build and Start Services ```bash # 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 ```bash # 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 1. Check TypeScript compilation: ```bash cd backend npm install npm run build ``` 2. Verify all source files exist 3. Check tsconfig.json configuration ### Frontend Build Fails 1. Check Vite build: ```bash cd frontend npm install npm run build ``` 2. Verify all dependencies are installed 3. Check build arguments are correct ### Container Won't Start 1. Check logs: ```bash docker-compose logs [service-name] ``` 2. Verify environment variables in .env 3. Check health check status: ```bash docker-compose ps ``` ### Database Connection Issues 1. Verify DATABASE_URL format: ``` postgresql://user:password@postgres:5432/database ``` 2. Check postgres service is healthy: ```bash docker-compose ps postgres ``` 3. Verify network connectivity: ```bash 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 ```bash # Rebuild images with latest dependencies docker-compose build --no-cache docker-compose up -d ``` ### Backup Database ```bash # 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 ```bash # All services docker-compose logs -f # Specific service docker-compose logs -f backend # Last 100 lines docker-compose logs --tail=100 backend ``` ### Restart Services ```bash # Restart all docker-compose restart # Restart specific service docker-compose restart backend ``` ## Performance Optimization ### Build Cache Docker uses layer caching. To optimize: 1. Copy package files first 2. Install dependencies 3. 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: ```yaml services: backend: deploy: resources: limits: cpus: '1.0' memory: 512M ``` ## Monitoring ### Container Stats ```bash docker stats ``` ### Health Status ```bash docker-compose ps ``` ### Resource Usage ```bash docker system df ``` ## Production Deployment ### Using Docker Compose ```bash # On production server git clone cd feuerwehr_dashboard cp .env.example .env # Configure .env with production values docker-compose up -d ``` ### Using Container Registry ```bash # 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 ``` ## Additional Resources - [Docker Documentation](https://docs.docker.com/) - [Docker Compose Documentation](https://docs.docker.com/compose/) - [Nginx Documentation](https://nginx.org/en/docs/) - [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices)