Dockerfile y Builds Personalizados
Dockerfile y Builds Personalizados
Cuando Nixpacks no es suficiente o necesitas control total sobre la imagen final, Dokploy permite usar tu propio Dockerfile. Es el metodo ideal para proyectos con dependencias complejas, imagenes minimas o procesos de build no convencionales.
Cuando usar Dockerfile custom
Opta por un Dockerfile propio si:
- Necesitas una imagen base especifica (alpine, distroless, scratch)
- Tu proyecto tiene dependencias de sistema complejas (FFmpeg, CUDA, librerías nativas)
- Quieres optimizar el tamano de la imagen final
- El proceso de build tiene pasos no estandar
- Necesitas multi-stage builds con control preciso
- Trabajas con lenguajes o frameworks que Nixpacks no soporta bien
Configurar build type como Docker en Dokploy
- Ve a tu proyecto y selecciona el servicio (o crea uno nuevo)
- En la seccion General, busca Build Type
- Cambia de Nixpacks a Dockerfile
- En Dockerfile Path, indica la ruta al archivo (por defecto
./Dockerfile) - Clic en Save y luego Deploy
Dokploy ejecutara docker build usando el Dockerfile especificado y desplegara la imagen resultante.
Ejemplo: Dockerfile multi-stage para app Go
Una API REST en Go con multi-stage build que produce una imagen final de ~15MB:
# Stage 1: Build
FROM golang:1.23-alpine AS builder
WORKDIR /app
# Copiar dependencias primero para cache
COPY go.mod go.sum ./
RUN go mod download
# Copiar codigo fuente
COPY . .
# Compilar binario estatico
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server ./cmd/api
# Stage 2: Runtime
FROM alpine:3.21
RUN apk add --no-cache ca-certificates tzdata
WORKDIR /app
COPY --from=builder /app/server .
EXPOSE 8080
USER nobody:nobody
ENTRYPOINT ["./server"]
Por que multi-stage
- El stage
buildertiene todo el toolchain de Go (~800MB) - El stage final solo copia el binario compilado (~15MB total)
- Reduccion de superficie de ataque: menos paquetes, menos vulnerabilidades
Build arguments
Los build arguments permiten parametrizar el Dockerfile sin hardcodear valores.
Definir ARGs en el Dockerfile
FROM node:22-alpine AS builder
ARG NODE_ENV=production
ARG API_URL
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
COPY . .
RUN API_URL=${API_URL} npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
Configurar build args en Dokploy
- En la configuracion del servicio, ve a la seccion Advanced
- Busca Build Arguments
- Agrega cada argumento como clave-valor:
API_URL=https://api.midominio.com
NODE_ENV=production
Los build args solo estan disponibles durante el build, no en runtime. Para variables de runtime, usa la seccion Environment.
Build context
El build context define que directorio se envia al daemon de Docker para el build.
Proyecto en la raiz
Si el Dockerfile esta en la raiz del repositorio, no necesitas configurar nada:
mi-proyecto/
Dockerfile
src/
package.json
Monorepo con multiples servicios
En un monorepo, configura el Build Path para apuntar al subdirectorio correcto:
monorepo/
apps/
api/
Dockerfile
src/
web/
Dockerfile
src/
packages/
shared/
Para deployar api:
- Build Path:
./apps/api - Dockerfile Path:
./Dockerfile(relativo al build path)
Si el Dockerfile necesita acceso a archivos fuera de su directorio (por ejemplo, packages/shared), mantiene el build path en la raiz y usa una ruta de Dockerfile:
- Build Path:
./ - Dockerfile Path:
./apps/api/Dockerfile
Dentro del Dockerfile, las rutas COPY son relativas al build context:
FROM node:22-alpine
WORKDIR /app
COPY apps/api/package.json ./
COPY packages/shared ./packages/shared
Docker path personalizado
No estas limitado a ./Dockerfile. Dokploy permite especificar cualquier ruta:
./docker/production.Dockerfile
./infra/Dockerfile.api
./apps/web/Dockerfile.prod
Configura el campo Dockerfile Path en la seccion General del servicio. La ruta es relativa al build path.
Esto es util cuando tienes multiples Dockerfiles para distintos entornos:
mi-proyecto/
Dockerfile # Desarrollo
Dockerfile.prod # Produccion
Dockerfile.test # Testing/CI
Optimizar imagenes para produccion
Usar alpine como base
Alpine Linux produce imagenes significativamente mas pequenas:
# ~900MB
FROM node:22
# ~150MB
FROM node:22-alpine
Si necesitas compilar dependencias nativas en Node.js:
FROM node:22-alpine
RUN apk add --no-cache python3 make g++
COPY package.json package-lock.json ./
RUN npm ci --only=production
RUN apk del python3 make g++
Usar scratch para binarios estaticos
Para Go, Rust u otros lenguajes que producen binarios estaticos:
FROM rust:1.84-alpine AS builder
WORKDIR /app
COPY . .
RUN cargo build --release --target x86_64-unknown-linux-musl
FROM scratch
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/mi-app /
EXPOSE 8080
ENTRYPOINT ["/mi-app"]
La imagen final pesa solo lo que pesa el binario (tipicamente 5-20MB).
Usar distroless de Google
Un punto medio entre alpine y scratch. Incluye certificados CA y timezone data:
FROM golang:1.23 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o /server .
FROM gcr.io/distroless/static-debian12
COPY --from=builder /server /
EXPOSE 8080
ENTRYPOINT ["/server"]
Buenas practicas para imagenes pequeñas
- Ordenar las capas: Copiar archivos de dependencias antes que el codigo fuente para aprovechar cache
- Combinar RUN: Menos capas = imagen mas pequena
- Limpiar cache: Eliminar cache de package managers
- Usar .dockerignore: Excluir archivos innecesarios
Ejemplo de .dockerignore:
node_modules
.git
*.md
.env*
dist
coverage
.vscode
Ejemplo: Node.js optimizado para produccion
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production && npm cache clean --force
FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package.json .
USER node
EXPOSE 3000
CMD ["node", "dist/index.js"]
Este patron usa tres stages:
- deps: Solo dependencias de produccion
- builder: Build completo con devDependencies
- final: Imagen limpia con solo lo necesario
Debugging de builds fallidos
Si el build falla, revisa los logs en Deployments. Problemas comunes:
Archivo no encontrado en COPY:
COPY failed: file not found in build context
Verifica que el build path y dockerfile path esten correctos, y que el archivo no este en .dockerignore.
Dependencia nativa faltante:
node-gyp ERR! build error
Agrega las dependencias de compilacion en el stage de build:
RUN apk add --no-cache python3 make g++
Puerto no expuesto:
Asegurate de que el EXPOSE coincida con el puerto configurado en Dokploy y que tu app escuche en 0.0.0.0, no en 127.0.0.1.
Anterior: Capitulo 6: Nixpacks | Siguiente: Capitulo 8: Docker Compose