Add Docker infrastructure with caddy-docker-proxy

Multi-stage Dockerfile (node build → caddy:2-alpine production, node dev).
Compose base + dev override for prod/dev switching via .env ENVIRONMENT flag.
Makefile targets: build, up, down, restart, logs, dev, prod, clean, rebuild.
Caddyfile serves static Astro output with caching and compression.
This commit is contained in:
Ryan Malloy 2026-01-28 14:07:02 -07:00
parent 364b6602c4
commit 162d485c31
6 changed files with 160 additions and 0 deletions

10
.dockerignore Normal file
View File

@ -0,0 +1,10 @@
node_modules
dist
.git
.gitignore
.env
*.md
!README.md
Makefile
docker-compose*.yml
.astro

16
Caddyfile Normal file
View File

@ -0,0 +1,16 @@
:80 {
root * /srv
file_server
encode gzip zstd
# SPA-style fallback for clean URLs
try_files {path} {path}/ {path}/index.html =404
# Cache static assets aggressively
@static path /_astro/* /pagefind/* /favicon.svg
header @static Cache-Control "public, max-age=31536000, immutable"
# Short cache for HTML (allows quick content updates)
@html path *.html /
header @html Cache-Control "public, max-age=3600"
}

38
Dockerfile Normal file
View File

@ -0,0 +1,38 @@
# --- Build stage ---
FROM node:22-slim AS build
WORKDIR /app
# Install dependencies first (layer caching)
COPY package.json package-lock.json ./
RUN npm ci
# Copy source and build
COPY . .
ENV ASTRO_TELEMETRY_DISABLED=1
RUN npm run build
# --- Production stage ---
FROM caddy:2-alpine AS production
COPY --from=build /app/dist /srv
COPY Caddyfile /etc/caddy/Caddyfile
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD wget -qO- http://127.0.0.1:80/ || exit 1
# --- Dev stage ---
FROM node:22-slim AS dev
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
ENV ASTRO_TELEMETRY_DISABLED=1
EXPOSE 4321
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]

59
Makefile Normal file
View File

@ -0,0 +1,59 @@
include .env
export
# Compose file selection based on ENVIRONMENT
ifeq ($(ENVIRONMENT),dev)
COMPOSE_FILES := -f docker-compose.yml -f docker-compose.dev.yml
else
COMPOSE_FILES := -f docker-compose.yml
endif
DC := docker compose $(COMPOSE_FILES)
.PHONY: help build up down restart logs status clean rebuild dev prod
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'
build: ## Build container image
$(DC) build
up: ## Start services (detached)
$(DC) up -d --build
@sleep 2
$(DC) logs --tail=20
down: ## Stop services
$(DC) down
restart: ## Restart services
$(DC) down
$(DC) up -d --build
@sleep 2
$(DC) logs --tail=20
logs: ## Show service logs
$(DC) logs -f
status: ## Show running containers
$(DC) ps
clean: ## Remove containers, images, and volumes
$(DC) down --rmi local -v
rebuild: ## Full rebuild (no cache)
$(DC) build --no-cache
$(DC) up -d
@sleep 2
$(DC) logs --tail=20
dev: ## Switch to dev mode and start
@sed -i 's/^ENVIRONMENT=.*/ENVIRONMENT=dev/' .env
@echo "Switched to dev mode"
$(MAKE) restart
prod: ## Switch to prod mode and start
@sed -i 's/^ENVIRONMENT=.*/ENVIRONMENT=prod/' .env
@echo "Switched to prod mode"
$(MAKE) restart

21
docker-compose.dev.yml Normal file
View File

@ -0,0 +1,21 @@
services:
docs:
build:
target: dev
volumes:
- ./src:/app/src
- ./public:/app/public
- ./astro.config.mjs:/app/astro.config.mjs
environment:
- VITE_HMR_HOST=${DOMAIN}
labels:
caddy: ${DOMAIN}
caddy.reverse_proxy: "{{upstreams 4321}}"
caddy.reverse_proxy.flush_interval: "-1"
caddy.reverse_proxy.transport: http
caddy.reverse_proxy.transport.read_timeout: "0"
caddy.reverse_proxy.transport.write_timeout: "0"
caddy.reverse_proxy.transport.keepalive: 5m
caddy.reverse_proxy.transport.keepalive_idle_conns: "10"
caddy.reverse_proxy.stream_timeout: 24h
caddy.reverse_proxy.stream_close_delay: 5s

16
docker-compose.yml Normal file
View File

@ -0,0 +1,16 @@
services:
docs:
build:
context: .
target: production
dockerfile: Dockerfile
restart: unless-stopped
networks:
- caddy
labels:
caddy: ${DOMAIN}
caddy.reverse_proxy: "{{upstreams 80}}"
networks:
caddy:
external: true