Search code examples
angulardocker

Unable to serve Angular 17 website build with production using Docker


I'm trying to run an angular 17 (node 18) website using Docker and Docker Compose

This is the docker file

FROM node:18-alpine as build
WORKDIR /app
RUN npm i -g @angular/cli
COPY package*.json ./
RUN npm ci
COPY . ./
RUN ng build --configuration production

FROM node:18-alpine
EXPOSE 4200
# Remove default nginx website
RUN rm -rf /usr/share/nginx/html/*
# Copy built Angular app from the previous stage to Nginx directory
COPY --from=build /app/dist/web-site /usr/share/nginx/html

After a few hours of google and chatgpt I have tried running :

  • "ngingx -g dameon off;
  • "ng s"

..but none of that works since they are not recognized or not found

Just in case here my section of the Docker-compose file

  angular-app:
    build:
      context: ./web-site
      dockerfile: web-site.Dockerfile
    ports:
      - "4200:4200" # Map container port to host port
    networks:
      - cp_network
    depends_on:
      - dotnet-api

I have spent some hours searching for a solution but something is probably missing


Solution

  • As mentioned in the comments, the question mixes the way of how a server side rendered application is delivered (via node image) and how static files for a client side rendered application are delivered (via web server, for example nginx).

    Be aware that the following files are just a very simple setup for demonstration purposes and that the configuration and naming is close to the setup in the question. For a clean production setup it would require more configuration (like caching in the nginx configuration), better naming (web-site and angular-app are not very good names and mixing them makes it worse) and a more sophisticated directory structure. That said, to get the code up and running you can start with those files:

    Dockerfile

    FROM node:18-alpine as build
    WORKDIR /app
    COPY package*.json ./
    RUN npm ci
    COPY . ./
    RUN npm run ng -- build --configuration production
    
    
    FROM nginx:1.25.4-alpine
    COPY nginx.default.conf.template /etc/nginx/templates/default.conf.template
    EXPOSE 4200
    COPY --from=build /app/dist/web-site/browser /usr/share/nginx/html/web-site
    

    Notes:

    • If you use npm run ng -- build you don't have to install npm i -g @angular/cli globally in your build container. It just uses the local Angular CLI that is installed anyway via npm ci.
    • I am personally not really a fan of staged builds in docker files for node projects. I would run a node container with a bind-mount to create the build artifacts, saved to the file system, and then do a separate build for the docker image. This gives you the possibility to reuse the same installed node_modules to create the build artifact and use it for other things, like running ng serve or tests in a docker container. But however, that's another topic.

    nginx.default.conf.template

    server {
      listen       4200;
    
      root   /usr/share/nginx/html/web-site;
    
      location / {
        index index.html;
        try_files $uri $uri/ /index.html;
        expires -1;
      }
    }
    

    Notes:

    • Usually in the docker image you want to deliver through port 80 instead of 4200, but I kept it, to keep the setup close to the question.
    • Be aware, that if you use this image in a production environment you should have quite some more configuration. Especially cache headers and stuff like that.

    docker-compose.yaml

    version: '3.9'
    services:
      angular-app:
        build: .
        ports:
          - "4200:4200"
    

    Notes:

    • Be aware that the example uses for the Dockerfile the default filename instead of the web-site.Dockerfile that was used in the setup of the question. This is just to keep the build configuration more simple. However, I would anyway suggest to always use Dockerfile as filename and if you need multiple of them, splitting them up in separate directories instead of using prefixes.

    For this specific example all files should be in the root of a standard Angular CLI workspace.

    enter image description here

    Build and run

    $ docker compose build
    $ docker compose up angular-app
    

    Note: You can also use docker-compose if you're using the non integrated docker compose

    docker-compose vs docker compose
    Unlike Compose V1, Compose V2 integrates into the Docker CLI platform and the recommended command-line syntax is docker compose.