# 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](/get-started/get-docker/).
- Un [cliente git](https://git-scm.com/downloads). 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](/get-started/docker-concepts/the-basics/what-is-a-container/) 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:

```console
$ 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:

```console
$ cd docker-angular-sample
```

Then run the following command:

```console
$ docker init
```

Verás una salida similar a:

```text
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:

| Pregunta                                                   | Respuesta      |
| ---------------------------------------------------------- | -------------- |
| 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:

```text
├── 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](/reference/dockerfile/).

### Paso 2: Configurar el Dockerfile

Antes de crear un Dockerfile, debes elegir una imagen base. Puedes usar la [imagen oficial de Node.js](https://hub.docker.com/_/node) o una Docker Hardened Image (DHI) del [catálogo de imágenes endurecidas](https://hub.docker.com/hardened-images/catalog).

Elegir DHI ofrece una imagen lista para producción, ligera y segura. Para más información, consulta [Docker Hardened Images](https://docs-docker.esdocu.com/dhi/).

> [!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

**Uso de Docker Hardened Images**


Las Docker Hardened Images (DHI) están disponibles para Node.js en el [catálogo de Docker Hardened Images](https://hub.docker.com/hardened-images/catalog/dhi/node). 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](/dhi/get-started/).

1. Inicia sesión en el registro DHI:

   ```console
   $ docker login dhi.io
   ```

2. Haz pull de la DHI de Node.js (consulta el catálogo para las versiones disponibles):
   ```console
   $ 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.

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

**Uso de la imagen oficial de Docker**



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

```dockerfile
# =========================================
# 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`](/reference/dockerfile/#dockerignore-file).

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

```dockerignore
# ================================
# 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](https://nginx.org/en/docs/).

```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:

```text
├── 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](/build/concepts/context/#what-is-a-build-context)).

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

```console
$ 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](/desktop/use-desktop/images/). Como ya trabajas en la terminal, usaremos la CLI de Docker.

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

```console
$ docker images
```

Ejemplo de salida:

```shell
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.

```console
$ docker compose up --build
```

Abre un navegador y visita la aplicación en [http://localhost:8080](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.

```console
$ docker compose up --build -d
```

Abre un navegador y visita la aplicación en [http://localhost:8080](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`:

```console
$ 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:

```shell
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:

```console
$ docker compose down
```

> [!NOTE]
> Para más información sobre los comandos de Compose, consulta la [referencia de la CLI de Compose](/reference/cli/docker/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](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:

- [Compilaciones multi-etapa](/build/building/multi-stage/) – Separa etapas de compilación y ejecución.
- [Buenas prácticas para escribir Dockerfiles](/develop/develop-images/dockerfile_best-practices/) – Escribe Dockerfiles eficientes, mantenibles y seguros.
- [Contexto de compilación en Docker](/build/concepts/context/) – Cómo el contexto afecta a las compilaciones de imágenes.
- [Referencia de la CLI `docker init`](/reference/cli/docker/init/) – Genera recursos Docker automáticamente.
- [Referencia de la CLI `docker build`](/reference/cli/docker/image/build/) – Compila imágenes Docker desde un Dockerfile.
- [Referencia de la CLI `docker images`](/reference/cli/docker/image/ls/) – Gestiona e inspecciona imágenes Docker locales.
- [Referencia de la CLI `docker compose up`](/reference/cli/docker/compose/up/) – Inicia y ejecuta aplicaciones multicontenedor.
- [Referencia de la CLI `docker compose down`](/reference/cli/docker/compose/down/) – Detiene y elimina contenedores, redes y volúmenes.

---

## 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.

