# Build context must be the repo root (not server/), since this also needs # the client/ folder: docker build -f server/Dockerfile -t watchparty . # # Two stages: the first compiles both TypeScript projects (server/src -> JS # via tsc, client/src -> a bundled JS+CSS via esbuild - see # docs/architecture.md's TypeScript-migration section for why two separate # toolchains rather than one shared build), the second is the actual # runtime image, holding only the compiled output plus production # dependencies - none of the TypeScript/esbuild toolchain itself ships. FROM node:20-alpine AS build WORKDIR /app COPY server/package.json server/package-lock.json* ./server/ RUN cd server && npm install COPY server/tsconfig.json ./server/ COPY server/src ./server/src RUN cd server && npm run build COPY client/package.json client/package-lock.json* ./client/ RUN cd client && npm install COPY client/build.mjs ./client/ COPY client/tsconfig.json ./client/ COPY client/src ./client/src RUN cd client && npm run build FROM node:20-alpine # ffmpeg - TranscodeService spawns it directly (see server/src/services/ # TranscodeService.ts) to burn PGS/image subtitles into video ourselves, # the one gap Emby's own transcode + this app's client-side # rendering can't close between them. Unlike Emby's own text-subtitle # burn-in, this needs no fontconfig/font packages at all - a PGS overlay # is bitmap compositing, not text rendering. RUN apk add --no-cache ffmpeg WORKDIR /app COPY server/package.json server/package-lock.json* ./server/ RUN cd server && npm install --omit=dev COPY --from=build /app/server/dist ./server/dist COPY --from=build /app/client/dist ./client/dist WORKDIR /app/server ENV PORT=3000 # Static watch parties' JSON file (StaticPartyStore) - mount a volume here # (docker-compose.yml does), or they're lost with the container. ENV DATA_DIR=/data RUN mkdir -p /data EXPOSE 3000 CMD ["node", "dist/index.js"]