diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ede3a55 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +node_modules +dist +.astro +.git +.env +.env.* +!.env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..004823d --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +COMPOSE_PROJECT_NAME=mcwaddams-site +DOMAIN=mcwaddams.mcp.website diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 0000000..cec1847 --- /dev/null +++ b/Caddyfile @@ -0,0 +1,24 @@ +:8080 { + root * /srv + file_server + + handle /health { + respond "OK" 200 + } + + # SPA-style fallback for clean URLs + try_files {path} {path}/ {path}.html /index.html + + # Cache static assets aggressively + @static path *.css *.js *.svg *.png *.webp *.jpg *.jpeg *.gif *.ico *.woff *.woff2 + header @static Cache-Control "public, max-age=31536000, immutable" + + # Security headers + header { + X-Content-Type-Options "nosniff" + X-Frame-Options "DENY" + Referrer-Policy "strict-origin-when-cross-origin" + } + + encode gzip zstd +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7698e4f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +# Stage 1: Build the Astro/Starlight site +FROM node:22-alpine AS build + +WORKDIR /app + +COPY package.json package-lock.json* ./ +RUN npm ci + +COPY . . +RUN npm run build + +# Stage 2: Serve static files with Caddy +FROM caddy:2-alpine + +COPY --from=build /app/dist /srv +COPY Caddyfile /etc/caddy/Caddyfile + +EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget -qO- http://127.0.0.1:8080/health || exit 1 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8c1a521 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +.PHONY: build up down restart logs status clean + +build: + docker compose build + +up: build + docker compose up -d + @echo "Waiting for container to start..." + @sleep 3 + docker compose logs --tail=20 + +down: + docker compose down + +restart: + docker compose down + docker compose up -d --build + @sleep 3 + docker compose logs --tail=20 + +logs: + docker compose logs -f + +status: + docker compose ps + +clean: + docker compose down --rmi local --volumes diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..9b8fccb --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,19 @@ +services: + site: + build: . + restart: unless-stopped + networks: + - caddy + labels: + caddy: ${DOMAIN} + caddy.reverse_proxy: "{{upstreams 8080}}" + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/health"] + interval: 30s + timeout: 3s + start_period: 5s + retries: 3 + +networks: + caddy: + external: true