Nginx Serving Compressed Multiple Static Files w/ Unique Paths using Docker for Improved Performance ⏩ ⏩ ⏩
Tags: nginx, docker, webdev, devops
Published: 2021-06-06 20:00:53



> In this setup we will configure the <strong>nginx</strong> server to serve multiple static files on different paths with gzip, media, js and css enabled. This scenario may fit well with Microfrontends (MFE) architechture where each individual route may serve a complete isolated application's production build version and by implementing the concept we will discuss here in this article you will also gain performance improvement.
> This is a simple starter guide on how to get you up and running and in case you should want to implement this kind of setup, you will have to add in your additional custom configurations that suits your needs best.

## 📓 📓 📓 Prerequisites 📓 📓 📓

- [Visual Studio Code](https://code.visualstudio.com/)
- [Docker](https://docs.docker.com/get-docker/)
- [Nginx](https://hub.docker.com/_/nginx)
- [Bash](https://git-scm.com/downloads/)

## 🏃🏃🏃 Getting started guide 🏃🏃🏃

> _we will be working with vscode and the integrated bash terminal on windows 10_

- open vscode

- open bash integrated termianl

```sh
  ctrl + shift + `
```

or you can choose the <strong>'view'</strong> tab in the menu and then click on <strong>'Terminal'</strong>

- create a project folder and cd into it

```sh
  $ mkdir <project_name> && cd <project_name>
```

- create a Dockerfile file

```sh
  $ touch Dockerfile
```

you will excuse me if i skip the .dockerignore file as we are not focusing on Docker here but rather on what we want to achieve with nginx.

- create a nginx.conf file

```sh
  $ touch nginx.conf
```

> We now will take a look on the overview of the configuration files and for a more detailed information we will have towards the end of the article or you can read the repo as well.

---

## Dockerfile file content description

```docker
# base image to build on: we use a lightweight image
FROM nginx:alpine

# You may want to clear defaults on producion server - set working directory to nginx asset directory and then remove default nginx static assets; uncomment these next 2 lines
# WORKDIR /usr/share/nginx/html
# RUN rm -rf ./*

# repeat this line for each application you fancy serving on different route
COPY ./<app_build_dir> ./usr/share/nginx/html/<app_build_dir>

# override the default nginx configuration file with our own
COPY ./nginx.conf ./etc/nginx/conf.d/default.conf

# expose the port your server should support (80/443..)
EXPOSE <port>
```

<div style="color:red; font-weight: bold;"> Pay attention: If you come from any Nginx background with Docker, do not add this command in the Dockerfile file

```sh
ENTRYPOINT ["nginx", "-g", "daemon off;"]
```

The reason we don't add it is because we override the default configurations and we want to control the custom behaviour of our nginx server.

</div>

---

## nginx.conf file content description

```nginx
server {
    listen 80;                  # default port
    root /usr/share/nginx/html; # absolute paths only
    gzip on;                    # enable compression


    # nginx default config
    location / {
        index index.html index.htm;
    }


    # repeat this block as many times as you need
    # my application page
    location /<my_app> {
        index index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

    # set up gzip config
    # set up assets (javascript, css, media files)
}
```

---

## 💪💪💪 Adding in our applications 💪💪💪

> Before we move forward we would actually need to get our project(s) available for us to use and deploy on to the nginx server. We can use basically anything we want for serving html, css and javascript for example React or Angular, but i am not going to go and implement this extra step as this is out of focus, but rather mimic a production build of a frontend framework by just creating 3 folders with an index.html file.

Again in the terminal pointing to the root of the project run these 2 commands:

```sh
    $ mkdir {Home,Profile,Dashboard}
```

```sh
    $ touch {Home,Profile,Dashboard}/index.html
```

Now open all the index.html files, add some html skeleton for home, profile and dashboard applications.

Here is an example. Just copy and paste it and don't forget to update the heading for each directory.

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Profile</title>
  </head>

  <body>
    <nav>
      <li>
        <a href="//localhost:8080/home/"> Home </a>
      </li>
      <li>
        <a href="//localhost:8080/profile/"> Profile </a>
      </li>
      <li>
        <a href="//localhost:8080/dashboard/"> Dashboard </a>
      </li>
    </nav>

    <h1>_Change_This_ page</h1>
  </body>
</html>
```

_I want to take a moment and explain what just happened in this last step. We are acting as we have 3 different frontend projects that we want to serve on 3 different routes. In this case we are just implementing the bare minimum for the static html because this article is not about the static assets but rather on how to use them with nginx._

---

## ⏰ ⏰ ⏰ Mini Recap ⏰ ⏰ ⏰

As for this stage we should have one project folder which contains 2 configuration files (Dockerfile and nginx.conf) and 3 folders (Home, Profile and Dashboard) with index.html files in them that represents a full blown production build application.

```sh
project
|
|_  Dockerfile
|_  nginx.conf
|
|___Home
|   |_  index.html
|
|___Profile
|   |_  index.html
|
|___Dashboard
    |_  index.html
```

The only part that is missing here, before we can apply our settings are the exact contents of the Dockerfile and the nginx.conf files. Let's quickly see what they are.

---

## 🔨 🔨 🔨 Filling the missing pieces 🔨 🔨 🔨

### Dockerfile

```docker

    FROM nginx:alpine

    COPY ./Home ./usr/share/nginx/html/home
    COPY ./Profile ./usr/share/nginx/html/profile
    COPY ./Dashboard ./usr/share/nginx/html/dashboard

    COPY ./nginx.conf ./etc/nginx/conf.d/default.conf

    EXPOSE 80
```

### nginx.conf

```nginx

    server {
        listen          80;
        root            /usr/share/nginx/html;

        include         /etc/nginx/mime.types;
        default_type    application/octet-stream;

        gzip on;
        gzip_disable "msie6";

        gzip_vary on;
        gzip_proxied any;
        gzip_comp_level 6;
        gzip_buffers 16 8k;
        gzip_http_version 1.1;
        gzip_min_length 256;
        
        gzip_types  font/eot
                    font/otf
                    font/ttf
                    image/svg+xml
                    text/css
                    text/javascript
                    text/plain
                    text/xml
                    application/atom+xml
                    application/geo+json
                    application/javascript
                    application/x-javascript
                    application/json
                    application/ld+json
                    application/manifest+json
                    application/rdf+xml
                    application/rss+xml
                    application/xhtml+xml
                    application/xml;


        # home page
        location /home {
            index index.html index.htm;
            try_files $uri $uri/ /index.html =404;
        }

        # dashboard page
        location /dashboard {
            index index.html index.htm;
            try_files $uri $uri/ /index.html =404;
        }
        # profile page
        location /profile {
            index index.html index.htm;
            try_files $uri $uri/ /index.html =404;
        }

        # root - nginx default
        location / {
            index index.html index.htm;
        }


        # Javascript and CSS files
        location ~* \.(?:css|js)$ {
            try_files $uri =404;
            expires 1y;
            access_log off;
            add_header Cache-Control "public";
        }


        # Media: images, icons, video, audio, HTC
        location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
            expires 1M;
            access_log off;
            add_header Cache-Control "public";
        }


        # Any route containing a file extension (e.g. /devicesfile.js)
        location ~ ^.+\..+$ {
            try_files $uri =404;
        }
    }
```

---

## 🎹 🎹 🎹 Putting theory 2 practice 🎹 🎹 🎹

Now after we set up our Nginx server as for what and how it should behave, we will build our very own custom version of it and then run it to see that everything works as expected!

Let's open a terminal from inside the project root folder and run the next command. We would want to '[tag](https://docs.docker.com/engine/reference/commandline/tag/)' our custom image so we can address it by name and not by its id, to do it simply by adding the -t flag.

```sh
    $ docker build -t <my_custom_tag_image> .
```

- and for the docker less experienced ones, this is what you need to type and run, pay attention to spacing and the dot!

```sh
    $ docker build -t multi_nginx .
```

Next step is to run the image in a container and we will accomplish it by running the next command.

```sh
    $ docker run -p <out_port>:<in_port> <my_custom_tag_image>
```

---

Pay attention that we specify the our local port to a container's inner port which can be anything but defaults to 80 or 443, this is the exact port number we exposed inside the Dockerfile configuration file. (I used port 8080)

---

- and again for the docker less experienced ones, this is what you need to type and run

```sh
    $ docker run -p 8080:80 multi_nginx
```

> pay a little attention, if you do come from a docker background, to the fact we don't run the container in a [detached](https://docs.docker.com/engine/reference/commandline/run/#options) mode with the -d flag because we want to see the nginx server logs every route it should serve.

Our final step is to open a browser and browse to the container root path on [localhost:8080](http://localhost:8080/). In this demo we have 3 different paths that are isolated from one another [home](http://localhost:8080/home/), [profile](http://localhost:8080/profile/) and [dashboard](http://localhost:8080/dashboard/).

Thank you very much for staying all the way through this article and hope that you may have learned something. until the next time.


Stay tuned for next
Like, subscribe, comment and whatever ...
Goodbye