diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..9bdaea27 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,16 @@ +.env + +Dockerfile +./**/*/Dockerfile + +.dockerignore + +node_modules +./**/*/node_modules + +pnpm-debug.log +./**/*/pnpm-debug.log + +README.md +.next +.git \ No newline at end of file diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile new file mode 100644 index 00000000..ff756fc4 --- /dev/null +++ b/apps/web/Dockerfile @@ -0,0 +1,85 @@ +ARG NODE_VERSION=20 +ARG APP_DIRNAME=web +ARG PROJECT=@kan/web + +# 1. Alpine image +FROM node:${NODE_VERSION}-alpine AS alpine +RUN apk update +RUN apk add --no-cache libc6-compat python3 make g++ + +# Setup pnpm and turbo on the alpine base +FROM alpine AS base +RUN corepack enable +# Replace with the major version installed in your repository. For example: +# RUN npm install turbo@2.1.3 --global +RUN npm install turbo@2.3.1 --global + +RUN pnpm config set store-dir ~/.pnpm-store + +# 2. Prune projects +FROM base AS pruner +# https://stackoverflow.com/questions/49681984/how-to-get-version-value-of-package-json-inside-of-dockerfile +# RUN export VERSION=$(npm run version) + +ARG PROJECT + +# Set working directory +WORKDIR /app + +# It might be the path to turborepo +COPY . . + +# Generate a partial monorepo with a pruned lockfile for a target workspace. +# Assuming "@acme/nextjs" is the name entered in the project's package.json: { name: "@acme/nextjs" } +RUN turbo prune --scope=${PROJECT} --docker + +# 3. Build the project +FROM base AS builder +ARG PROJECT + +# Environment to skip .env validation on build +ENV CI=true + +WORKDIR /app + +ARG NEXT_PUBLIC_KAN_ENV=${NEXT_PUBLIC_KAN_ENV} +ARG NEXT_PUBLIC_UMAMI_ID=${NEXT_PUBLIC_UMAMI_ID} +ARG NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL} +ARG NEXT_PUBLIC_STORAGE_URL=${NEXT_PUBLIC_STORAGE_URL} +ARG NEXT_PUBLIC_AVATAR_BUCKET_NAME=${NEXT_PUBLIC_AVATAR_BUCKET_NAME} + +# Copy lockfile and package.json's of isolated subworkspace +COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml +COPY --from=pruner /app/out/pnpm-workspace.yaml ./pnpm-workspace.yaml +COPY --from=pruner /app/out/json/ . + +# First install the dependencies (as they change less often) +RUN --mount=type=cache,id=pnpm,target=~/.pnpm-store pnpm install --frozen-lockfile + +# Copy source code of isolated subworkspace +COPY --from=pruner /app/out/full/ . + +RUN pnpm build --filter=${PROJECT} + +# 4. Final image - runner stage to run the application +FROM base AS runner +ARG APP_DIRNAME + +# Don't run production as root +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs +USER nextjs + +WORKDIR /app + +ENV NODE_ENV=production + +COPY --chown=nextjs:nodejs --from=builder /app/ ./ +WORKDIR /app/apps/${APP_DIRNAME} + +ARG PORT=3000 +ENV PORT=${PORT} +EXPOSE ${PORT} + +CMD ["pnpm", "start"] + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..b1762dc3 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,41 @@ +version: "3.7" + +services: + web: + ports: + - "3000:3000" + build: + context: . + dockerfile: ./apps/web/Dockerfile + args: + NEXT_PUBLIC_KAN_ENV: ${NEXT_PUBLIC_KAN_ENV} + NEXT_PUBLIC_UMAMI_ID: ${NEXT_PUBLIC_UMAMI_ID} + NEXT_PUBLIC_BASE_URL: ${NEXT_PUBLIC_BASE_URL} + NEXT_PUBLIC_STORAGE_URL: ${NEXT_PUBLIC_STORAGE_URL} + NEXT_PUBLIC_AVATAR_BUCKET_NAME: ${NEXT_PUBLIC_AVATAR_BUCKET_NAME} + env_file: + - .env + command: ["pnpm", "start"] + environment: + - WEBSITE_URL=${WEBSITE_URL} + - EMAIL_FROM=${EMAIL_FROM} + - EMAIL_URL=${EMAIL_URL} + - EMAIL_TOKEN=${EMAIL_TOKEN} + - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET} + - BETTER_AUTH_URL=${BETTER_AUTH_URL} + - BETTER_AUTH_TRUSTED_ORIGINS=${BETTER_AUTH_TRUSTED_ORIGINS} + - STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY} + - STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET} + - POSTGRES_URL=${POSTGRES_URL} + - GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID} + - GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET} + - S3_ACCESS_KEY_ID=${S3_ACCESS_KEY_ID} + - S3_SECRET_ACCESS_KEY=${S3_SECRET_ACCESS_KEY} + - S3_REGION=${S3_REGION} + - S3_ENDPOINT=${S3_ENDPOINT} + - NEXT_PUBLIC_KAN_ENV=${NEXT_PUBLIC_KAN_ENV} + - NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL} + - NEXT_PUBLIC_STORAGE_URL=${NEXT_PUBLIC_STORAGE_URL} + - NEXT_PUBLIC_AVATAR_BUCKET_NAME=${NEXT_PUBLIC_AVATAR_BUCKET_NAME} + - NEXT_PUBLIC_STORAGE_DOMAIN=${NEXT_PUBLIC_STORAGE_DOMAIN} + - NEXT_PUBLIC_UMAMI_ID=${NEXT_PUBLIC_UMAMI_ID}