Engineering

Deploy your Create React App with Docker and Nginx

No items found.
First thank you to Simona Cotin and Super Diana for answering the noob docker and nginx questions and reminding me that nginx is better for static files than node. Also thank you to the amazing humans who built a Docker extension for VSCode.

One of my main frustrations when it comes to Create React App, or really any frontend app that is served statically without a server backing it, is that your routes are now gone. You can access them from inside the app and go from page to page but if you try to access a page directly well… This happens: https://isthereuber.in/leiria

This is not okay, I know the website is static but the user doesn’t need to know.

Nginx to the Rescue

We will now need to create a nginx configuration for our server. The reason I choose nginx over node is mostly because it has been proven to be faster for static assets but you can totally do this with node.

A configuration file for nginx is a nginx_.conf_ file so let’s start by creating that file and start coding it:

# auto detects a good number of processes to run
worker_processes auto;

#Provides the configuration file context in which the directives that affect connection processing are specified.
events {
    # Sets the maximum number of simultaneous connections that can be opened by a worker process.
    worker_connections 8000;
    # Tells the worker to accept multiple connections at a time
    multi_accept on;
}


http {
    # what times to include
    include       /etc/nginx/mime.types;
    # what is the default one
    default_type  application/octet-stream;

    # Sets the path, format, and configuration for a buffered log write
    log_format compression '$remote_addr - $remote_user [$time_local] '
        '"$request" $status $upstream_addr '
        '"$http_referer" "$http_user_agent"';

    server {
        # listen on port 80
        listen 80;
        # save logs here
        access_log /var/log/nginx/access.log compression;

        # where the root here
        root /var/www;
        # what file to server as index
        index index.html index.htm;

        location / {
            # First attempt to serve request as file, then
            # as directory, then fall back to redirecting to index.html
            try_files $uri $uri/ /index.html;
        }

        # 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";
        }

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

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

This is a pretty standard configuration for nginx and we tell it where to show our files and what the cache expiration date is. There is also some helpers for 404 and logs.

We don’t enable gzip here but for an example with it you can look here.

Docker time

Now that we have our nginx config we can now create our Dockerfile and we will start by stating what is the base image we will be using:

FROM nginx:1.15.2-alpine

After this we need to tell docker what it needs to run our app and in this case we need to do three things:

  • Copy the build folder over to /var/www
  • Copy the nginx.conf to it’s folder in /etc/nginx/
  • Expose the port 80 to the public since that is the one we are using in the nginx config

Details on how to do this are below:

FROM nginx:1.15.2-alpine
COPY ./build /var/www
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80

Now to finish our Dockerfile we need to tell it what command to run and for that we will the nginx cli and pass it as the entrypoint in our Dockerfile:

FROM nginx:1.15.2-alpine
COPY ./build /var/www
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
ENTRYPOINT ["nginx","-g","daemon off;"]

As you can see we run nginx with -g daemon off; so that nginx stays in the foreground so that Docker can track the process properly (otherwise your container will stop immediately after starting). You can read more about this here.

You can now build and tag this image with:

docker build --rm -f Dockerfile -t rick-morty-random-episode:latest .

And then run it with:

docker run --rm -d -p 80:80 rick-morty-random-episode:latest

If you use now you can just deploy this as it is and now will build and run the image on their own servers.

We now have static routes on our static projects! If someone hits any route on your webapp this will be redirected to index.html and your app will work flawlessly.

This is the webapp I used to test this approach: https://github.com/SaraVieira/rick-morty-random-episode

https://rick-morty-random-episode.now.sh/

Routes 🎉

Deploy your Create React App with Docker and Nginx
was originally published in YLD Blog on Medium.
Share this article: