| Which package manager do you want to use? | npm |
| Do you want to run "npm run build" before starting server? | yes |
| What directory is your build output to? | dist |
| What command do you want to use to start the app? | npm run start |
| What port does your server listen on? | 8080 |
After completion, your project directory will contain the following new files:
```text
├── docker-angular-sample/
│ ├── Dockerfile
│ ├── .dockerignore
│ ├── compose.yaml
│ └── README.Docker.md
```
---
## Build the Docker image
The default Dockerfile generated by `docker init` serves as a solid starting point for general Node.js applications. However, Angular is a front-end framework that compiles into static assets, so we need to tailor the Dockerfile to optimize for how Angular applications are built and served in a production environment.
### Step 1: Improve the generated Dockerfile and configuration
In this step, you’ll improve the Dockerfile and configuration files by following best practices:
- Use multi-stage builds to keep the final image clean and small
- Serve the app using NGINX, a fast and secure web server
- Improve performance and security by only including what’s needed
These updates help ensure your app is easy to deploy, fast to load, and production-ready.
> [!NOTE]
> A `Dockerfile` is a plain text file that contains step-by-step instructions to build a Docker image. It automates packaging your application along with its dependencies and runtime environment.
> For full details, see the [Dockerfile reference](/reference/dockerfile/).
### Step 2: Configure the Dockerfile
Copy and replace the contents of your existing `Dockerfile` with the configuration below:
```dockerfile
# =========================================
# Stage 1: Build the Angular Application
# =========================================
# =========================================
# Stage 1: Build the Angular Application
# =========================================
ARG NODE_VERSION=22.14.0-alpine
ARG NGINX_VERSION=alpine3.21
# Use a lightweight Node.js image for building (customizable via ARG)
FROM node:${NODE_VERSION} AS builder
# Set the working directory inside the container
WORKDIR /app
# Copy package-related files first to leverage Docker's caching mechanism
COPY package.json package-lock.json ./
# Install project dependencies using npm ci (ensures a clean, reproducible install)
RUN --mount=type=cache,target=/root/.npm npm ci
# Copy the rest of the application source code into the container
COPY . .
# Build the Angular application
RUN npm run build
# =========================================
# Stage 2: Prepare Nginx to Serve Static Files
# =========================================
FROM nginxinc/nginx-unprivileged:${NGINX_VERSION} AS runner
# Use a built-in non-root user for security best practices
USER nginx
# Copy custom Nginx config
COPY nginx.conf /etc/nginx/nginx.conf
# Copy the static build output from the build stage to Nginx's default HTML serving directory
COPY --chown=nginx:nginx --from=builder /app/dist/*/browser /usr/share/nginx/html
# Expose port 8080 to allow HTTP traffic
# Note: The default NGINX container now listens on port 8080 instead of 80
EXPOSE 8080
# Start Nginx directly with custom config
ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"]
CMD ["-g", "daemon off;"]
```
> [!NOTE]
> We are using nginx-unprivileged instead of the standard NGINX image to follow security best practices.
> Running as a non-root user in the final image:
>- Reduces the attack surface
>- Aligns with Docker’s recommendations for container hardening
>- Helps comply with stricter security policies in production environments
### Step 3: Configure the .dockerignore file
The `.dockerignore` file tells Docker which files and folders to exclude when building the image.
> [!NOTE]
>This helps:
>- Reduce image size
>- Speed up the build process