- Published on
How to Serve an Angular Application with Nginx
- Authors
- Name
Nginx is a popular HTTP and reverse proxy server that runs on Linux and is often used to serve Angular applications. A common way to run Nginx is within a Docker container.
This article covers the minimum steps required to get this running on a local machine. You will need to have Docker installed on your machine if you want to follow along.
Create an Angular Application
Create a new Angular application using the following command:
ng new nginx-example-app
Build the Angular app by running:
npm run build
The building of the application can be done inside of the Docker container but we’re going to do it outside of the container for now.
Configure Nginx
The Docker image we will use has Nginx installed and ready to go. All we have to do is configure it. To do that we create a file in the root of the solution (where angular.json
is) named nginx.conf
with this content (which is explained with comments):
# the events block is required
events{}
http {
# include the default mime.types to map file extensions to MIME types
include /etc/nginx/mime.types;
server {
# set the root directory for the server (we need to copy our
# application files here)
root /usr/share/nginx/html;
# set the default index file for the server (Angular generates the
# index.html file for us and it will be in the above directory)
index index.html;
# specify the configuration for the '/' location
location / {
# try to serve the requested URI. if that fails then try to
# serve the URI with a trailing slash. if that fails, then
# serve the index.html file; this is needed in order to serve
# Angular routes--e.g.,'localhost:8080/customer' will serve
# the index.html file
try_files $uri $uri/ /index.html;
}
}
}
Configure Docker
Create a file in the root of the solution (where angular.json
is) named Dockerfile
(no extension) with this content (which is explained line-by-line with comments):
# use the latest version of the official nginx image as the base image
FROM nginx:latest
# copy the custom nginx configuration file to the container in the
# default location
COPY nginx.conf /etc/nginx/nginx.conf
# copy the built Angular app files to the default nginx html directory
COPY /dist/nginx-example-app /usr/share/nginx/html
# the paths are relative from the Docker file
Build the Docker Image
There are two basic entities in docker — images
and containers
. The container
is like a server and the image
contains instructions for building that server. To borrow OOP terminology, the image
is like the class and the container
is like the instantiation of that class.
VS Code has some helpful tools that allow you to build images
and run containers
using a GUI, but we’ll use the command line here. First, we build the image:
docker build -t nginx-example-app-docker-image .
This command simply builds the image.
The -t nginx-example-app-docker-image
portion names the image.
The period
at the end specifies the current directory (where the Docker file is) as the build context.
Run the Docker Container
Next run the container which will use the image, as follows:
docker run --name nginx-example-app-docker-container -d -p 8080:80 nginx-example-app-docker-image
The --name nginx-example-app-docker-container
portion species the name of the container.
The -d
flag tells docker to run in detached mode (in the background).
The -p 8080:80
flag maps port 8080 to 80. Inside the container, Nginx exposes the website on port 80 which is the default, and we did not have to specify that. Here we are saying that external clients can hit port 8080 on the container to access the website.
The last argument is the name of the image to run: nginx-example-app-docker-image
. Remember, this is what we named the image in the previous section.
You should now be able to open a browser to http://localhost:8080 and see the default Angular scaffolding.
Build the Application in Docker
It’s common to let Docker build the Angular application. We can do that by modifying the Dockerfile
with this content (which is explained line-by-line with comments):
# use a node image as the base image and name it 'build' for
# later reference
FROM node:18.18-alpine3.18 as build
# set the working directory to /app
WORKDIR /app
# copy the current directory contents into the container at /app
COPY . .
# install dependencies, matching package-lock.json
RUN npm ci
# build the app
RUN npm run build
# Use the latest version of the official Nginx image as the base image
FROM nginx:latest
# copy the custom nginx configuration file to the container in the default
# location
COPY nginx.conf /etc/nginx/nginx.conf
# copy the built application from the build stage to the nginx html
# directory
COPY --from=build /app/dist/nginx-example-app /usr/share/nginx/html
# The above commands build the Angular app and then configure and build a
# Docker image for serving it using the nginx web server.
Our Docker file now uses what are called stages. Building the application will be the first stage and we name it build
so that we can reference it later.
Notice that for the build stage, we are starting from the node:18.18-alpine3.18
image. How did I choose this image?
We need an image that has node installed and I decided to use version 18. So, I went to the node page on the Docker hub and searched for 18-alpine. The alpine
variant uses the Alpine Linux image, which is smaller and contains everything we need.
The search returned many images
, and I chose the newest one:
The rest of the changes are documented in the updated Docker file.
There is one last thing to do though. Currently, the Docker file is copying all of our local files into the container, and we don’t need to do that. Let’s create a .dockerignore
file in the root (where Dockerfile
is) with the files we want to ignore, which will speed things up:
dist node_modules
Rebuilding and Rerunning
If you still have the container running, you will need to remove the container before rebuilding the image and running a new container. Let’s add some tasks to package.json
to make this easier:
"build-image": "docker build -t nginx-example-app-docker-image .",
"remove-image": "docker rmi nginx-example-app-docker-image",
"run-container": "docker run --name nginx-example-app-docker-container -d -p 8080:80 nginx-example-app-docker-image",
"remove-container": "docker rm -f nginx-example-app-docker-container",
"containerize": "npm run build-image && npm run run-container",
"recontainerize": "npm run remove-container && npm run build-image && npm run run-container"
With these tasks in place, I can now do the following:
npm run recontainerize
This will remove the running container, rebuild the image (which now rebuilds the application), and start a new container using the updated image. Now you can refresh the browser to see the updated running application.
That’s it! I hope you found this useful.
Bibliography
- Beginner’s Guide (nginx.org)
- nginx — Official Image | Docker Hub
- Module ngx_http_core_module (nginx.org)
- Example nginx configuration
- http://nginx.org/en/docs/ngx_core_module.html#include
- Module ngx_http_index_module (nginx.org)
- Overview of the get started guide | Docker Docs
- node — Official Image | Docker Hub
- https://hub.docker.com/_/alpine