Dockerized Angular App with Nginx - NgDocker

Zaki Mohammed Zaki Mohammed
Mar 26, 2023 | 4 min read | 1848 Views | Comments

Meet super friendly NgDocker app, made with love and care using Angular and Cirrus UI, garnished with Docker and Nginx. In this article, we will explore how to make and bake a Dockerized Angular App with Nginx without much hassle.

If we already know about wonders Docker brings to the table then we are good to go ahead with the article, but if we have any doubt related to Docker (what, why and how) then the pre-requisite here is to know the background details of Docker. Here, we will talk about how to setup a minimalist Dockerized Angular application along with Nginx configurations. The idea is to keep it dead simple and explore what things are needed to make Angular App as Dockerized. Enough said lets get started with the code already!

We will head in this direction:

  1. Setup Angular App
  2. Add Production Build Command
  3. Setup Docker and Nginx
  4. Build Docker Image and Run Docker Container

Setup Angular App

Let us first do the daily chores of any Angular app and as per the need of the hour we are considering v15 of Angular; just for fun we are using Cirrus UI:

ng new ng-docker-mark-1

npm i cirrus-ui

As already mentioned we will do bare minimum with the Angular project:

app.component.html

<section class="section">
  <div class="hero fullscreen bg-gray-100">
    <div class="hero-body">
      <div class="content">
        <div class="row">
          <div class="col text-center">
            <h1>?? Hello <span class="text-red-700">Ng</span><span class="text-blue-500">Docker</span>!</h1>
            <h6 class="font-alt font-light">
              A webpage powered by
              <b>
                <i class="fab fa-angular text-red-700"></i> Angular + <i class="fab fa-docker text-blue-500"></i> Docker
                + <i class="fas fa-cloud text-pink-600"></i> Cirrus.
              </b>
            </h6>
            <div class="tag-container group-tags group-tags--rounded mt-2">
              <div class="tag tag--dark">Mark</div>
              <div class="tag tag--info">1.0.0</div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>

Add Production Build Command

Adding the build command for production in "package.json" file

package.json

{
  "scripts": {
    "build:prod": "ng build --configuration production",
  },
}

Setup Docker and Nginx

Before we address the Docker whale in the project, we first create Nginx configuration file, later this configuration file will overrides the default Nginx configuration. Create a folder for this file named "nginx" and create a file named "nginx.conf" inside that folder.

nginx/nginx.conf

server {
    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;
    }
}

Through this file we declare the re-write rule for the SPA application. The line try_files $uri $uri/ /index.html; makes the route to always be redirected to the "index.html", since the SPA serves everything through the index page only. This file is very important to setup security headers, implement caching, enabling Gzip etc. Basically, this file is same as "web.config" file of ASP.NET project.

The time has come to create our own "Dockerfile" and surprisingly the name of the file is itself "Dockerfile". Create this file on your root of Angular project directory.

Dockerfile

FROM node:latest as build-env
WORKDIR /app

ADD package.json .

RUN npm install

ADD . .

RUN npm run build:prod

FROM nginx:alpine

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

EXPOSE 8080

Here, when I first seen this file, I got little nervous, like what kind of Egyptian literature is this. But seems pretty simple to digest and get along with, let me break it down. This can be simply broken down into 2 major steps:

Step 1: Installing Dependencies and Building Project

FROM node:latest as build-env

This tells that the image will be for Node project, since most of the SPA are NPM and Node dependent, this line acknowledges the same.

WORKDIR /app

Here, we are naming our working directory as "/app".

ADD package.json .

Now, we are first adding only the "package.json" file to the "/app" directory using ADD command, this is to reduce the adding cost, and if the install command fails it fails early without adding the entire project.

RUN npm install

Now, we are installing the NPM packages based on the configured "package.json" file with the help of RUN command.

ADD . .

Once the package installation is succeeded and after doing yay! we then add all of the project files to the working directory "/app".

RUN npm run build:prod

Post that we run the build prod command, the same command which we have added to "package.json" script.

Step 2: Setup Nginx and Nginx Configuration

COPY --from=build-env /app/dist/ng-docker-mark-1 /usr/share/nginx/html

Here, we copy the built Angular project to Nginx HTML directory. This makes the project ready to be accessed.

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

Now, we will override the default Nginx configuration to the configuration that we have created in our Angular project "nginx/nginx.conf".

EXPOSE 8080

Finally, exposing it to the default port 8080.

Build Docker Image and Run Docker Container

We can take further steps if we have installed Docker Desktop already, otherwise we are unworthy to proceed.

Build Docker Image

# build image
docker build -t ng-docker:mark-1 .

Once, executed then check in the image list:

# list images
docker image ls

Run Docker Container

# run container
docker run -p 3300:80 --name ng-docker-container-1 ng-docker:mark-1

Here, you can change to your favorite port, we are going with "3300" for a change. Once, executed then open `http://localhost:3300/` to run the application.

Here, have some additional commands if failed in between; to stop and remove the existing container to create another one with same name.

# stop container
docker stop ng-docker-container-1

# remove container
docker rm ng-docker-container-1

Zaki Mohammed
Zaki Mohammed
Learner, developer, coder and an exceptional omelet lover. Knows how to flip arrays or omelet or arrays of omelet.