A estas alturas, ya te puedo decir que en mi equipo personal ya no hay ni rastro de Docker. Sin embargo, me quedaba una asignatura pendiente por contarte, y era la construcción de imágenes utilizando Podman. Pero en concreto quería centrarme en buildah. En su momento, hice alguna imagen con Podman, pero utilizando Dockerfile, ahora quería explorar esto de hacerlo con scritps que es la solución que aporta Buildah y es básicamente donde se centra la gran diferencia entre ambos. Lo cierto es que me ha resultado realmente interesante y aunque seguiré utilizando Dockerfile en algunos proyectos, no dudo que en otros me centre en la experiencia de los scripts, que por otro lado tanto me gustan a mi.

Buildah vs Dockerfile. La guía definitiva para construir imágenes como un PRO
Una curiosidad
En general me gusta averiguar el porqué de las cosas, y en este caso, me ha resultado curioso que Buildah se centre en la construcción de imágenes utilizando scripts, mientras que Podman se centre en la construcción de imágenes utilizando Dockerfile. No es que sea algo malo, pero me ha resultado curioso. De hecho, lo que he hecho ha sido construir una imagen utilizando ambos métodos para ver las diferencias entre ambos y la verdad es que me ha resultado realmente interesante. En concreto he creado una imagen de nginx pero como webdav.
Pero lo que quería era conocer el porqué del nombre. Y es que Buildah viene de la pronunciación de Builder, constructor con un marcado acento de Boston donde la r final se omite o se transforma en un sonido de tipo ah. El porqué del nombre,
- Funcionalidad. Buildah se llama así porque su única misión es construir imágenes.
- Humor del desarrollador. El equipo orginal de Red Hat que lo creó buscaba algo que sonara cercano y desenfadado. En lugar de un nombre técnico o formal, optaron por algo que reflejara la idea de construcción de una manera divertida y memorable. La pronunciación de «Builder» con un acento de Boston les pareció una opción perfecta para transmitir esa sensación de cercanía y humor.
- Identidad propia. Al separarse de Docker, era necesario un nombre que reflejara que esta herrramienta se espcializa solo en una parte del proceso, delegando la ejecución en otros.
El ejemplo de nginx como webdav
Como te comentaba, para comparar una solución con otra, he implementado la misma imagen utilizando ambos métodos, y la verdad es que me ha resultado realmente interesante. En concreto he creado una imagen de nginx pero como webdav. Para ello, he utilizado el siguiente Dockerfile,
# 1. Base ligera de Alpine
FROM alpine:3.23
# 2. Argumentos y variables de entorno
ARG USER=webdav
ARG UID=1000
ARG GID=1000
# 3. Instalación de paquetes (Nginx + Módulo WebDAV Extendido)
RUN apk add --no-cache nginx nginx-mod-http-dav-ext
# 4. Crear usuario sin privilegios
RUN adduser -D -u ${UID} ${USER}
# 5. Preparar estructura de directorios necesaria
# Creamos todas las rutas que Nginx necesita para escribir sin ser root
RUN mkdir -p /data \
/var/log/nginx \
/var/lib/nginx/tmp \
/run/nginx \
/tmp/nginx_upload && \
# Ajustamos la propiedad al usuario 1000
chown -R ${UID}:${GID} /data /var/log/nginx /var/lib/nginx /run/nginx /tmp/nginx_upload && \
# Redirigir logs a la salida estándar para que 'podman logs' funcione
ln -sf /dev/stdout /var/log/nginx/access.log && \
ln -sf /dev/stderr /var/log/nginx/error.log
# 6. Copiar configuración personalizada
COPY nginx.conf /etc/nginx/nginx.conf
# 7. Configuración de ejecución
USER ${USER}
EXPOSE 8080
VOLUME ["/data"]
# Comando de arranque
CMD ["nginx", "-g", "daemon off;"]
Y por otro lado utilizando Buildah con un script de construcción,
#!/usr/bin/fish
set user webdav
set name "nginx-webdav"
set image_base "alpine:3.23"
echo "--- Construyendo imagen NO-ROOT de $name ---"
set container (buildah from $image_base)
# 1. Instalar paquetes
buildah run $container -- apk add --no-cache nginx nginx-mod-http-dav-ext
# 2. Crear usuario '$user' (UID 1000 para que mapee con tu usuario de Arch)
buildah run $container -- adduser -D -u 1000 $user
# 3. Montar para configurar
set mountpoint (buildah mount $container)
# 4. Crear y asegurar directorios para el usuario '$user'
# Necesitamos que Nginx pueda escribir logs, pids y temporales sin ser root
mkdir -p $mountpoint/data
mkdir -p $mountpoint/var/log/nginx
mkdir -p $mountpoint/var/lib/nginx/tmp
mkdir -p $mountpoint/run/nginx
mkdir -p $mountpoint/tmp/nginx_upload
# CAMBIO CLAVE: Todo pertenece al usuario 1000
chown -R 1000:1000 $mountpoint/data \
$mountpoint/var/log/nginx \
$mountpoint/var/lib/nginx \
$mountpoint/run/nginx \
$mountpoint/tmp/nginx_upload
# Enlazamos logs a la salida del contenedor
ln -sf /dev/stdout $mountpoint/var/log/nginx/access.log
ln -sf /dev/stderr $mountpoint/var/log/nginx/error.log
# 5. Copiar configuración
cp nginx.conf $mountpoint/etc/nginx/nginx.conf
# 6. Metadatos: Cambiamos el usuario de ejecución
buildah config --user $user $container
buildah config --port 8080 $container
buildah config --cmd '["nginx", "-g", "daemon off;"]' $container
buildah unmount $container
buildah commit --rm --squash $container atareao/$name:latest
De cuialquier forma te recomiendo que visites el respoitorio de GitHub donde he subido ambos ejemplos, y donde puedes encontrar el código completo de ambos métodos, así como las instrucciones para probarlos y utilizarlos.
Comparando uno con otro
Ambos métodos, llegan al mismo destino (una imagen de Alpine 3.23 con WebDAV y seguridad no-root), pero lo hacen con filosofías de construcción distintas que vale la pena desglosar para tu audiencia.
Gestión de la construcción
- Transparencia vs. Control. El
Dockerfilees declarativo; cualquier usuario que lo lea entiende el estado final deseado. El script debuildahes imperativo y procedimental, lo que te permite manipular el sistema de archivos del contenedor directamente desde tu shell de Arch (mountpoint), algo que un Dockerfile no permite.
_ Eficiencia de Capas. En elDockerfile, agrupamos los comandosmkdir,chownylnen un solo pasoRUNpara generar una única capa y ahorrar espacio. En el script debuildah, aunque ejecutes varios comandos, elcommit --squashfinal logra un resultado similar de una sola capa de datos.
Configuración de Usuario y Permisos
- Flexibilidad (Args vs. Hardcoded).
- El Dockerfile usa
ARGpara el UID y GID (por defecto 1000), permitiendo que alguien cambie esos valores al construir la imagen sin editar el archivo. - El script tiene el UID
1000grabado directamente en los comandoschownyadduser, lo que lo hace menos flexible si otro usuario con distinto UID quisiera usarlo.
- El Dockerfile usa
- Propiedad de Archivos. Ambos aseguran que las rutas críticas (
/data,/var/log/nginx, etc.) pertenezcan al usuario sin privilegios para que Nginx pueda arrancar y escribir sin ser root.
Anatomía de las Instrucciones
| Característica | Instrucción en Dockerfile | Equivalencia en build-webdav.fish |
|---|---|---|
| Base | FROM alpine:3.23 | buildah from alpine:3.23 |
| Instalación | RUN apk add --no-cache ... | buildah run ... apk add --no-cache ... |
| Manipulación FS | RUN mkdir / chown | mkdir / chown sobre el $mountpoint |
| Metadatos | USER webdav | buildah config --user webdav |
| Puerto | EXPOSE 8080 | buildah config --port 8080 |
La «Magia» de los Logs
Ambos métodos implementan una solución idéntica para que podman logs funcione correctamente: crean enlaces simbólicos (ln -sf) desde los archivos de log de Nginx hacia la salida estándar (/dev/stdout) y de error (/dev/stderr).
Las ventajas
Utilizar un script de Buildah (como tu build-webdav.fish) en lugar de un Dockerfile tradicional ofrece ventajas específicas,
- Control Granular y Depuración en Vivo
- Manipulación Directa: Al usar
buildah mount, puedes entrar al sistema de archivos del contenedor desde tu propia terminal de Arch Linux y realizar cambios con tus herramientas locales antes de hacer elcommit. - Pruebas Atómicas: Puedes ejecutar el script paso a paso. Si una instrucción de
buildah runfalla, el contenedor sigue ahí y puedes inspeccionar su estado exacto sin tener que reconstruir todo desde la última capa válida del Dockerfile.
- Manipulación Directa: Al usar
- Flexibilidad y Lógica de Programación
- Uso de Lenguajes de Scripting: Al ser un archivo
.fish(o.sh), puedes usar toda la potencia de tu shell: buclesfor, condicionales complejos o llamadas a otros scripts locales que serían muy difíciles de implementar en un Dockerfile. - Variables Dinámicas: Puedes capturar la salida de comandos de tu sistema host y pasarlas directamente al contenedor durante la construcción de forma más natural que con los
ARGde Docker.
- Uso de Lenguajes de Scripting: Al ser un archivo
- Eficiencia en el Resultado Final
- Squashing Nativo: El comando
buildah commit --rm --squashaplasta automáticamente todas las operaciones en una sola capa de sistema de archivos, lo que garantiza una imagen final muy ligera (especialmente usando Alpine) sin preocuparte por el número de instrucciones ejecutadas. - Sin Demonios: Buildah no necesita un demonio corriendo (como Docker), lo que reduce la superficie de ataque y el consumo de recursos en tu máquina de desarrollo.
- Squashing Nativo: El comando
- Gestión de Archivos y Permisos
- Copiado Inteligente: Puedes usar el comando
cpestándar de Linux hacia el$mountpoint, permitiéndote mantener permisos de archivos de forma más precisa que con la instrucciónCOPYde Docker. - Estructura a Medida: Te permite crear directorios complejos y asignar propietarios (
chown) de manera imperativa y clara, asegurando que el entorno no-root funcione perfectamente desde el primer segundo.
- Copiado Inteligente: Puedes usar el comando
Los inconvenientes
Aunque el uso de scripts de Buildah (como tu build-webdav.fish) es una delicia para quienes amamos el control total en Linux, no todo es «miel sobre hojuelas». Para ser justos, también hay que mencionar los puntos donde el Dockerfile le gana la partida.
- Falta de Estandarización y Portabilidad
- Dependencia del Host: Tu script depende de que el sistema donde se ejecuta tenga instalado
fish,buildahy las utilidades de GNU/Linux. Un Dockerfile, en cambio, se puede construir en casi cualquier sitio (Windows, macOS, CI/CD) sin cambiar ni una línea. - Curva de Aprendizaje: Para la comunidad, leer un Dockerfile es el estándar de la industria. Un script imperativo requiere que quien lo lea entienda la lógica de tu shell y los comandos específicos de Buildah.
- Dependencia del Host: Tu script depende de que el sistema donde se ejecuta tenga instalado
- Gestión de la Caché
- Reconstrucciones lentas: El Dockerfile brilla gracias a su sistema de capas cacheables. Si solo cambias el
nginx.conf, Docker/Podman solo reconstruye desde esa línea. - Todo o Nada: En tu script, cada vez que lo ejecutas, se suele empezar desde el
buildah from. Aunque Buildah tiene caché, no es tan automática ni granular como la del motor de Docker, lo que puede hacer que las reconstrucciones sean más pesadas si la imagen crece.
- Reconstrucciones lentas: El Dockerfile brilla gracias a su sistema de capas cacheables. Si solo cambias el
- Mantenimiento y Fragilidad
- Rutas de montaje: El uso de
buildah mountes potente pero peligroso. Si el script falla a mitad y no ejecuta elunmount, puedes dejar puntos de montaje «zombis» en tu sistema Arch que requieren intervención manual. - Errores Silenciosos: En un Dockerfile, si un paso falla, el proceso se detiene inmediatamente. En un script, si no gestionas bien los errores (con
set -eo lógica similar), el script podría seguir ejecutando pasos sobre un contenedor corrupto.
- Rutas de montaje: El uso de
- Complejidad en el CI/CD
- Entornos Especiales: La mayoría de las plataformas de integración continua (GitHub Actions, GitLab CI) están optimizadas para
docker build. Ejecutar un script que requiere privilegios para montar sistemas de archivos (mountpoint) suele requerir configuraciones adicionales o contenedores con «privilegios» dentro del pipeline.
- Entornos Especiales: La mayoría de las plataformas de integración continua (GitHub Actions, GitLab CI) están optimizadas para
Comparativa rápida: ¿Cuándo usar cada uno?
| Situación | Ganador | Razón |
|---|---|---|
| Uso personal en Arch | Script | Tienes el control total y tus herramientas de siempre. |
| Colaboración/Open Source | Dockerfile | Es el «idioma universal» de los contenedores. |
| Integración Continua | Dockerfile | Aprovecha mejor la caché y la infraestructura estándar. |
| Depuración Profunda | Script | Puedes inspeccionar el interior sin capas intermedias. |
Más información,