angularlinuxdockernginxbrotli

How to enable Brotli compression in Nginx and Docker for Angular App


For Docker based Angular application I want to enable Brotli compression on Nginx for this I have added below commands to Dockerfile and turned brotli on in nginx/default.conf file.

nginx/default.conf:

server {
    brotli on;
    brotli_static on;

    listen 80;

    root /usr/share/nginx/html;

    location / {
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }
    
    error_page   500 502 503 504  /50x.html;
    
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

Dockerfile:

FROM node:16-alpine as build-env
WORKDIR /app

ADD package.json .

RUN npm install

ADD . .

RUN npm run build:prod

FROM nginx:alpine

RUN apk add nginx-mod-http-brotli

RUN sed -i '1i load_module /usr/lib/nginx/modules/ngx_http_brotli_filter_module.so;' /etc/nginx/nginx.conf
RUN sed -i '1i load_module /usr/lib/nginx/modules/ngx_http_brotli_static_module.so;' /etc/nginx/nginx.conf

COPY --from=build-env /app/dist/ng-docker-mark-2 /usr/share/nginx/html
COPY nginx/default.conf /etc/nginx/conf.d/default.conf

EXPOSE 8080

Here, I have added nginx:alpine after that installing nginx-mod-http-brotli which is an Apline Linux Package nginx-mod-http-brotli. Later adding load_module on top of /etc/nginx/nginx.conf file.

Now, the build command runs without any error:

docker build -t ng-docker:mark-3 .

But when I try to create and run the container get below error:

docker run -p 4300:80 --name ng-docker-mark-3-container-1 ng-docker:mark-3

Error:

/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: /etc/nginx/conf.d/default.conf differs from the packaged version
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2023/05/12 14:34:22 [emerg] 1#1: module "/usr/lib/nginx/modules/ngx_http_brotli_static_module.so" version 1022001 instead of 1023004 in /etc/nginx/nginx.conf:1
nginx: [emerg] module "/usr/lib/nginx/modules/ngx_http_brotli_static_module.so" version 1022001 instead of 1023004 in /etc/nginx/nginx.conf:1

Mainly it talks about this:

version 1022001 instead of 1023004

I have checked the container through Windows Docker Desktop app and I can see the modules got installed correctly, and its appearing in world file:

/etc/apk/world:

alpine-baselayout
alpine-keys
apk-tools
busybox
ca-certificates
curl
libc-utils
libintl
musl
nginx=1.23.4-r1
nginx-mod-http-brotli
nginx-module-geoip=1.23.4-r1
nginx-module-image-filter=1.23.4-r1
nginx-module-njs=1.23.4.0.7.11-r1
nginx-module-xslt=1.23.4-r1
tzdata

world file

Also, modules are appearing in the /usr/lib/nginx/modules folder too:

modules folder

What I tried already:


Solution

  • Struggled with this too. It is a mismatch between the compiled version of nginx and the module. The answer is to fetch them from the same source. So instead of using nginx:alpine, just use alpine

    Dockerfile

    # Build container
    FROM node:18-alpine AS builder
    
    # Make sure we got brotli
    RUN apk update
    RUN apk add --upgrade brotli
    WORKDIR /usr
    COPY package*.json ./
    COPY src ./src
    COPY public ./public
    RUN npm install
    RUN npm run build
    RUN cd /usr/dist && find . -type f -exec brotli {} \;
    
    # Actual runtime container
    FROM alpine
    RUN apk add brotli nginx nginx-mod-http-brotli
    
    # Minimal config
    COPY nginx/default.conf /etc/nginx/http.d/default.conf
    
    # Actual data
    COPY --from=builder /usr/dist /usr/share/nginx/html
    CMD ["nginx", "-g", "daemon off;"]
    EXPOSE 80
    

    Note: I only use brotli static since I created .br files in the build step.

    default.conf

    access_log /dev/stdout;
    error_log /dev/stdout info;
    
    server {
    
        brotli_static on;
    
        listen 80 default_server;
        listen [::]:80 default_server;
    
        # Everything is a 404
        root /usr/share/nginx/html;
        location / {
            try_files $uri $uri/ =404;
        }
    
        # You may need this to prevent return 404 recursion.
        location = /404.html {
            internal;
        }
    }
    

    You might want to play with more nginx settings.