DEV Community

Cover image for Nginx Serving Compressed Multiple Static Files w/ Unique Paths using Docker for Improved Performance ⏩ ⏩ ⏩
alxizr
alxizr

Posted on • Updated on

Nginx Serving Compressed Multiple Static Files w/ Unique Paths using Docker for Improved Performance ⏩ ⏩ ⏩

In this setup we will configure the nginx 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 📓 📓 📓

🏃🏃🏃 Getting started guide 🏃🏃🏃

we will be working with vscode and the integrated bash terminal on windows 10

  • open vscode

  • open bash integrated termianl

  ctrl + shift + `
Enter fullscreen mode Exit fullscreen mode

or you can choose the 'view' tab in the menu and then click on 'Terminal'

  • create a project folder and cd into it
  $ mkdir <project_name> && cd <project_name>
Enter fullscreen mode Exit fullscreen mode
  • create a Dockerfile file
  $ touch Dockerfile
Enter fullscreen mode Exit fullscreen mode

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
  $ touch nginx.conf
Enter fullscreen mode Exit fullscreen mode

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

# 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>
Enter fullscreen mode Exit fullscreen mode

Pay attention: If you come from any Nginx background with Docker, do not add this command in the Dockerfile file

ENTRYPOINT ["nginx", "-g", "daemon off;"]
Enter fullscreen mode Exit fullscreen mode

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.


nginx.conf file content description

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)
}
Enter fullscreen mode Exit fullscreen mode

💪💪💪 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:

    $ mkdir {Home,Profile,Dashboard}
Enter fullscreen mode Exit fullscreen mode
    $ touch {Home,Profile,Dashboard}/index.html
Enter fullscreen mode Exit fullscreen mode

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.

<!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>
Enter fullscreen mode Exit fullscreen mode

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.

project
|
|_  Dockerfile
|_  nginx.conf
|
|___Home
|   |_  index.html
|
|___Profile
|   |_  index.html
|
|___Dashboard
    |_  index.html
Enter fullscreen mode Exit fullscreen mode

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


    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
Enter fullscreen mode Exit fullscreen mode

nginx.conf


    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;
        }
    }
Enter fullscreen mode Exit fullscreen mode

🎹 🎹 🎹 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' our custom image so we can address it by name and not by its id, to do it simply by adding the -t flag.

    $ docker build -t <my_custom_tag_image> .
Enter fullscreen mode Exit fullscreen mode
  • and for the docker less experienced ones, this is what you need to type and run, pay attention to spacing and the dot!
    $ docker build -t multi_nginx .
Enter fullscreen mode Exit fullscreen mode

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

    $ docker run -p <out_port>:<in_port> <my_custom_tag_image>
Enter fullscreen mode Exit fullscreen mode

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
    $ docker run -p 8080:80 multi_nginx
Enter fullscreen mode Exit fullscreen mode

pay a little attention, if you do come from a docker background, to the fact we don't run the container in a detached 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. In this demo we have 3 different paths that are isolated from one another home, profile and 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

Oldest comments (3)

Collapse
 
vishalraj82 profile image
Vishal Raj

In case you start the docker containers to run in background, you can always use the command - docker logs -f to see the log output from the selected container. You can also specify multiple container ids / names.

Collapse
 
alxizr profile image
alxizr

Mate you're absolutely right, but you didn't pay attention. The focus is not on docker but the nginx machine and the configurations we fancy.

I could obviously choose a different method to demonstrate this for example using a linux VM and install Nginx from scratch with curl and what not.. Then this article would be a mile long.

Docker is a tool to make you more productive and get going as soon as possible. It allows you and others who may be less familiar with it to start working very quickly just by running some commands that i specified. I also added the nuances that if you do have some docker background then just take in mind why i chose this command over another.

Have a good week man 😎😎😎

Collapse
 
alxizr profile image
alxizr

Thanks mate! 😎

When i decided to write this piece, i had developers in mind who are less or maybe not so often have to deal with DevOps tasks.

stay tuned, couple more exciting pieces on their way ... 😲 😲 😲