8.7 KiB
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:
- Builder Stage: Node.js 20 Alpine with pnpm for building assets
- 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 versionvX.X-next- Latest prerelease for minor versionvX-next- Latest prerelease for major versionlatest-next- Latest prerelease overall{sha}- Commit SHA for traceability
Local Development
Using Docker Compose
Start both services locally:
# 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
# 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:
# 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:
# 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
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
# 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
# Check logs
docker logs container-name
# Common causes:
# - Missing dependencies in build
# - Permission issues
# - Port conflicts
Build Failures
# Check build logs
docker build -f docker/astro/Dockerfile -t test .
# Common causes:
# - Node version mismatch
# - pnpm cache issues
# - Missing build dependencies
Permission Denied
# Check if running as correct user
docker exec container-name whoami
# Should output: nginx-user
Debugging
Enable debug mode in nginx:
# 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:
# 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
# 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
# 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:
- Pre-release: Merge to
nextbranch triggers prerelease - Release: Manual workflow dispatch creates stable release
Workflow Outputs
Each workflow provides:
new_release_published: Boolean indicating if release was creatednew_release_version: The version number of the release
Manual Triggers
# 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
- Base Image Updates: Rebuild monthly for security patches
- Dependency Updates: Follow project dependency update schedule
- 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
# 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.