Containerfile Standards
Shared skill — referenced by bc-gov-devops and ci-cd-pipeline. All container
images for BC Gov projects must follow these patterns.
Port Rule
Always expose port 8080 inside containers. Never 80, 443, 5000, or 5005.
OpenShift Routes and the AVI load balancer handle TLS externally.
.NET API Containerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /app
COPY . .
RUN dotnet publish -c Release -o /publish
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app
EXPOSE 8080
ENV ASPNETCORE_URLS=http://+:8080
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
COPY --from=build /publish .
USER appuser
HEALTHCHECK --interval=30s --timeout=5s \
CMD curl -f http://localhost:8080/health/live || exit 1
ENTRYPOINT ["dotnet", "<AppName>.dll"]
React / Vite Frontend Containerfile
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine AS runtime
EXPOSE 8080
RUN apk add --no-cache wget
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
USER appuser
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -q -O /dev/null http://localhost:8080/nginx-health || exit 1
Runtime Config — Frontend API URL
Never bake VITE_API_URL at build time. Use a /config.json served by Nginx
so one image works across dev, test, and prod.
Nginx location block
location = /config.json {
alias /usr/share/nginx/html/config.json;
add_header Cache-Control "no-cache";
}
React app fetch (main.jsx)
// top-level await requires build.target: 'esnext' in vite.config.js
const config = await fetch('/config.json').then(r => r.json()).catch(() => ({
apiUrl: 'http://localhost:5200',
}));
window.__env__ = config;
The Helm chart writes config.json into the container via a ConfigMap at deploy time.
Common Rules
- Non-root user
appuseron all containers —USER appuseras final instruction cap_drop: [ALL]andsecurity_opt: [no-new-privileges: true]in compose files- Install
curl(API) orwget(frontend) for health checks HEALTHCHECKrequired on every image pointing at/health/liveor/nginx-health