This commit is contained in:
2026-01-13 02:42:03 +01:00
parent 19081ec682
commit e46b104127
21 changed files with 1622 additions and 2 deletions

47
docker/Caddyfile Normal file
View File

@@ -0,0 +1,47 @@
# Caddyfile for reverse proxy with automatic SSL
{
# Email for Let's Encrypt notifications
email {$EMAIL:-admin@example.com}
}
# Main website
{$DOMAIN:-localhost} {
# Reverse proxy to website container
reverse_proxy website:80
# Security headers
header {
# Enable HSTS
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Prevent clickjacking
X-Frame-Options "SAMEORIGIN"
# Prevent MIME sniffing
X-Content-Type-Options "nosniff"
# XSS protection
X-XSS-Protection "1; mode=block"
# Remove server info
Server "mintel"
}
# Logging
log {
output file /var/log/caddy/access.log
format json
}
# Compression
encode zstd gzip
}
# Analytics subdomain (if using your existing Plausible)
analytics.{$DOMAIN:-localhost} {
# Point to your existing Plausible instance
# Replace with your Plausible server IP/domain
reverse_proxy http://YOUR_PLAUSIBLE_SERVER:8000
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
}
}

34
docker/Dockerfile Normal file
View File

@@ -0,0 +1,34 @@
# Multi-stage build for optimized Astro static site
FROM node:22-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY . .
# Build the Astro site
RUN npm run build
# Production stage - Nginx for serving static files
FROM nginx:alpine
# Remove default nginx static assets
RUN rm -rf /usr/share/nginx/html/*
# Copy built assets from builder stage
COPY --from=builder /app/dist /usr/share/nginx/html
# Copy custom nginx config
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
# Expose port
EXPOSE 80
# Start nginx
CMD ["nginx", "-g", "daemon off;"]

46
docker/nginx.conf Normal file
View File

@@ -0,0 +1,46 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Enable gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/xml+rss
application/json
font/woff2
image/svg+xml;
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header X-Content-Type-Options "nosniff";
}
# Security headers
location / {
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Try files, fallback to index.html for SPA routing
try_files $uri $uri/ /index.html;
}
# Health check endpoint
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}