The Docker implementation for this project provides a production-ready container using Caddy as the web server. The container is designed for security, performance, and ease of deployment across various environments.
The Dockerfile uses a single-stage build to create an optimized image:
# Using the lightweight Alpine variant of Caddy for better performance
FROM caddy:2.8.4-alpine
# Set the working directory for the site files
WORKDIR /usr/share/caddy
# Copy the Astro-built static files (from the 'dist' directory after 'bun run build')
COPY ./dist .
# Copy our custom Caddy configuration
COPY Caddyfile /etc/caddy/Caddyfile
# Set proper ownership and permissions for security
RUN chown -R root:root /usr/share/caddy && \
chmod -R 755 /usr/share/caddy && \
# Create Caddy-specific directories with proper permissions
mkdir -p /data/caddy /config/caddy && \
chmod 700 /data/caddy /config/caddy
# Expose the HTTP port (HTTPS is handled by Cloudflare in production)
EXPOSE 80
# Run Caddy with our custom config
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]- Base Image: Uses the Alpine variant of Caddy for a minimal footprint (~40MB)
- Security: Sets proper file permissions for web content
- Configuration: Uses a custom Caddyfile for optimized serving
- Simplicity: Assumes the static files are pre-built before Docker build
A more comprehensive multi-stage build could include the build process itself:
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
# Install Bun for faster builds
RUN npm install -g bun
# Copy package files and install dependencies
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile
# Copy source files and build
COPY . .
RUN bun run build
# Production stage
FROM caddy:2.8.4-alpine
WORKDIR /usr/share/caddy
# Copy built files from builder stage
COPY --from=builder /app/dist .
COPY Caddyfile /etc/caddy/Caddyfile
# Set permissions and create directories
RUN chown -R root:root /usr/share/caddy && \
chmod -R 755 /usr/share/caddy && \
mkdir -p /data/caddy /config/caddy && \
chmod 700 /data/caddy /config/caddy
EXPOSE 80
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]This improved approach would:
- Build the site inside the container
- Ensure build environment consistency
- Reduce the final image size by excluding build tools
- Simplify the CI/CD process
The project includes multi-architecture builds for maximum compatibility:
docker buildx build \
--platform linux/arm64,linux/amd64,linux/arm/v6,linux/arm/v7 \
-t [repo]/[image-name]:[tag] . \
--pushThis enables deployment on:
- Standard x86_64 servers (linux/amd64)
- ARM-based servers (linux/arm64)
- Raspberry Pi and similar devices (linux/arm/v6, linux/arm/v7)
# Basic Caddyfile for the Revista site
:80 {
# Enable gzip compression
encode gzip
# Set cache control headers for better performance
header /* {
# Cache static assets for 1 week
Cache-Control "public, max-age=604800, must-revalidate"
# Security headers
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
}
# Special cache settings for images
header /assets/* {
Cache-Control "public, max-age=2592000, must-revalidate"
}
# Serve the static site from the container's working directory
root * /usr/share/caddy
file_server
}
This configuration provides:
- Compression: Enables gzip compression for faster delivery
- Cache Control: Sets appropriate cache times for different asset types
- Security Headers: Adds security headers for enhanced protection
- Static Serving: Configures the file server for optimal performance
The project includes a compose.yaml file for easy deployment:
version: '3'
services:
revista:
build: .
ports:
- "8080:80"
restart: unless-stopped
volumes:
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:Key aspects:
- Port Mapping: Maps container port 80 to host port 8080
- Restart Policy: Ensures the container restarts automatically
- Persistent Volumes: Creates volumes for Caddy data and configuration
The Docker setup includes several security enhancements:
- Read-Only Content: Web content is not modified at runtime
- Minimal Permissions: Content files are owned by root but readable by the Caddy process
- Security Headers: The Caddyfile adds protective HTTP headers
- No Unnecessary Services: The container runs only Caddy
The final image size is optimized at approximately 40MB by:
- Using Alpine Linux as the base OS
- Including only the built static files
- Not installing additional packages
This small footprint provides:
- Faster container startup
- Reduced storage requirements
- Smaller attack surface
- Faster deployment and downloads