Skip to content

Docker Images

Purpose

Build, tag, and inspect the two production images the whole rest of this portal (Kubernetes, CI/CD, Deployment) deploys.

Prerequisites

Docker Desktop running (docker version succeeds).

Files involved

backend/Dockerfile, backend/.dockerignore, frontend/Dockerfile, frontend/.dockerignore, frontend/nginx.conf.

Backend image (akshayabazaar-backend)

  • Build stage: mcr.microsoft.com/dotnet/sdk:8.0 - restores only the four .csproj files first (cached layer), then copies src/ and runs dotnet publish --no-restore.
  • Runtime stage: mcr.microsoft.com/dotnet/aspnet:8.0 - installs wget (for the HEALTHCHECK), creates appuser pinned to uid 1000, copies the published output owned by that user, runs as appuser.
  • Environment variables baked in: ASPNETCORE_URLS=http://+:8080, ASPNETCORE_ENVIRONMENT=Production (overridden per-environment at deploy time via Helm/Compose).
  • Exposed port: 8080.
  • HEALTHCHECK: wget -qO- http://localhost:8080/health every 30s, 10s timeout, 3 retries, 30s start period.
  • .dockerignore: excludes **/bin/, **/obj/, **/.vs/, **/*.user, .git.
docker build -f backend/Dockerfile -t akshayabazaar-backend:local `
  --build-arg VCS_REF=$(git rev-parse --short HEAD) `
  --build-arg BUILD_VERSION=0.0.0-dev `
  --build-arg BUILD_DATE=$(Get-Date -AsUTC -Format "yyyy-MM-ddTHH:mm:ssZ") `
  ./backend

Frontend image (akshayabazaar-frontend)

  • Build stage: node:22-alpine - npm ci (cached layer, fails loudly if package-lock.json is out of sync with package.json), then npm run build with VITE_API_URL baked in as a build arg (relative /api in every environment - see CI/CD → Jenkins "promote, don't rebuild").
  • Runtime stage: nginx:alpine - removes the default nginx assets, copies the built dist/, installs this repo's own nginx.conf (SPA routing + /api/* proxy to the backend), runs as the image's own built-in nginx user (uid 101).
  • Exposed port: 80.
  • HEALTHCHECK: wget -qO- http://127.0.0.1:80 every 30s, 10s timeout, 3 retries.
  • .dockerignore: excludes node_modules, dist, coverage, .env* (except .env.example), .git.
docker build -f frontend/Dockerfile -t akshayabazaar-frontend:local `
  --build-arg VITE_API_URL=/api `
  ./frontend

Why Node 22, not 20, for the build stage

jsdom@^30 (a test-only devDependency) transitively requires @asamuzakjp/css-color and @asamuzakjp/dom-selector, both pinned to node: "^22.13.0 || >=24.0.0". This only affects the build stage - the nginx runtime stage never runs Node at all. Dockerfile.dev (below) still uses node:20-alpine, since it never runs npm run build/tests, only the Vite dev server.

Dev images (Dockerfile.dev) - not deployed anywhere

  • backend/Dockerfile.dev: dotnet watch run for hot reload, used only by docker-compose.dev.yml.
  • frontend/Dockerfile.dev: npm install && npm run dev -- --host, same.

These are never built by CI/Jenkins and never referenced by the Helm chart - local development only.

Tagging strategy

Context Tag pattern Example
Local build :local akshayabazaar-backend:local
CI/Jenkins immutable short commit SHA akshayabazaar-backend:a1b2c3d4e5f6
Environment-moving pointer (human browsing only, never deployed with) :dev-latest / :staging-latest akshayabazaar-backend:staging-latest
Release matching git tag akshayabazaar-backend:v1.2.0

"Never rely only on :latest" - the Helm chart's own _helpers.tpl refuses to render at all if backend.image.tag/frontend.image.tag is left empty, specifically to prevent an accidental floating-tag deploy.

Validation

docker images | Select-String akshayabazaar
docker run --rm akshayabazaar-backend:local dotnet ECommerce.API.dll --help  # sanity-check the entrypoint

Troubleshooting

See Docker → Troubleshooting.

Security considerations

  • Neither runtime image runs as root.
  • No secrets are ever baked into an image - ConnectionStrings__*, Jwt__SecretKey, provider API keys are all runtime environment variables / Kubernetes Secrets, never ARG/ENV with a real value in the Dockerfile.
  • .dockerignore on both images excludes .git, .env* - a build context never leaks source control metadata or a local .env file into a layer.