nginxinc/nginx-unprivileged sets USER nginx internally, but the kubelet
cannot resolve non-numeric string usernames against OCI image config at
container-create time. With runAsNonRoot: true, K3s kubelet reports:
"container has runAsNonRoot and image will run as root"
Fix: explicitly add USER 101 after the COPY steps. UID 101 is the numeric
UID that nginx-unprivileged's nginx user already runs as — this instruction
just makes it visible in the final OCI image config layer so the kubelet
can verify non-root without username resolution.
Companion infra PR cartsnitch/infra#77 adds runAsUser: 101 as immediate
unblock while this Dockerfile change propagates through CI.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Switch from nginx:stable-alpine to nginxinc/nginx-unprivileged:stable-alpine.
The unprivileged image runs as nginx user (UID 101) on port 8080, satisfying
the runAsNonRoot: true security context in Kubernetes.
Fixes: https://github.com/cartsnitch/infra/issues/65
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Avoids Docker Hub 429 rate limits by pulling node:20-alpine and
nginx:stable-alpine from ghcr.io/cartsnitch/mirror/. GHCR login
now runs on all builds (not just main push) to authenticate pulls.
Ref: cartsnitch/infra#7, CAR-55
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Build stage uses node:20-alpine to install deps and build.
Prod stage uses nginx:stable-alpine to serve static assets.
Includes nginx config with SPA routing, gzip, health endpoint,
and aggressive caching for Vite-hashed assets.
Closes#6
Co-Authored-By: Paperclip <noreply@paperclip.ing>