# Start with a base image and install system dependencies. # Pin to the production-known-good Node runtime. node:alpine floated to Node 26.2.0 # and broke bundled Vault provider initialization in the blue deployment. FROM node:26.1.0-alpine AS base RUN apk add --no-cache \ bash \ postgresql-client \ redis \ graphicsmagick \ imagemagick \ ghostscript \ curl \ nano WORKDIR /app # Stage for installing dependencies (cache-friendly) FROM base AS deps # Copy package files for both root and server parts of the project COPY package.json package-lock.json ./ # .npmrc carries legacy-peer-deps=true (next-auth@5 beta wants nodemailer ^7, # project uses ^8); without it this stage's `npm install` fails ERESOLVE on npm@11. COPY .npmrc ./ COPY server/package.json ./server/ COPY ee/server/package.json ./ee/server/ COPY tsconfig.base.json ./ COPY server/src/invoice-templates/assemblyscript ./server/src/invoice-templates/assemblyscript RUN npm install ## Ensure workspace installs for server and shared so node_modules exist per workspace RUN npm install --workspace=server --workspace=shared # Builder stage for compiling the application FROM deps AS builder # Build-only fallback secrets let NextAuth edge guards pass; real secrets are injected at runtime. ARG NEXTAUTH_SECRET_BUILD="dev-placeholder-nextauth-secret-32" ENV NEXTAUTH_SECRET=${NEXTAUTH_SECRET_BUILD} ENV AUTH_SECRET=${NEXTAUTH_SECRET_BUILD} # Copy all project files and build the server COPY . . WORKDIR /app # Full install AFTER `COPY . .` so every workspace package (packages/*, @product/*, # @alga-psa/*) is symlinked into node_modules. The deps-stage install ran before # those dirs existed, so a shared-only relink leaves @product/chat (and siblings) # unresolvable by the EE build (`Module not found: @product/chat/context`). RUN npm install # Build all upstream packages (shared + pre-built) via Nx (handles dependency ordering) RUN npx nx build-deps server ENV USE_PREBUILT="true" # Copy EE files and build enterprise edition COPY scripts/ ./scripts/ ENV NEXT_PUBLIC_EDITION=enterprise RUN ./scripts/build-enterprise.sh WORKDIR /app/server # Create secrets directory and populate with secure placeholder values RUN mkdir -p /app/secrets && \ echo "secure-admin-password-placeholder" > /app/secrets/postgres_password && \ echo "secure-app-password-placeholder" > /app/secrets/db_password_server && \ echo "secure-hocuspocus-password-placeholder" > /app/secrets/db_password_hocuspocus && \ echo "secure-redis-password-placeholder" > /app/secrets/redis_password && \ echo "secure-32char-auth-key-placeholder-xxxxx" > /app/secrets/alga_auth_key && \ echo "secure-32char-crypto-key-placeholder-xxxx" > /app/secrets/crypto_key && \ echo "secure-32char-token-key-placeholder-xxxx" > /app/secrets/token_secret_key && \ echo "secure-32char-nextauth-key-placeholder-xx" > /app/secrets/nextauth_secret && \ echo "secure-email-password-placeholder" > /app/secrets/email_password && \ echo "secure-oauth-client-id-placeholder" > /app/secrets/google_oauth_client_id && \ echo "secure-oauth-client-secret-placeholder" > /app/secrets/google_oauth_client_secret && \ echo "secure-gmail-client-id-placeholder" > /app/secrets/GOOGLE_CLIENT_ID && \ echo "secure-gmail-client-secret-placeholder" > /app/secrets/GOOGLE_CLIENT_SECRET && \ echo "secure-ms-client-id-placeholder" > /app/secrets/MICROSOFT_CLIENT_ID && \ echo "secure-ms-client-secret-placeholder" > /app/secrets/MICROSOFT_CLIENT_SECRET && \ chmod 600 /app/secrets/* # Copy example environment file and set Enterprise Edition COPY .env.example /app/.env COPY .env.example /app/server/.env RUN sed -i 's/NEXT_PUBLIC_EDITION=community/NEXT_PUBLIC_EDITION=enterprise/' /app/.env && \ sed -i 's/NEXT_PUBLIC_EDITION=community/NEXT_PUBLIC_EDITION=enterprise/' /app/server/.env WORKDIR /app RUN npm run build:ee # Final production image with minimal runtime artifacts FROM node:26.1.0-alpine RUN apk add --no-cache bash \ postgresql-client \ redis \ graphicsmagick \ imagemagick \ ghostscript \ curl \ nano \ chromium \ nss \ freetype \ freetype-dev \ harfbuzz \ ca-certificates \ ttf-freefont WORKDIR /app COPY tsconfig.base.json ./ COPY server/setup /app/server/setup COPY .env.example /app/.env COPY .env.example /app/server/.env RUN sed -i 's/NEXT_PUBLIC_EDITION=community/NEXT_PUBLIC_EDITION=enterprise/' /app/.env && \ sed -i 's/NEXT_PUBLIC_EDITION=community/NEXT_PUBLIC_EDITION=enterprise/' /app/server/.env # Copy built application and node_modules from earlier stages -- minimalist approach COPY --from=builder /app/shared ./shared COPY --from=builder /app/server/.next ./server/.next COPY --from=builder /app/server/public ./server/public COPY --from=builder /app/server/next.config.mjs ./server/ COPY --from=builder /app/server/tsconfig.json ./server/ COPY --from=builder /app/server/package.json ./server/ COPY --from=builder /app/package.json ./ COPY --from=builder /app/package-lock.json ./ COPY --from=builder /app/server/knexfile.cjs ./server/ COPY --from=builder /app/server/index.ts ./server/ COPY --from=builder /app/server/migrations/ ./server/migrations/ COPY --from=builder /app/server/seeds/ ./server/seeds/ COPY --from=builder /app/server/src/ ./server/src/ # server/scripts holds runtime entrypoints invoked at boot, notably # create-tenant.ts which the appliance bootstrap runs to seed the initial # tenant/admin. Without it the appliance install fails ERR_MODULE_NOT_FOUND. COPY --from=builder /app/server/scripts/ ./server/scripts/ COPY --from=builder /app/secrets ./secrets COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/server/node_modules ./server/node_modules # Copy EE migrations (they get merged during build-enterprise.sh) COPY --from=builder /app/ee/server/migrations/ ./server/migrations/ # Copy core package.json for version info COPY packages/core/package.json /app/packages/core/package.json COPY server/entrypoint.sh /app/entrypoint.sh RUN chmod +x /app/entrypoint.sh # Ensure tsx is available on PATH at runtime for `npm start` RUN npm install -g tsx # Create build timestamp for verification RUN echo "BUILD_TIME=$(date)" > /app/build-info.txt && \ echo "BUILD_EPOCH=$(date +%s)" >> /app/build-info.txt EXPOSE 3000 # Environment configuration ENV NODE_ENV=production ENV NEXT_PUBLIC_EDITION=enterprise # Puppeteer chromium configuration ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser # Secret provider configuration # Default configuration for composite secret system # Can be overridden in docker-compose or deployment environments ENV SECRET_READ_CHAIN="env,filesystem" ENV SECRET_WRITE_PROVIDER="filesystem" ENTRYPOINT ["/app/entrypoint.sh"]