Skip to content

Docker Image & Container

How images are built

Frontend and backend are separate images, both built from the build: blocks in docker-compose.yml; neither is pulled from an external registry.

Image Dockerfile Build context
Backend iqvflex/backend/Dockerfile ./iqvflex/backend
Frontend dashboard/Dockerfile ./dashboard

Backend image

node:22-alpine based, multi-stage build (base → deps → runtime):

  • The deps stage copies only package.json / package-lock.json and runs npm ci --omit=dev for production dependencies (this layer is reused across rebuilds when only the source changes — fast rebuilds).
  • The runtime stage copies node_modules, app.js, server.js, src/; the container does not run as root (USER node). EXPOSE 3001.
  • The in-container HEALTHCHECK uses the same logic as the install/update scripts: a real HTTP 200 + data.status === 'ok' (/health).

Frontend image

Two stages: node:22-alpine (build) for the build, nginx:1.27-alpine (runtime) to serve it. The runtime image contains no Node.js or source code (small, narrow attack surface).

  • The build stage runs npm ci (including devDependencies — vite/typescript are needed for the build), then npm run build (tsc && vite build) with the VITE_API_BASE_URL build-arg (empty in Docker).
  • The runtime stage copies only the dist/ output, nginx.conf and proxy_params.conf. EXPOSE 80.
  • The in-container HEALTHCHECK: wget against http://127.0.0.1/__nginx_health (nginx's own endpoint, not proxied to the backend).

Production image approach

The same images are used in both development and production; production hardening (read-only filesystem, closed ports, resource limits) is applied not at the image level but through the docker-compose.prod.yml override (see Docker Architecture).

Image tag / version logic

image: ${COMPOSE_PROJECT_NAME:-iqvflex}-backend:${VERSION:-latest}
image: ${COMPOSE_PROJECT_NAME:-iqvflex}-frontend:${VERSION:-latest}

If the VERSION environment variable is not set, images are built with the latest tag. install.sh / update.sh read the repository root VERSION file (canonical SemVer version, e.g. 1.0.0) and export it as the VERSION environment variable — this is how the image tag stays in sync with the repository version. Versions in dashboard/package.json and iqvflex/backend/package.json are component-level and are not used in the image tag.