Add Docker support for self-hosting

- Add Dockerfile with multi-stage build for Next.js
- Add docker-compose.yml with Traefik labels for jeffemmett.com
- Add .dockerignore
- Configure next.config.mjs for standalone output

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2025-12-26 22:56:05 -05:00
parent 7f7c0580d6
commit 53c793342a
4 changed files with 80 additions and 1 deletions

8
.dockerignore Normal file
View File

@ -0,0 +1,8 @@
node_modules
.next
.git
.gitignore
README.md
*.log
.env*
.vercel

43
Dockerfile Normal file
View File

@ -0,0 +1,43 @@
# Build stage
FROM node:20-alpine AS builder
# Install pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
# Copy package files
COPY package.json pnpm-lock.yaml ./
# Install dependencies
RUN pnpm install --frozen-lockfile
# Copy source files
COPY . .
# Build the Next.js app
ENV NEXT_TELEMETRY_DISABLED=1
RUN pnpm build
# Production stage
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
# Install pnpm for production
RUN corepack enable && corepack prepare pnpm@latest --activate
# Copy necessary files from builder
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]

28
docker-compose.yml Normal file
View File

@ -0,0 +1,28 @@
# Jeff Emmett Personal Website
# Serves: jeffemmett.com, www.jeffemmett.com
services:
personal-site:
build:
context: .
dockerfile: Dockerfile
container_name: personal-site
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik-public"
- "traefik.http.services.personal-site.loadbalancer.server.port=3000"
- "traefik.http.routers.personal-site.rule=Host(`jeffemmett.com`) || Host(`www.jeffemmett.com`)"
- "traefik.http.routers.personal-site.entrypoints=web"
- "traefik.http.routers.personal-site.service=personal-site"
networks:
- traefik-public
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000"]
interval: 30s
timeout: 10s
retries: 3
networks:
traefik-public:
external: true

View File

@ -1,12 +1,12 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
typescript: {
ignoreBuildErrors: true,
},
images: {
unoptimized: true,
},
}
export default nextConfig