How to deploy Angular on Kubernetes
Deploying an Angular application to Kubernetes is a standard requirement for modern, scalable web applications.
With over 25 years of experience in software development and as the creator of CoreUI, I have architected and deployed numerous enterprise-grade frontend applications using container orchestration.
The most efficient and modern approach involves creating a multi-stage Docker build to compile your app and then serving the static assets via Nginx within a Kubernetes cluster.
This method ensures your production environment is lightweight, secure, and easily manageable through declarative configuration files.
Containerize your Angular application using Nginx and deploy it using Kubernetes Deployment and Service manifests.
Building the Docker Image
The first step is creating a Dockerfile that uses a multi-stage build. This separates the build environment from the runtime environment, significantly reducing the final image size and improving security.
# Stage 1: Build the Angular application
FROM node:20-alpine as build-step
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build -- --configuration=production
# Stage 2: Serve the application using Nginx
FROM nginx:1.25-alpine
# Angular 17+: output is in dist/my-angular-app/browser
# Angular 16 and earlier: output is in dist/my-angular-app
COPY --from=build-step /app/dist/my-angular-app/browser /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
In this setup, we first use a Node.js image to install dependencies and run the production build. Once the dist folder is generated, we copy only those static files into a lightweight Nginx image. This ensures your Kubernetes pods don’t carry unnecessary build tools or source code. Save the nginx.conf file in the root of your project alongside the Dockerfile so the COPY instruction can find it.
Configuring Nginx for Angular Routing
Angular is a Single Page Application (SPA), which means the client-side router handles navigation. Without a proper Nginx configuration, refreshing the page on any route other than the root will result in a 404 error.
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
This configuration tells Nginx to try serving the requested file as a static asset. If the file doesn’t exist (which is common with SPA routes), it redirects the request to index.html, allowing the Angular router to take over. This is a critical step for any Angular Dashboard Template deployment.
Defining the Kubernetes Deployment
The Deployment manifest defines the desired state for your application pods. It specifies which image to use, how many replicas should run, and how the containers should be configured.
apiVersion: apps/v1
kind: Deployment
metadata:
name: angular-app-deployment
labels:
app: angular-app
spec:
replicas: 3
selector:
matchLabels:
app: angular-app
template:
metadata:
labels:
app: angular-app
spec:
containers:
- name: angular-container
image: your-registry/angular-app:latest
ports:
- containerPort: 80
resources:
limits:
cpu: '500m'
memory: '512Mi'
requests:
cpu: '250m'
memory: '256Mi'
This YAML file ensures that three instances (replicas) of your Angular app are always running. It also includes resource limits to prevent a single pod from consuming all cluster resources. By using labels and selectors, Kubernetes manages the lifecycle of these pods efficiently.
Exposing the App with a Service
A Kubernetes Service provides a stable IP address and DNS name for your pods. Since pods are ephemeral and can be restarted with different IPs, the Service acts as a load balancer for internal or external traffic.
apiVersion: v1
kind: Service
metadata:
name: angular-app-service
spec:
selector:
app: angular-app
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
Using ClusterIP exposes the service on a cluster-internal IP. This is ideal when you intend to use an Ingress controller to manage external access. If you need direct external access without an Ingress, you could change the type to LoadBalancer.
Managing Traffic with Ingress
Ingress is the most professional way to expose your Angular application to the internet. It allows you to manage SSL termination, path-based routing, and host-based virtual hosting.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: angular-app-ingress
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
rules:
- host: my-angular-app.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: angular-app-service
port:
number: 80
tls:
- hosts:
- my-angular-app.com
secretName: angular-app-tls
The Ingress resource maps the external hostname my-angular-app.com to your internal Service. It also integrates with cert-manager to automatically provision and renew SSL certificates from Let’s Encrypt, ensuring your application is served over HTTPS.
Handling Configuration via ConfigMaps
Frontend applications often need different API URLs depending on the environment. Since Angular is compiled into static files, you can use a ConfigMap to inject a configuration file at runtime or use a script to replace placeholders.
apiVersion: v1
kind: ConfigMap
metadata:
name: angular-config
data:
env-config.json: |
{
"apiUrl": "https://api.production.com",
"enableFeatureX": true
}
By mounting this ConfigMap as a volume in your pod, you can make the JSON file available to your Nginx server. Your Angular application can then fetch this JSON file at startup to configure its services dynamically without needing to rebuild the Docker image for every environment.
Best Practice Note:
Always use health checks (liveness and readiness probes) in your Deployment manifests. This allows Kubernetes to know when your pod is ready to accept traffic or if it needs to be restarted. This is the same philosophy we follow in CoreUI to ensure high availability and reliability in production environments. For further optimization, consider using a CDN in front of your Kubernetes Ingress to cache static assets globally.



