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.csprojfiles first (cached layer), then copiessrc/and runsdotnet publish--no-restore. - Runtime stage:
mcr.microsoft.com/dotnet/aspnet:8.0- installswget(for theHEALTHCHECK), createsappuserpinned to uid 1000, copies the published output owned by that user, runs asappuser. - 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/healthevery 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 ifpackage-lock.jsonis out of sync withpackage.json), thennpm run buildwithVITE_API_URLbaked in as a build arg (relative/apiin every environment - see CI/CD → Jenkins "promote, don't rebuild"). - Runtime stage:
nginx:alpine- removes the default nginx assets, copies the builtdist/, installs this repo's ownnginx.conf(SPA routing +/api/*proxy to the backend), runs as the image's own built-innginxuser (uid 101). - Exposed port:
80. HEALTHCHECK:wget -qO- http://127.0.0.1:80every 30s, 10s timeout, 3 retries..dockerignore: excludesnode_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 runfor hot reload, used only bydocker-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¶
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, neverARG/ENVwith a real value in the Dockerfile. .dockerignoreon both images excludes.git,.env*- a build context never leaks source control metadata or a local.envfile into a layer.