Compartir comentarios
Las respuestas se generan en base a la documentación.

Containerizar una aplicación Angular

Requisitos previos

Antes de empezar, asegúrate de tener instaladas y disponibles las siguientes herramientas en tu sistema:

  • La última versión de Docker Desktop.
  • Un cliente git. Los ejemplos de esta sección usan git por línea de comandos, pero puedes usar cualquier cliente.

¿Nuevo en Docker?
Empieza con la guía Conceptos básicos de Docker para familiarizarte con conceptos clave como imágenes, contenedores y Dockerfiles.


Descripción general

Esta guía te recorre el proceso completo de containerizar una aplicación Angular con Docker. Aprenderás a crear una imagen Docker lista para producción siguiendo buenas prácticas que mejoran el rendimiento, la seguridad, la escalabilidad y la eficiencia del despliegue.

Al final de esta guía podrás:

  • Containerizar una aplicación Angular con Docker.
  • Crear y optimizar un Dockerfile para compilaciones de producción.
  • Usar compilaciones multi-etapa para reducir el tamaño de la imagen.
  • Servir la aplicación de forma eficiente con una configuración personalizada de Nginx.
  • Crear imágenes Docker seguras y mantenibles siguiendo buenas prácticas.

Obtener la aplicación de ejemplo

Clona la aplicación de ejemplo para usarla en esta guía. Abre una terminal, ve al directorio donde quieras trabajar y ejecuta el siguiente comando para clonar el repositorio git:

$ git clone https://github.com/kristiyan-velkov/docker-angular-sample

Generar un Dockerfile

Docker ofrece una herramienta CLI interactiva llamada docker init que ayuda a generar los archivos de configuración necesarios para containerizar tu aplicación. Incluye la generación de Dockerfile, .dockerignore, compose.yaml y README.Docker.md.

Para empezar, ve a la raíz del directorio del proyecto:

$ cd docker-angular-sample

Then run the following command:

$ docker init

Verás una salida similar a:

Welcome to the Docker Init CLI!

This utility will walk you through creating the following files with sensible defaults for your project:
  - .dockerignore
  - Dockerfile
  - compose.yaml
  - README.Docker.md

Let's get started!

La CLI te hará algunas preguntas sobre la configuración de tu aplicación. Para mantener la coherencia, usa las mismas respuestas del ejemplo siguiente cuando te lo pida:

PreguntaRespuesta
What application platform does your project use?Node
What version of Node do you want to use?24.12.0-alpine
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

Al terminar, el directorio del proyecto contendrá los siguientes archivos nuevos:

├── docker-angular-sample/
│ ├── Dockerfile
│ ├── .dockerignore
│ ├── compose.yaml
│ └── README.Docker.md

Compilar la imagen Docker

El Dockerfile por defecto generado por docker init es un buen punto de partida para aplicaciones Node.js en general. Sin embargo, Angular es un framework front-end que compila en recursos estáticos, así que debemos adaptar el Dockerfile para optimizar cómo se compilan y sirven las aplicaciones Angular en producción.

Paso 1: Mejorar el Dockerfile y la configuración generados

En este paso mejorarás el Dockerfile y los archivos de configuración siguiendo buenas prácticas:

  • Usar compilaciones multi-etapa para mantener la imagen final limpia y pequeña
  • Servir la aplicación con Nginx, un servidor web rápido y seguro
  • Mejorar rendimiento y seguridad incluyendo solo lo necesario

Estas mejoras ayudan a que tu aplicación sea fácil de desplegar, cargue rápido y esté lista para producción.

Note

Un Dockerfile es un archivo de texto plano con instrucciones paso a paso para compilar una imagen Docker. Automatiza el empaquetado de tu aplicación con sus dependencias y entorno de ejecución.
Para más detalles, consulta la referencia de Dockerfile.

Paso 2: Configurar el Dockerfile

Antes de crear un Dockerfile, debes elegir una imagen base. Puedes usar la imagen oficial de Node.js o una Docker Hardened Image (DHI) del catálogo de imágenes endurecidas.

Elegir DHI ofrece una imagen lista para producción, ligera y segura. Para más información, consulta Docker Hardened Images.

Important

Esta guía usa una etiqueta estable de imagen Node.js LTS considerada segura cuando se escribió la guía. Como se publican versiones y parches de seguridad con regularidad, la etiqueta mostrada puede dejar de ser la opción más segura cuando sigas la guía. Revisa siempre las etiquetas de imagen disponibles y elige una versión segura y actualizada antes de compilar o desplegar tu aplicación.

Imágenes Docker oficiales de Node.js: https://hub.docker.com/_/node

Las Docker Hardened Images (DHI) están disponibles para Node.js en el catálogo de Docker Hardened Images. Las Docker Hardened Images están disponibles gratis para todos, sin suscripción. Puedes hacer pull y usarlas como cualquier otra imagen Docker tras iniciar sesión en el registro DHI. Para más información, consulta la guía de inicio rápido de DHI.

  1. Inicia sesión en el registro DHI:

    $ docker login dhi.io
    
  2. Haz pull de la DHI de Node.js (consulta el catálogo para las versiones disponibles):

    $ docker pull dhi.io/node:24-alpine3.22-dev
    

