Nginx Front End

A Dockerfile for the Static Front End with Nginx

The front end needs no build step: its image is the official nginx 75 :1.30-alpine (Nginx 1.30.5, the stable branch, 27 MB compressed) plus public/, built from web/Dockerfile with the project root as context:

web/Dockerfile: BookNest's front-end imageDockerfile
# syntax=docker/dockerfile:1
# BookNest front end: Nginx serves public/ and proxies /api/ to the API service.
FROM nginx:1.30-alpine
# The entrypoint renders /etc/nginx/templates/*.template with these variables.
ENV API_UPSTREAM=api:3000 \
    NGINX_ENTRYPOINT_LOCAL_RESOLVERS=1
COPY web/default.conf.template /etc/nginx/templates/
COPY public/ /usr/share/nginx/html/
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --start-interval=1s \
  CMD ["wget", "-q", "-O", "/dev/null", "http://127.0.0.1/healthz"]

At startup the image's entrypoint runs envsubst over /etc/nginx/templates/*.template, substituting only variables that exist in the environment, so Nginx's own $uri survives and the API's address becomes a run-time setting, as Kubernetes 5,150 (Kubernetes) will need:

web/default.conf.template: static files plus a proxy to the APIShell
server {
    listen 80;
    root /usr/share/nginx/html;
    resolver ${NGINX_LOCAL_RESOLVERS} valid=10s;
    set $api http://${API_UPSTREAM};
    location / {
        try_files $uri $uri/ /index.html;
    }
    location /api/ {
        proxy_pass $api;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    location = /healthz {
        access_log off;
        return 200 "ok\n";
    }
}

Under the hood: a plain proxy_pass http://api:3000; resolves api once, at startup; if up --build recreates the API on a different address, Nginx keeps the old one and answers 502 until it restarts. With the address in a variable, Nginx re-resolves it through resolver, caching each answer for 10 seconds, and NGINX_ENTRYPOINT_LOCAL_RESOLVERS=1 makes the entrypoint fill NGINX_LOCAL_RESOLVERS from the container's /etc/resolv.conf: Docker 514 's DNS server 127.0.0.11 here, the cluster DNS in Kubernetes, where Nginx needs the Service's full name because it ignores search domains. /healthz lets the healthcheck test the web tier alone.