# Container Deployment Guide This guide covers the containerized deployment setup for the www.ermnvldmr.com website, including both the main Astro site and the Storybook component library. ## Overview The project uses Docker containers to package and deploy: - **Main Site**: Astro static website served via nginx - **Storybook**: Component library served via nginx - **Registry**: GitHub Container Registry (ghcr.io) - **CI/CD**: GitHub Actions with semantic versioning ## Container Architecture ### Multi-stage Build Strategy Both containers follow a multi-stage build pattern: 1. **Builder Stage**: Node.js 20 Alpine with pnpm for building assets 2. **Production Stage**: nginx:1.25-alpine for serving static content ### Security Features - **Non-root user**: Containers run as `nginx-user` (UID 1001) - **Non-privileged ports**: Expose port 8080 instead of 80 - **Security headers**: CSP, HSTS, and other security headers configured - **Signal handling**: dumb-init for proper signal handling ### Images - **Main Site**: `ghcr.io/deytenit/www.ermnvldmr.com` - **Storybook**: `ghcr.io/deytenit/www.ermnvldmr.com/storybook` ## Tagging Strategy ### Release Tags (from `release/semantic-dispatch`) For both images: - `vX.X.X` - Full version (e.g., `v1.2.3`) - `vX.X` - Minor version (e.g., `v1.2`) - `vX` - Major version (e.g., `v1`) - `latest` - Latest stable release - `{sha}` - Commit SHA for traceability ### Pre-release Tags (from `next/semantic-dispatch`) For both images: - `vX.X.X-next.X` - Full prerelease version (e.g., `v1.2.3-next.1`) - `vX.X.X-next` - Latest prerelease for that version - `vX.X-next` - Latest prerelease for minor version - `vX-next` - Latest prerelease for major version - `latest-next` - Latest prerelease overall - `{sha}` - Commit SHA for traceability ## Local Development ### Using Docker Compose Start both services locally: ```bash # Start both containers docker-compose up # Start in background docker-compose up -d # View logs docker-compose logs -f # Stop services docker-compose down ``` Access points: - **Main Site**: http://localhost:3000 - **Storybook**: http://localhost:6006 ### Building Individual Images ```bash # Build Astro site docker build -f docker/astro/Dockerfile -t www-astro . # Build Storybook docker build -f docker/storybook/Dockerfile -t www-storybook . # Run containers docker run -p 3000:8080 www-astro docker run -p 6006:8080 www-storybook ``` ## Production Deployment ### Manual Container Deployment Pull and run the latest images: ```bash # Pull latest stable images docker pull ghcr.io/deytenit/www.ermnvldmr.com:latest docker pull ghcr.io/deytenit/www.ermnvldmr.com/storybook:latest # Run main site docker run -d \ --name www-ermnvldmr-astro \ -p 3000:8080 \ --restart unless-stopped \ ghcr.io/deytenit/www.ermnvldmr.com:latest # Run storybook docker run -d \ --name www-ermnvldmr-storybook \ -p 6006:8080 \ --restart unless-stopped \ ghcr.io/deytenit/www.ermnvldmr.com/storybook:latest ``` ### Environment Variables The containers are designed to be stateless and don't require runtime environment variables. Configuration is baked into the build. ### Health Checks Both containers include health check endpoints: ```bash # Check container health curl http://localhost:3000/health # Astro site curl http://localhost:6006/health # Storybook ``` ### Resource Requirements **Recommended minimums per container:** - **CPU**: 0.1 cores - **Memory**: 64MB - **Storage**: 100MB **Production recommendations:** - **CPU**: 0.25 cores - **Memory**: 128MB - **Storage**: 500MB (for logs and cache) ## Reverse Proxy Configuration ### nginx Configuration Example ```nginx server { listen 80; server_name www.ermnvldmr.com; location / { proxy_pass http://astro-container:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } server { listen 80; server_name storybook.ermnvldmr.com; location / { proxy_pass http://storybook-container:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } ``` ### Traefik Configuration Example ```yaml # docker-compose.yml with Traefik version: '3.8' services: astro: image: ghcr.io/deytenit/www.ermnvldmr.com:latest labels: - "traefik.enable=true" - "traefik.http.routers.astro.rule=Host(`www.ermnvldmr.com`)" - "traefik.http.services.astro.loadbalancer.server.port=8080" storybook: image: ghcr.io/deytenit/www.ermnvldmr.com/storybook:latest labels: - "traefik.enable=true" - "traefik.http.routers.storybook.rule=Host(`storybook.ermnvldmr.com`)" - "traefik.http.services.storybook.loadbalancer.server.port=8080" ``` ## Troubleshooting ### Common Issues #### Container Won't Start ```bash # Check logs docker logs container-name # Common causes: # - Missing dependencies in build # - Permission issues # - Port conflicts ``` #### Build Failures ```bash # Check build logs docker build -f docker/astro/Dockerfile -t test . # Common causes: # - Node version mismatch # - pnpm cache issues # - Missing build dependencies ``` #### Permission Denied ```bash # Check if running as correct user docker exec container-name whoami # Should output: nginx-user ``` ### Debugging Enable debug mode in nginx: ```bash # Create debug container docker run -it --entrypoint=/bin/sh ghcr.io/deytenit/www.ermnvldmr.com:latest # Check nginx config nginx -t # Check file permissions ls -la /usr/share/nginx/html/ ``` ### Performance Tuning #### nginx Optimization For high-traffic deployments, consider: - Adjusting worker processes - Enabling gzip compression levels - Tuning keepalive settings - Adding rate limiting #### Container Resources Monitor and adjust: ```bash # Check resource usage docker stats # Update container limits docker run --memory=256m --cpus=0.5 ... ``` ## Security Considerations ### Container Security - Containers run as non-root user - Minimal attack surface with Alpine base - Regular security updates via base image updates - No sensitive data in containers ### Network Security - Expose only necessary ports - Use internal networks for service communication - Implement proper firewall rules - Enable HTTPS with reverse proxy ### Registry Security - Images are signed and scanned - Access controlled via GitHub permissions - Automatic vulnerability scanning - Regular base image updates ## Monitoring ### Health Monitoring ```bash # Simple health check script #!/bin/bash if curl -f http://localhost:3000/health >/dev/null 2>&1; then echo "Astro site: healthy" else echo "Astro site: unhealthy" exit 1 fi ``` ### Log Management ```bash # View logs docker-compose logs -f astro docker-compose logs -f storybook # Log rotation (configure docker daemon) # /etc/docker/daemon.json { "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } } ``` ### Metrics Collection Consider integrating with: - Prometheus + Grafana - nginx prometheus exporter - Container metrics exporters - Application performance monitoring ## CI/CD Integration The containers are automatically built and pushed when: 1. **Pre-release**: Merge to `next` branch triggers prerelease 2. **Release**: Manual workflow dispatch creates stable release ### Workflow Outputs Each workflow provides: - `new_release_published`: Boolean indicating if release was created - `new_release_version`: The version number of the release ### Manual Triggers ```bash # Trigger release workflow via GitHub CLI gh workflow run "release/semantic-dispatch" --ref main # Trigger prerelease via merge to next branch git checkout next git merge feature-branch git push origin next ``` ## Maintenance ### Regular Updates 1. **Base Image Updates**: Rebuild monthly for security patches 2. **Dependency Updates**: Follow project dependency update schedule 3. **Configuration Review**: Quarterly review of nginx and security settings ### Backup Strategy Since containers are stateless: - Source code is backed up in Git repository - Container images are versioned in registry - No persistent data to backup ### Rollback Procedure ```bash # Rollback to previous version docker pull ghcr.io/deytenit/www.ermnvldmr.com:v1.2.2 docker stop current-container docker run -d --name new-container ghcr.io/deytenit/www.ermnvldmr.com:v1.2.2 # Or use docker-compose with specific version docker-compose down # Edit docker-compose.yml to specify version docker-compose up -d ``` --- For additional support or questions, refer to the project repository issues or documentation.