En el siguiente Dockerfile, la instrucción FROM usa dhi.io/node:24-alpine3.22-dev como imagen base.

# =========================================
# Stage 1: Build the Angular Application
# =========================================

# Use a lightweight DHI Node.js image for building
FROM dhi.io/node:24-alpine3.22-dev 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 dhi.io/nginx:1.28.0-alpine3.21-dev AS runner

# 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

# Use a non-root user for security best practices
USER nginx

# 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;"]

Ahora debes crear un Dockerfile multi-etapa listo para producción. Sustituye el Dockerfile generado por la siguiente configuración optimizada:

# =========================================
# Stage 1: Build the Angular Application
# =========================================
ARG NODE_VERSION=24.12.0-alpine
ARG NGINX_VERSION=alpine3.22

# 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

# 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

# Use a built-in non-root user for security best practices
USER nginx

# 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

Usamos nginx-unprivileged en lugar de la imagen estándar de Nginx para seguir buenas prácticas de seguridad. Ejecutar como usuario no root en la imagen final:

  • Reduce la superficie de ataque
  • Se alinea con las recomendaciones de Docker para endurecer contenedores
  • Ayuda a cumplir políticas de seguridad más estrictas en entornos de producción

Paso 3: Configurar el archivo .dockerignore

El archivo .dockerignore indica a Docker qué archivos y carpetas excluir al compilar la imagen.

Note

Esto ayuda a:

  • Reducir el tamaño de la imagen
  • Acelerar el proceso de compilación
  • Evitar que archivos sensibles o innecesarios (como .env, .git o node_modules) se añadan a la imagen final.

Para más información, consulta la referencia de .dockerignore.

Copia y sustituye el contenido de tu .dockerignore existente por la configuración siguiente:

# ================================
# Node and build output
# ================================
node_modules
dist
out-tsc
.angular
.cache
.tmp

# ================================
# Testing & Coverage
# ================================
coverage
jest
cypress
cypress/screenshots
cypress/videos
reports
playwright-report
.vite
.vitepress

# ================================
# Environment & log files
# ================================
*.env*
!*.env.production
*.log
*.tsbuildinfo

# ================================
# IDE & OS-specific files
# ================================
.vscode
.idea
.DS_Store
Thumbs.db
*.swp

# ================================
# Version control & CI files
# ================================
.git
.gitignore

# ================================
# Docker & local orchestration
# ================================
Dockerfile
Dockerfile.*
.dockerignore
docker-compose.yml
docker-compose*.yml

# ================================
# Miscellaneous
# ================================
*.bak
*.old
*.tmp

Paso 4: Crear el archivo nginx.conf

Para servir tu aplicación Angular de forma eficiente dentro del contenedor, configurarás Nginx con una configuración personalizada. Está optimizada para rendimiento, caché del navegador, compresión gzip y enrutamiento del lado del cliente.

Crea un archivo llamado nginx.conf en la raíz del proyecto y añade el siguiente contenido:

Note

Para más información sobre la configuración de Nginx, consulta la documentación oficial de Nginx.

worker_processes auto;

pid /tmp/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    client_body_temp_path /tmp/client_temp;
    proxy_temp_path       /tmp/proxy_temp_path;
    fastcgi_temp_path     /tmp/fastcgi_temp;
    uwsgi_temp_path       /tmp/uwsgi_temp;
    scgi_temp_path        /tmp/scgi_temp;

    # Logging
    access_log off;
    error_log  /dev/stderr warn;

    # Performance
    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;
    keepalive_timeout  65;
    keepalive_requests 1000;

    # Compression
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_min_length 256;
    gzip_comp_level 6;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/javascript
        application/x-javascript
        application/json
        application/xml
        application/xml+rss
        font/ttf
        font/otf
        image/svg+xml;

    server {
        listen       8080;
        server_name  localhost;

        root /usr/share/nginx/html;
        index index.html;

        # Angular Routing
        location / {
            try_files $uri $uri/ /index.html;
        }

        # Static Assets Caching
        location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|eot|ttf|svg|map)$ {
            expires 1y;
            access_log off;
            add_header Cache-Control "public, immutable";
        }

        # Optional: Explicit asset route
        location /assets/ {
            expires 1y;
            add_header Cache-Control "public, immutable";
        }
    }
}

Paso 5: Compilar la imagen de la aplicación Angular

Con tu configuración personalizada lista, ya puedes compilar la imagen Docker de tu aplicación Angular.

La configuración actualizada incluye:

  • Una configuración de Nginx limpia y lista para producción, adaptada a Angular.
  • Una compilación Docker multi-etapa eficiente que garantiza una imagen final pequeña y segura.

