diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3bcee91 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +node_modules +build +.svelte-kit +data +.git +.gitignore +.env* +*.md +.planning +.vscode diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4ad0d87 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,54 @@ +# Build stage +FROM node:22-alpine AS builder + +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install dependencies +RUN npm ci + +# Copy source code +COPY . . + +# Build the application +RUN npm run build + +# Prune dev dependencies +RUN npm prune --production + +# Production stage +FROM node:22-alpine AS production + +WORKDIR /app + +# Create non-root user +RUN addgroup -g 1001 -S nodejs && \ + adduser -S nodejs -u 1001 + +# Copy built application from builder +COPY --from=builder --chown=nodejs:nodejs /app/build ./build +COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules +COPY --from=builder --chown=nodejs:nodejs /app/package.json ./ + +# Create data directory with proper ownership +RUN mkdir -p /app/data && chown -R nodejs:nodejs /app/data + +# Switch to non-root user +USER nodejs + +# Set environment variables +ENV NODE_ENV=production +ENV TASKPLANER_DATA_DIR=/app/data +ENV PORT=3000 + +# Expose port +EXPOSE 3000 + +# Health check (endpoint created in Plan 02) +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1 + +# Start the application +CMD ["node", "build/index.js"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f11f91b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +services: + taskplaner: + build: . + container_name: taskplaner + ports: + - "3000:3000" + volumes: + - taskplaner_data:/app/data + environment: + - NODE_ENV=production + - TASKPLANER_DATA_DIR=/app/data + restart: unless-stopped + +volumes: + taskplaner_data: