Saltar al contenido principal

Wiedii — Política de Contenedores Docker

⚠️ Verificar versiones antes de usar — los números de versión de imágenes base en esta nota pueden estar desactualizados. Usar docker pull <image> y revisar las notas de lanzamiento oficiales antes de fijar versiones. Ver politicas-core.

Todo contenedor de producción en Wiedii sigue estas convenciones. El objetivo es: imágenes mínimas, reproducibles, seguras y multi-plataforma por defecto.

1. Estructura multi-stage obligatoria

Todo Dockerfile debe usar al menos dos stages:

StageImagen basePropósito
buildRuntime completo (oven/bun, golang, node)Compilar artefactos
Runtime (sin nombre especial)Imagen mínima (alpine, distroless)Ejecutar únicamente el artefacto

El stage de runtime nunca instala dependencias de construcción. Solo recibe los artefactos copiados desde build.

# syntax=docker/dockerfile:1

FROM --platform=$BUILDPLATFORM oven/bun:1.3.14-alpine AS build
WORKDIR /app
# ... instalar deps y compilar ...

FROM caddy:2.9.1-alpine
# ... copiar artefactos y configurar runtime ...

2. Primera línea obligatoria

# syntax=docker/dockerfile:1

Activa BuildKit y habilita las instrucciones avanzadas (--mount, --secret, --ssh). Sin esta línea, las instrucciones de caché y secretos no están disponibles.

3. Imágenes base — versiones exactas

# ❌ Prohibido
FROM node:alpine
FROM caddy:latest

# ✅ Requerido
FROM oven/bun:1.3.14-alpine
FROM caddy:2.9.1-alpine

Renovate gestiona las actualizaciones automáticamente si las versiones están fijadas. Con :latest o :alpine, Renovate no puede rastrear ni actualizar.

4. Multi-plataforma: BUILDPLATFORM y TARGETARCH

FROM --platform=$BUILDPLATFORM oven/bun:1.3.14-alpine AS build
  • --platform=$BUILDPLATFORM en el stage de build → usa la arquitectura de la máquina que construye (ej. linux/arm64 en Mac con M-chip). Evita la emulación lenta.
  • El stage de runtime no lleva --platform explícito → Docker selecciona la arquitectura correcta según el target.

Para condicionamiento por arquitectura en el runtime:

ARG TARGETARCH
RUN if [ "$TARGETARCH" = "arm64" ]; then echo "ARM path"; fi

5. BuildKit cache mounts

Evita descargar dependencias en cada build:

# Bun / Node
RUN --mount=type=cache,target=/var/cache/bun \
bun install --frozen-lockfile

# Go
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go build -o /app/bin ./...

# Python (uv)
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen

El cache mount persiste entre builds en la misma máquina. En CI, configurar el cache de BuildKit con --cache-from y --cache-to.

6. Usuario no-root obligatorio

El proceso en el runtime nunca corre como root.

Stack Bun / Node

FROM oven/bun:1.3.14-alpine
# La imagen oven/bun incluye el usuario `bun` (uid 1000)
COPY --chown=bun:bun . .
USER bun

Stack Caddy (servidor web estático)

caddy:2.x-alpine no incluye el usuario caddy. Crearlo explícitamente:

FROM caddy:2.9.1-alpine
RUN addgroup -S caddy \
&& adduser -S -G caddy caddy \
&& mkdir -p /data /config /srv \
&& chown -R caddy:caddy /data /config /srv /etc/caddy
COPY --chown=caddy:caddy docker/Caddyfile /etc/caddy/Caddyfile
COPY --from=build --chown=caddy:caddy /app/build /srv
USER caddy

Stack Go / Python / otros

Crear un usuario dedicado para el proceso:

RUN addgroup -S app && adduser -S -G app app
USER app

7. Puerto 8080

Todos los servicios exponen el puerto 8080 en producción. No usar 80, 3000, 8000, ni el puerto nativo del framework sin mapear.

EXPOSE 8080

La configuración del servidor web (Caddy, nginx, etc.) debe escuchar en 0.0.0.0:8080.

8. HEALTHCHECK obligatorio

HEALTHCHECK --interval=30s --timeout=5s --start-period=5s \
CMD wget -qO- http://localhost:8080/health > /dev/null || exit 1

Para servicios sin endpoint HTTP dedicado, usar un comando que verifique el proceso:

HEALTHCHECK --interval=30s --timeout=3s \
CMD pgrep -x myapp > /dev/null || exit 1

9. Archivos de configuración en docker/

Los archivos de configuración específicos del runtime (Caddyfile, nginx.conf, supervisord.conf, etc.) van en el directorio docker/ del repo, no en la raíz:

proyecto/
├── Dockerfile
├── docker/
│ ├── Caddyfile # configuración de Caddy
│ └── entrypoint.sh # script de entrada (si aplica)
├── src/
└── ...
COPY --chown=caddy:caddy docker/Caddyfile /etc/caddy/Caddyfile

10. .dockerignore — estructura de tres capas

# ─── Capa 1: control de versiones y metadatos del repo ────────────────────────
.git
.gitignore
.gitattributes

# ─── Capa 2: JS/TS — excluir a cualquier profundidad ─────────────────────────
# NOTA: usar **/node_modules (con **/) no node_modules.
# .gitignore aplica recursivamente; Docker NO — cada patrón es relativo
# a la raíz del contexto a menos que se use **/.
**/node_modules
build/
.docusaurus/

# ─── Capa 3: específico del proyecto ──────────────────────────────────────────
.env
.env.*
!.env.example
README.md
CLAUDE.md
graphify-out/
.claude/

Nunca excluir el directorio de código fuente ni los artefactos que el build stage necesita.

11. Template completo — sitio estático Bun + Caddy

Referencia para proyectos Docusaurus / static sites:

# syntax=docker/dockerfile:1

FROM --platform=$BUILDPLATFORM oven/bun:1.3.14-alpine AS build
WORKDIR /app
ENV BUN_INSTALL_CACHE_DIR=/var/cache/bun
COPY package.json bun.lock ./
RUN --mount=type=cache,target=/var/cache/bun \
bun install --frozen-lockfile
COPY . .
RUN bun run build

FROM caddy:2.9.1-alpine
RUN addgroup -S caddy \
&& adduser -S -G caddy caddy \
&& mkdir -p /data /config /srv \
&& chown -R caddy:caddy /data /config /srv /etc/caddy
COPY --chown=caddy:caddy docker/Caddyfile /etc/caddy/Caddyfile
COPY --from=build --chown=caddy:caddy /app/build /srv
USER caddy
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s \
CMD wget -qO- http://localhost:8080 > /dev/null || exit 1

12. Template completo — API Bun/Node

# syntax=docker/dockerfile:1

FROM --platform=$BUILDPLATFORM oven/bun:1.3.14-alpine AS build
WORKDIR /app
COPY package.json bun.lock ./
RUN --mount=type=cache,target=/var/cache/bun \
bun install --frozen-lockfile --production
COPY . .

FROM oven/bun:1.3.14-alpine
WORKDIR /app
COPY --from=build --chown=bun:bun /app/node_modules ./node_modules
COPY --chown=bun:bun src/ ./src/
COPY --chown=bun:bun package.json .
USER bun
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
CMD wget -qO- http://localhost:8080/health > /dev/null || exit 1
CMD ["bun", "run", "src/index.ts"]

13. Lo que nunca se hace en runtime

  • apt-get install / apk add en el stage de runtime
  • bun install / npm install en el stage de runtime
  • ❌ Clonar repos o descargar binarios al arrancar el contenedor
  • ❌ Escribir en el sistema de archivos fuera de /tmp o volúmenes montados
  • ❌ Correr como root (uid 0)
  • ❌ Usar :latest o tags mutables como base

Referencias

  • repo-setup — checklist de archivos estándar de cualquier repo Wiedii
  • cross-platform — convenciones de compatibilidad multi-plataforma