Tras completar los pasos anteriores, el directorio del proyecto debería contener los siguientes archivos:

├── docker-angular-sample/
│ ├── Dockerfile
│ ├── .dockerignore
│ ├── compose.yaml
│ ├── nginx.conf
│ └── README.Docker.md

Con el Dockerfile configurado, puedes compilar la imagen Docker de tu aplicación Angular.

Note

El comando docker build empaqueta tu aplicación en una imagen usando las instrucciones del Dockerfile. Incluye todos los archivos necesarios del directorio actual (el contexto de compilación).

Ejecuta el siguiente comando desde la raíz del proyecto:

$ docker build --tag docker-angular-sample .

Qué hace este comando:

  • Usa el Dockerfile del directorio actual (.)
  • Empaqueta la aplicación y sus dependencias en una imagen Docker
  • Etiqueta la imagen como docker-angular-sample para referenciarla después

Paso 6: Ver imágenes locales

Tras compilar tu imagen Docker, puedes comprobar qué imágenes hay en tu máquina local con la CLI de Docker o Docker Desktop. Como ya trabajas en la terminal, usaremos la CLI de Docker.

Para listar todas las imágenes Docker disponibles en local, ejecuta:

$ docker images

Ejemplo de salida:

REPOSITORY                TAG               IMAGE ID       CREATED         SIZE
docker-angular-sample     latest            34e66bdb9d40   14 seconds ago   76.4MB

Esta salida muestra detalles clave de tus imágenes:

  • Repository – Nombre asignado a la imagen.
  • Tag – Etiqueta de versión que identifica distintas compilaciones (p. ej., latest).
  • Image ID – Identificador único de la imagen.
  • Created – Marca de tiempo de cuándo se compiló la imagen.
  • Size – Espacio en disco total que usa la imagen.

Si la compilación fue correcta, deberías ver la imagen docker-angular-sample en la lista.


Ejecutar la aplicación containerizada

En el paso anterior creaste un Dockerfile para tu aplicación Angular y compilaste una imagen Docker con el comando docker build. Ahora toca ejecutar esa imagen en un contenedor y comprobar que la aplicación funciona como se espera.

Dentro del directorio docker-angular-sample, ejecuta el siguiente comando en una terminal.

$ docker compose up --build

Abre un navegador y visita la aplicación en http://localhost:8080. Deberías ver una aplicación web Angular sencilla.

Pulsa ctrl+c en la terminal para detener la aplicación.

Ejecutar la aplicación en segundo plano

Puedes ejecutar la aplicación desacoplada de la terminal añadiendo la opción -d. Dentro del directorio docker-angular-sample, ejecuta el siguiente comando en una terminal.

$ docker compose up --build -d

Abre un navegador y visita la aplicación en http://localhost:8080. Deberías ver tu aplicación Angular en el navegador.

Para confirmar que el contenedor está en ejecución, usa el comando docker ps:

$ docker ps

Esto listará todos los contenedores activos con sus puertos, nombres y estado. Busca un contenedor que exponga el puerto 8080.

Ejemplo de salida:

CONTAINER ID   IMAGE                          COMMAND                  CREATED             STATUS             PORTS                    NAMES
eb13026806d1   docker-angular-sample-server   "nginx -c /etc/nginx…"   About a minute ago  Up About a minute  0.0.0.0:8080->8080/tcp   docker-angular-sample-server-1

Para detener la aplicación, ejecuta:

$ docker compose down
Note

Para más información sobre los comandos de Compose, consulta la referencia de la CLI de Compose.


Resumen

En esta guía aprendiste a containerizar, compilar y ejecutar una aplicación Angular con Docker. Siguiendo buenas prácticas, creaste una configuración segura, optimizada y lista para producción.

Esto es lo que lograste:

  • Inicializar el proyecto con docker init para generar los archivos de configuración Docker esenciales.
  • Sustituir el Dockerfile por defecto por una compilación multi-etapa que compila la aplicación Angular y sirve los archivos estáticos con Nginx.
  • Sustituir el .dockerignore por defecto para excluir archivos innecesarios y mantener la imagen limpia y eficiente.
  • Compilar la imagen Docker con docker build.
  • Ejecutar el contenedor con docker compose up, en primer plano y en segundo plano.
  • Verificar que la aplicación funcionaba visitando http://localhost:8080.
  • Aprender a detener la aplicación containerizada con docker compose down.

Ya tienes una aplicación Angular totalmente containerizada, en ejecución en un contenedor Docker y lista para desplegarse en cualquier entorno con confianza y consistencia.


Recursos relacionados

Consulta referencias oficiales y buenas prácticas para mejorar tu flujo con Docker:


Próximos pasos

Con tu aplicación Angular ya containerizada, estás listo para el siguiente paso.

En la siguiente sección aprenderás a desarrollar tu aplicación con contenedores Docker, con un entorno de desarrollo consistente, aislado y reproducible en cualquier máquina.