# Contenedoriza una aplicación Vue.js



## Requisitos previos

Antes de comenzar, asegúrate de que las siguientes herramientas estén instaladas y disponibles en tu sistema:

- Has instalado la última versión de [Docker Desktop](/get-started/get-docker/).
- Tienes un [cliente de git](https://git-scm.com/downloads). Los ejemplos de esta sección utilizan un cliente de git basado en línea de comandos, pero puedes utilizar cualquier cliente.

> **¿Es tu primera vez con Docker?**  
> Comienza con la guía de [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 acompaña a lo largo de todo el proceso de contenedorización de una aplicación Vue.js con Docker. Aprenderás a crear una imagen de Docker lista para producción utilizando las mejores prácticas que mejoran el rendimiento, la seguridad, la escalabilidad y la eficiencia del despliegue.

Al final de esta guía, lograrás:

- Contenedorizar una aplicación Vue.js utilizando Docker.
- Crear y optimizar un Dockerfile para compilaciones de producción. 
- Utilizar compilaciones multi-etapa (multi-stage) para minimizar el tamaño de la imagen.
- Servir la aplicación de manera eficiente con una configuración de Nginx personalizada.
- Compilar imágenes Docker seguras y fáciles de mantener siguiendo las mejores prácticas.

---

## Obtén la aplicación de ejemplo

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

```console
$ git clone https://github.com/kristiyan-velkov/docker-vuejs-sample
```
---

## Generar un Dockerfile

Docker proporciona una herramienta de CLI interactiva llamada `docker init` que ayuda a estructurar los archivos de configuración necesarios para contenedorizar tu aplicación. Esto incluye la generación de un `Dockerfile`, `.dockerignore`, `compose.yaml` y `README.Docker.md`.

Para comenzar, navega a la raíz del directorio de tu proyecto:

```console
$ cd docker-vuejs-sample
```

Luego ejecuta el siguiente comando:

```console
$ docker init
```
Deberías ver 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 interfaz de línea de comandos te hará algunas preguntas sobre la configuración de tu aplicación.
Para mantener la coherencia, utiliza las mismas respuestas que se muestran en el ejemplo a continuación cuando se te solicite:

| 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 build   |
| What port does your server listen on?                      | 8080            |

Una vez finalizado, el directorio de tu proyecto contendrá los siguientes archivos nuevos:

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

---

## Compilar la imagen de Docker

El Dockerfile predeterminado generado por `docker init` proporciona una base sólida para aplicaciones típicas de Node.js. Sin embargo, Vue.js es un framework de front-end que se compila en recursos estáticos, lo que significa que el Dockerfile debe personalizarse para adaptarse a cómo se compilan y se sirven de manera eficiente las aplicaciones Vue.js en un entorno de producción. Adaptarlo adecuadamente garantiza un mejor rendimiento, tamaños de imagen más pequeños y un proceso de despliegue más fluido.

### Paso 1: Revisar los archivos generados

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

- Utilizar compilaciones multi-etapa (multi-stage) para mantener la imagen final limpia y pequeña
- Servir la aplicación utilizando Nginx, un servidor web rápido y seguro
- Mejorar el rendimiento y la seguridad incluyendo únicamente lo necesario

Estas actualizaciones ayudan a garantizar que tu aplicación sea fácil de desplagar, rápida de cargar y esté lista para producción.

> [!NOTE]
> Un `Dockerfile` es un archivo de texto plano que contiene instrucciones paso a paso para compilar una imagen de Docker. Automatiza el empaquetado de tu aplicación junto con sus dependencias y el entorno de ejecución.  
> Para obtener detalles completos, consulta la [referencia de Dockerfile](/reference/dockerfile/).


### Paso 2: Configurar el Dockerfile

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

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

> [!IMPORTANT]
> Esta guía utiliza una etiqueta de imagen LTS estable de Node.js que se considera segura en el momento de escribir la guía. Dado que se publican nuevas versiones y parches de seguridad de forma regular, es posible que la etiqueta que se muestra aquí ya no sea la opción más segura cuando sigas la guía. Revisa siempre las últimas etiquetas de imagen disponibles y selecciona 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 (DHIs) 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 gratuitamente para todos sin necesidad de suscripción. Puedes descargarlas y utilizarlas como cualquier otra imagen de Docker después de iniciar sesión en el registro DHI. Para obtener 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. Descarga la DHI de Node.js (consulta el catálogo para ver las versiones disponibles):
   ```console
   $ docker pull dhi.io/node:24-alpine3.22-dev
   ```

3. Descarga la DHI de Nginx (consulta el catálogo para ver las versiones disponibles):
   ```console
   $ docker pull dhi.io/nginx:1.28.0-alpine3.21-dev
   ```

En el siguiente Dockerfile, las instrucciones `FROM` utilizan `dhi.io/node:24-alpine3.22-dev` y `dhi.io/nginx:1.28.0-alpine3.21-dev` como imágenes base.

```dockerfile
# =========================================
# Etapa 1: Compilar la aplicación Vue.js
# =========================================
# Usar una imagen ligera DHI de Node.js para la compilación
FROM dhi.io/node:24-alpine3.22-dev AS builder

# Establecer el directorio de trabajo dentro del contenedor
WORKDIR /app

# Copiar primero los archivos relacionados con los paquetes para aprovechar el mecanismo de caché de Docker
COPY package.json package-lock.json* ./

# Instalar las dependencias del proyecto usando npm ci (garantiza una instalación limpia y reproducible)
RUN --mount=type=cache,target=/root/.npm npm ci

# Copiar el resto del código fuente de la aplicación en el contenedor
COPY . .

# Compilar la aplicación Vue.js
RUN npm run build

# =========================================
# Etapa 2: Preparar Nginx para servir archivos estáticos
# =========================================

FROM dhi.io/nginx:1.28.0-alpine3.21-dev AS runner

# Copiar la configuración personalizada de Nginx
COPY nginx.conf /etc/nginx/nginx.conf

# Copiar la salida estática de la compilación de la etapa de construcción al directorio de servicio HTML predeterminado de Nginx
COPY --chown=nginx:nginx --from=builder /app/dist /usr/share/nginx/html

# Usar un usuario no root incorporado como mejor práctica de seguridad
USER nginx

# Exponer el puerto 8080 para permitir el tráfico HTTP
# Nota: El contenedor Nginx predeterminado ahora escucha en el puerto 8080 en lugar del 80 
EXPOSE 8080

# Iniciar Nginx directamente con la configuración personalizada
ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"]
CMD ["-g", "daemon off;"]
```

**Uso de la imagen oficial de Docker**



Reemplaza el contenido de tu `Dockerfile` actual con la configuración optimizada a continuación. Esta configuración está adaptada específicamente para compilar y servir aplicaciones Vue.js en un entorno limpio, eficiente y listo para producción.

```dockerfile
# =========================================
# Etapa 1: Compilar la aplicación Vue.js
# =========================================
ARG NODE_VERSION=24.12.0-alpine
ARG NGINX_VERSION=alpine3.22

# Usar una imagen ligera de Node.js para la compilación (personalizable mediante ARG)
FROM node:${NODE_VERSION} AS builder

# Establecer el directorio de trabajo dentro del contenedor
WORKDIR /app

# Copiar primero los archivos relacionados con los paquetes para aprovechar el mecanismo de caché de Docker
COPY package.json package-lock.json* ./

# Instalar las dependencias del proyecto usando npm ci (garantiza una instalación limpia y reproducible)
RUN --mount=type=cache,target=/root/.npm npm ci

# Copiar el resto del código fuente de la aplicación en el contenedor
COPY . .

# Compilar la aplicación Vue.js
RUN npm run build

# =========================================
# Etapa 2: Preparar Nginx para servir archivos estáticos
# =========================================

FROM nginxinc/nginx-unprivileged:${NGINX_VERSION} AS runner

# Copiar la configuración personalizada de Nginx
COPY nginx.conf /etc/nginx/nginx.conf

# Copiar la salida estática de la compilación de la etapa de construcción al directorio de servicio HTML predeterminado de Nginx
COPY --chown=nginx:nginx --from=builder /app/dist /usr/share/nginx/html

# Usar un usuario no root incorporado como mejor práctica de seguridad
USER nginx

# Exponer el puerto 8080 para permitir el tráfico HTTP
# Nota: El contenedor Nginx predeterminado ahora escucha en el puerto 8080 en lugar del 80 
EXPOSE 8080

# Iniciar Nginx directamente con la configuración personalizada
ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"]
CMD ["-g", "daemon off;"]
```

> [!NOTE]
> Estamos utilizando nginx-unprivileged en lugar de la imagen estándar de Nginx para seguir las mejores prácticas de seguridad.
> Ejecutar la imagen final como un usuario no root:
>- Reduce la superficie de ataque
>- Se alinea con las recomendaciones de Docker para el aseguramiento (hardening) de contenedores
>- Ayuda a cumplir con políticas de seguridad más estrictas en entornos de producción



### Paso 3: Configurar el archivo .dockerignore

El archivo `.dockerignore` juega un papel crucial en la optimización de tu imagen de Docker al especificar qué archivos y directorios deben excluirse del contexto de compilación. 

> [!NOTE]
>Esto ayuda a:
>- Reducir el tamaño de la imagen  
>- Acelerar el proceso de compilación  
>- Evitar que se añadan archivos sensibles o innecesarios (como `.env`, `.git` o `node_modules`) a la imagen final.
>
> Para obtener más información, consulta la [referencia de .dockerignore](/reference/dockerfile/#dockerignore-file).

Copia y reemplaza el contenido de tu archivo `.dockerignore` existente con la configuración a continuación:

```dockerignore
# -------------------------------
# Directorios de dependencias
# -------------------------------
node_modules/

# -------------------------------
# Salidas de producción y compilación
# -------------------------------
dist/
out/
build/
public/build/

# -------------------------------
# Directorios de caché y de Vite/VuePress
# -------------------------------
.vite/
.vitepress/
.cache/
.tmp/

# -------------------------------
# Salidas de pruebas y cobertura
# -------------------------------
coverage/
reports/
jest/
cypress/
cypress/screenshots/
cypress/videos/

# -------------------------------
# Archivos de entorno y configuración
# -------------------------------
*.env*
!.env.production    # Mantener el entorno de producción si es necesario
*.local
*.log

# -------------------------------
# Artefactos de TypeScript
# -------------------------------
*.tsbuildinfo

# -------------------------------
# Configuración de editores e IDEs
# -------------------------------
.vscode/
.idea/
*.swp

# -------------------------------
# Archivos del sistema
# -------------------------------
.DS_Store
Thumbs.db

# -------------------------------
# Archivos de bloqueo (opcional)
# -------------------------------
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# -------------------------------
# Archivos de Git
# -------------------------------
.git/
.gitignore

# -------------------------------
# Archivos relacionados con Docker
# -------------------------------
Dockerfile
.dockerignore
docker-compose.yml
docker-compose.override.yml
```

### Paso 4: Crear el archivo `nginx.conf`

Para servir tu aplicación Vue.js de manera eficiente dentro del contenedor, configurarás Nginx con un diseño personalizado. Esta configuración está optimizada para el rendimiento, el almacenamiento en caché del navegador, la compresión gzip y el soporte para el enrutamiento del lado del cliente (client-side routing).

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

> [!NOTE]
> Para obtener 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;
    charset       utf-8;

    access_log    off;
    error_log     /dev/stderr warn;

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

    gzip on;
    gzip_comp_level 6;
    gzip_proxied any;
    gzip_min_length 256;
    gzip_vary on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;

    server {
        listen       8080;
        server_name  localhost;

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

        location / {
            try_files $uri $uri/ /index.html;
        }

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

        location /assets/ {
            expires 1y;
            add_header Cache-Control "public, immutable";
            add_header X-Content-Type-Options nosniff;
        }

        error_page 404 /index.html;
    }
}
```

### Paso 5: Compilar la imagen de la aplicación Vue.js

Con tu configuración personalizada implementada, ya estás listo para compilar la imagen de Docker para tu aplicación Vue.js.

La configuración actualizada incluye:

- Una configuración de Nginx limpia y lista para producción adaptada específicamente para Vue.js.
- Una compilación Docker eficiente de múltiples etapas, garantizando una imagen final pequeña y segura.

Después de completar los pasos anteriores, el directorio de tu proyecto debería contener los siguientes archivos:

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

Ahora que tu Dockerfile está configurado, puedes compilar la imagen de Docker para tu aplicación Vue.js.

> [!NOTE]
> El comando `docker build` empaqueta tu aplicación en una imagen utilizando las instrucciones del Dockerfile. Incluye todos los archivos necesarios del directorio actual (llamado [contexto de compilación](/build/concepts/context/#what-is-a-build-context)).

Ejecuta el siguiente comando desde la raíz de tu proyecto:

```console
$ docker build --tag docker-vuejs-sample .
```

Qué hace este comando:
- Utiliza el Dockerfile en el directorio actual (.)
- Empaqueta la aplicación y sus dependencias en una imagen Docker
- Etiqueta la imagen como docker-vuejs-sample para que puedas hacer referencia a ella más adelante


#### Paso 6: Ver imágenes locales

Después de compilar tu imagen de Docker, puedes verificar qué imágenes están disponibles en tu máquina local utilizando la CLI de Docker o [Docker Desktop](/desktop/use-desktop/images/). Dado que ya estás trabajando en la terminal, utilicemos la CLI de Docker.

Para enumerar todas las imágenes de Docker disponibles localmente, ejecuta el siguiente comando:

```console
$ docker images
```

Ejemplo de salida:

```shell
REPOSITORY                TAG               IMAGE ID       CREATED         SIZE
docker-vuejs-sample       latest            8c9c199179d4   14 seconds ago   76.2MB
```

Esta salida proporciona detalles clave sobre tus imágenes:

- **Repository** – El nombre asignado a la imagen.
- **Tag** – Una etiqueta de versión que ayuda a identificar diferentes compilaciones (por ejemplo, latest).
- **Image ID** – Un identificador único para la imagen.
- **Created** – La marca de tiempo que indica cuándo se compiló la imagen.
- **Size** – El espacio total en disco utilizado por la imagen.

Si la compilación se completó con éxito, deberías ver la imagen `docker-vuejs-sample` en la lista. 

---

## Ejecutar la aplicación contenedorizada

En el paso anterior, creaste un Dockerfile para tu aplicación Vue.js y compilaste una imagen de Docker utilizando el comando docker build. Ahora es el momento de ejecutar esa imagen en un contenedor y verificar que tu aplicación funcione según lo esperado.


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

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

Abre un navegador y visualiza la aplicación en [http://localhost:8080](http://localhost:8080). Deberías ver una aplicación web Vue.js sencilla.

Presiona `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 (detached) agregando la opción `-d`. Dentro del directorio `docker-vuejs-sample`, ejecuta el siguiente comando en una terminal.

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

Abre un navegador y visualiza la aplicación en [http://localhost:8080](http://localhost:8080). Deberías ver tu aplicación Vue.js ejecutándose en el navegador.


Para confirmar que el contenedor se está ejecutando, utiliza el comando `docker ps`:

```console
$ docker ps
```

Esto enumerará todos los contenedores activos junto 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
37a1fa85e4b0   docker-vuejs-sample-server     "nginx -c /etc/nginx…"   About a minute ago  Up About a minute  0.0.0.0:8080->8080/tcp   docker-vuejs-sample-server-1
```


Para detener la aplicación, ejecuta:

```console
$ docker compose down
```


> [!NOTE]
> Para obtener 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 contenedorizar, compilar y ejecutar una aplicación Vue.js utilizando Docker. Al seguir las mejores prácticas, creaste una configuración segura, optimizada y lista para producción.

Lo que lograste:
- Inicializaste tu proyecto utilizando `docker init` para estructurar los archivos de configuración esenciales de Docker.
- Reemplazaste el `Dockerfile` predeterminado con una compilación multi-etapa que compila la aplicación Vue.js y sirve los archivos estáticos utilizando Nginx.
- Reemplazaste el archivo `.dockerignore` predeterminado para excluir archivos innecesarios y mantener la imagen limpia y eficiente.
- Compilaste tu imagen de Docker utilizando `docker build`.
- Ejecutaste el contenedor utilizando `docker compose up`, tanto en primer plano como en modo desacoplado.
- Verificaste que la aplicación se estuviera ejecutando visitando [http://localhost:8080](http://localhost:8080).
- Aprendiste a detener la aplicación contenedorizada utilizando `docker compose down`.

Ahora tienes una aplicación Vue.js completamente contenedorizada, ejecutándose en un contenedor Docker y lista para ser desplegada en cualquier entorno con confianza y coherencia.

---

## Recursos relacionados

Explora las referencias oficiales y las mejores prácticas para perfeccionar tu flujo de trabajo con Docker:

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

---

## Siguientes pasos

Con tu aplicación Vue.js ahora contenedorizada, estás listo para pasar al siguiente paso.

En la siguiente sección, aprenderás cómo desarrollar tu aplicación utilizando contenedores Docker, lo que permite un entorno de desarrollo coherente, aislado y reproducible en cualquier máquina.

