Cómo implementar WordPress en contenedores (Podman)
Podman es la alternativa a Docker de Red Hat/IBM. Daemonless, rootless por diseño, compatible con los comandos de Docker. Para WordPress en producción, Podman rootless es una opción sólida que evita el problema de seguridad de “Docker daemon corre como root”. Esta guía te lleva de cero a un WordPress funcionando con MariaDB, persistencia y HTTPS.
TL;DR — Lo que vas a tener al final
- Tiempo de setup: 1-2 horas para el primer multi-stage, 30 min para los siguientes.
- Requisitos: Docker 24+ con BuildKit habilitado.
- Resultado: Imágenes 80-95% más pequeñas, builds 50-70% más rápidos.
- Troubleshooting: DOCKER_BUILDKIT=1, dive para analizar layers, troubleshooting sin shell con stage debug.
Por qué Podman y no Docker
Docker es lo más usado, pero tiene dos problemas reales en producción:
- Daemon corre como root: si alguien compromete el daemon, tiene root en el host.
- Licencia: Docker Desktop es pago para empresas > 250 empleados desde 2022.
Podman resuelve ambos:
- Daemonless: cada comando es un proceso independiente, no hay un servicio central.
- Rootless por default: pods y contenedores corren con tu usuario, no como root.
- 100% compatible con CLI de Docker:
alias docker=podmany funciona el 90% de los comandos. - Open source puro (Apache 2.0): sin licencia enterprise.
Las diferencias operativas:
- Podman usa pods (grupos de contenedores que comparten red) en vez de solo containers.
docker-composeno existe nativo; usaspodman-composeo el nuevopodman compose(v4.x+).- Algunos flags específicos de Docker no existen (ej:
--cap-addtiene equivalentes).
Para WordPress, la migración es casi 1:1. Si sabes Docker, sabes Podman.
Requisitos
- Linux (Fedora, RHEL, CentOS, Rocky, Ubuntu 22.04+, Debian 12+). macOS con
podman machine. - 1 GB RAM mínimo para WordPress + MariaDB, 2 GB recomendado.
- 20 GB de disco mínimo.
- Podman 4.0+ (para
podman composebuilt-in).
Instalar en Fedora/RHEL:
sudo dnf install -y podman podman-compose
Si ya tienes Docker instalado: podman y docker coexisten sin conflictos. Para no romper scripts existentes que esperan el binario docker, agregá alias docker=podman en tu ~/.bashrc.
En Ubuntu/Debian:
# Ubuntu 22.04+ tiene podman en universe
sudo apt update
sudo apt install -y podman
# Si la versión es vieja (< 4.0) y la de universe es < 4.0
. /etc/os-release
curl -fsSL https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/Release.key
| sudo gpg --dearmor --yes -o /etc/apt/keyrings/devel_kubic_libcontainers_stable.gpg
echo "deb [signed-by=/etc/apt/keyrings/devel_kubic_libcontainers_stable.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/ /"
| sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
sudo apt update
sudo apt install -y podman
Verificar:
podman --version
# podman 4.x.x
# Test básico
podman run --rm hello-world
Si hello-world no descarga (timeout o error de registry), verifica DNS y mirror: podman info | grep -i registry. Para sistemas offline, configura mirror en /etc/containers/registries.conf.
Si podman --version no devuelve nada, el binario no está en PATH. verifica con which podman o rpm -ql podman | grep bin/ (RHEL/Fedora) o dpkg -L podman | grep bin/ (Debian/Ubuntu).
Setup rootless
Podman rootless necesita subuid/subgid mapeados para tu usuario:
# Verificar
cat /etc/subuid /etc/subgid
# tu-usuario:100000:65536
Si no aparece tu usuario, agregar:
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 tu-usuario
Límites importantes del modo rootless:
- No puedes bindear puertos < 1024 directamente (usar
unshareo proxy reverso). - Algunos filesystem mounts requieren ajustes.
- Cgroups v2 con delegación habilitada.
WordPress + MariaDB con Podman
Estructura de directorios
mkdir -p ~/wordpress-podman/{data,db,php-conf}
cd ~/wordpress-podman
docker-compose.yml (compatible con podman-compose)
version: "3.8"
services:
db:
image: mariadb:11
container_name: wordpress-db
restart: unless-stopped
environment:
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MARIADB_DATABASE: wordpress
MARIADB_USER: wordpress
MARIADB_PASSWORD: ${DB_PASSWORD}
volumes:
- ./db:/var/lib/mysql:Z
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
wordpress:
image: wordpress:6-php8.3-apache
container_name: wordpress-app
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
WORDPRESS_DB_NAME: wordpress
WORDPRESS_CONFIG_EXTRA: |
define('FS_METHOD', 'direct');
volumes:
- ./data:/var/www/html:Z
ports:
- "8080:80"
proxy:
image: caddy:2-alpine
container_name: wordpress-proxy
restart: unless-stopped
depends_on:
- wordpress
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:Z
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
.env (credenciales)
cat > .env <<'EOF'
DB_ROOT_PASSWORD=tu-root-password-seguro
DB_PASSWORD=tu-db-password-seguro
EOF
chmod 600 .env
Caddyfile (reverse proxy con HTTPS automático)
tu-dominio.com {
reverse_proxy wordpress-app:80
encode gzip
log {
output stdout
}
}
Caddy pide certificado HTTPS a Let's Encrypt automáticamente si el dominio apunta al servidor. Para localhost, usa cert autofirmado.
Levantar el stack
# Con podman-compose
podman-compose up -d
# O con el nuevo podman compose (4.x+)
podman compose up -d
Si el stack arranca pero los containers exit-an inmediatamente, revisa logs con podman-compose logs -t nombre_servicio. Errores comunes: variables de entorno mal pasadas, puertos ya en uso, falta de imagen base.
Verificar:
podman ps
# CONTAINER ID IMAGE STATUS
# abc123 docker.io/library/wordpress Up
# def456 docker.io/library/mariadb Up
# ghi789 docker.io/library/caddy Up
# Logs
podman logs -f wordpress-app
# Acceso
http://localhost:8080 # directo
http://tu-dominio.com # vía Caddy
WordPress setup wizard te guía los primeros pasos.
Persistencia
Lo crítico: dónde viven los datos. Si no entiendes esto, un podman-compose down -v te borra todo.
# Datos de WordPress (uploads, plugins, themes, wp-config.php custom)
./data/
# Base de datos MariaDB
./db/
# Certificados y config de Caddy
volumenes nombrados caddy_data, caddy_config
Los volúmenes nombrados (caddy_data, caddy_config) sobreviven a down, pero -v los borra. Para backups, los directorios bind-mount (./data, ./db) son más fáciles de copiar.
Backup simple:
# Backup completo
tar -czf wordpress-backup-$(date +%Y%m%d).tar.gz ./data ./db .env
# O con mariadb-dump
podman exec wordpress-db mariadb-dump -u root -p"${DB_ROOT_PASSWORD}" wordpress > db-dump-$(date +%Y%m%d).sql
Restore:
podman exec -i wordpress-db mariadb -u root -p"${DB_ROOT_PASSWORD}" wordpress < db-dump.sql
Networking entre contenedores
Compose crea una red con DNS interno. Los servicios se ven por nombre (db, wordpress, proxy). El DNS resuelve automáticamente.
Verificar:
podman exec wordpress-app getent hosts db
# db 10.89.1.3
Si quieres exponer un puerto individual:
ports:
- "127.0.0.1:8080:80" # solo localhost
- "8080:80" # todas las interfaces
En rootless, no puedes bindear < 1024. Workaround:
# Option 1: usé el puerto alto en compose y mappealo a 80 con Caddy
# Option 2: usar capabilities
sudo setcap cap_net_bind_service=+ep /usr/bin/podman
# Option 3: ports en compose son 8080, Caddy hace de proxy y bindea 80
La opción 3 es la más común y limpia: solo Caddy bindea 80/443, los demás servicios están en la red interna.
Pods vs containers
Docker tiene containers. Podman tiene pods (group of containers) y containers. Un pod comparte network namespace (mismo IP, mismo localhost) y opcionalmente volumes.
Para WordPress + MariaDB, pod NO es necesario (cada uno en su container con DNS). Pod es útil para:
- Logs collector + app
- App + sidecar de monitoreo
- App + proxy
Si quieres usar pods:
# Crear pod con WordPress y MariaDB
podman pod create --name wordpress-pod -p 8080:80
# Levantar dentro del pod
podman run -d --pod wordpress-pod --name db -e MARIADB_ROOT_PASSWORD=secret mariadb:11
podman run -d --pod wordpress-pod --name wordpress --env WORDPRESS_DB_HOST=127.0.0.1 wordpress:6
Equivalente al compose pero usando el concepto de pod. Para WordPress, no aporta mucho sobre compose.
Volúmenes y permisos
El :Z al final de los bind mounts es importante en rootless + SELinux:
volumes:
- ./data:/var/www/html:Z
El :Z re-etiqueta el contexto SELinux para que el container pueda acceder. Sin él, en Fedora/RHEL con SELinux enforcing, vas a ver "Permission denied" en los logs.
Permisos comunes:
# WordPress corre como www-data (uid 33) en el container
# Tu usuario en el host es tu-usuario (uid 1000)
# Para que ambos escriban, opciones:
# 1. Que el container escriba todo (recomendado)
# No hace falta ajustar host, dejar que WP se autocree estructura
# 2. ACL (más flexible)
sudo setfacl -R -m u:1000:rwx -m u:33:rwx ./data
# 3. chown (cambia owner al uid del container)
sudo chown -R 33:33 ./data # pero perdés acceso desde host
La opción 1 es la más simple para sitios que se manejan desde el admin de WP. La opción 2 si editas archivos desde el host.
Logs
# Logs de un container
podman logs wordpress-app
# Follow
podman logs -f wordpress-app
# Con timestamp
podman logs -t wordpress-app
# Desde un tiempo
podman logs --since 10m wordpress-app
Logs de systemd (rootless en Fedora/RHEL):
journalctl --user -u podman
journalctl --user CONTAINER_ID=abc123
Actualizar WordPress y plugins
Hay dos caminos: actualizar la imagen (cambio de versión de WP) o actualizar desde el admin de WP (cambia plugins/themes).
Actualizar la imagen:
podman-compose pull
podman-compose up -d # recrea containers con la nueva imagen
WordPress hace la migración de DB automáticamente al primer request (5-30 segundos). Los uploads en ./data persisten.
Actualizar desde admin es lo normal para plugins/themes. Combinado con cron:
# Cron de WP: definir WP-CRON en lugar de cron del sistema
# En wp-config.php:
define('DISABLE_WP_CRON', true);
# Cron del host:
0 */6 * * * curl -s http://tu-sitio.com/wp-cron.php > /dev/null
Más robusto que el WP-Cron default.
Troubleshooting
"Permission denied" en uploads
SELinux. Solución:
# Agregar :Z a los bind mounts
chcon -R -t container_file_t ./data ./db
O desactivar SELinux temporalmente para debug (no en producción):
sudo setenforce 0
"Cannot connect to database"
El container wordpress no resuelve db. Verificar:
# Están en la misma red?
podman network ls
podman network inspect wordpress-podman_default
# El container db tiene la variable de DB_NAME=wordpress?
podman exec wordpress-db env | grep MARIADB
El sitio va lento
- Cache de WP deshabilitado: instalar WP Super Cache o W3 Total Cache.
- DB con muchos queries: instalar Query Monitor plugin.
- Disco lento: si usas HDD, considerar SSD para
./db. - Memoria insuficiente: ver
docker statsopodman stats.
"Out of disk space"
Limpiar imágenes viejas:
podman system prune -a
Cuidado: -a borra TODAS las imágenes no usadas. Si tienes otros containers, mejor selectivo:
podman image prune
El container se reinicia solo
podman logs --tail 100 wordpress-app
# buscar "Fatal" o "Out of memory"
OOM es común. Subir memoria en el compose o limitar otros servicios:
deploy:
resources:
limits:
memory: 1G
Producción: cosas que faltan
Para producción real, agregar:
- HTTPS: ya está con Caddy + Let's Encrypt.
- Backups automatizados: cron +
mariadb-dump+tardel directorio. - Monitoreo: Prometheus + cAdvisor, o Uptime Kuma.
- Updates: Watchtower o cron que corra
podman-compose pull && up -dsemanal. - Firewall: ufw/nftables abriendo solo 80, 443, 22.
- Fail2ban: proteger wp-login.php.
- WAF: ModSecurity + OWASP rules.
- CDN: Cloudflare delante para cachear assets.
- Múltiples réplicas: para alta disponibilidad, usar Kubernetes o Nomad.
Migrar de Docker a Podman
Si ya tienes un setup Docker, migrar es:
# Detener todo
docker-compose down
# Cambiar alias
alias docker=podman
# Levantar con podman
podman-compose up -d
Verificar imágenes: Podman puede usar las imágenes de Docker Hub directo. Los nombres cambian ligeramente (docker.io/library/wordpress vs wordpress), pero el resultado es el mismo.
Para compose files complejos, podman-compose tiene limitaciones vs docker-compose. Si usas features avanzados (build args, secrets, networks custom), revisa la compatibilidad. La mayoría de setups simples migran sin tocar nada.
Conclusión
Podman es una alternativa sólida a Docker para WordPress, especialmente en Fedora/RHEL donde la integración es nativa. El modo rootless resuelve el problema de seguridad del Docker daemon, y los comandos son familiares.
Para empezar: instalar podman, levantar el stack compose, configurar Caddy con tu dominio. Para producción: agregar backups, monitoring, y los hardening usuales.
FAQ
¿Podman es 100% compatible con Docker? Comandos CLI, sí. Algunas features avanzadas de Docker (Docker Swarm, BuildKit con ciertas configs) no tienen equivalente directo. Para WordPress y la mayoría de apps, compatibilidad total.
¿Vale la pena migrar de Docker a Podman? Si Docker te funciona y no tienes problemas, no. Si te preocupa seguridad (rootless) o licensing enterprise, sí.
¿Qué imagen uso, wordpress o wordpress:6-php8.3-apache? La segunda. Pin la versión de PHP y de WP. wordpress:latest puede romper cuando actualizan a PHP 9 o cambian defaults.
¿Cómo hago HTTPS en localhost? Caddy con localhost se auto-firma. Para dev, mkcert y configuración manual. Para prod, dominio real + Caddy + Let's Encrypt.
¿Funciona con Mac (Apple Silicon)? Sí, con podman machine. Hay algunas diferencias de performance vs Linux pero funciona. La integración es menos pulida que Docker Desktop en Mac.
¿Es más rápido o más lento que Docker? Muy similar. Podman es daemonless, así que el primer comando puede tardar más (inicializa el container runtime). Después, performance idéntica.
¿Cómo actualizo Podman? Mismo método que instalaste: dnf update podman o apt upgrade podman.
Fuentes
- https://docs.podman.io/
- https://github.com/containers/podman
- https://docs.podman.io/en/latest/Guides/Compose.html
- https://hub.docker.com/_/wordpress
- https://hub.docker.com/_/mariadb
- https://caddyserver.com/docs/
Preguntas frecuentes
¿Por qué Cómo implementar WordPress en contenedores (Podman) es relevante para sysadmins Linux?
Forma parte del flujo diario de administración. Los conceptos explicados se aplican directamente a servidores en producción y resuelven problemas reales que enfrentan sysadmins hispanohablantes.
¿Qué necesito para empezar con Cómo implementar WordPress en contenedores?
Distribución Linux actualizada (Debian, Ubuntu, RHEL, Fedora o Arch), acceso root, y conexión a internet. Para producción: entorno de staging separado antes de aplicar cambios.
¿Cuáles son los errores más comunes con Cómo implementar WordPress en contenedores?
Los tres más frecuentes: (1) saltar la lectura de logs, (2) aplicar cambios directos en producción sin probar, (3) olvidar permisos y ownership. La documentación y un plan de rollback siempre.
¿Cuándo usar Cómo implementar WordPress en contenedores y cuándo una alternativa?
Esta solución es ideal para el caso descrito. Alternativas como Docker, Ansible o configuración manual pueden ser mejores según contexto: portabilidad, escala y reproducibilidad son los factores clave.
¿Cómo migro desde una configuración previa a Cómo implementar WordPress en contenedores?
Documentar el estado actual, probar la nueva configuración en staging, validar funcionalmente, y aplicar en producción con rollback plan listo. Los posts de SoyLinux incluyen troubleshooting para los problemas más comunes.
Lee también
- Guía Fedora 44: Novedades, Instalación y Upgrade para Sysadmins Linux
- Configura VLANs en Linux con nmcli: Guía paso a paso para sysadmins
- Protege tu SSH: Guía esencial para configurar Fail2ban en Linux
- Guía Definitiva: Configura Kubernetes con kubeadm en Ubuntu para Sysadmins Linux
- Configura Prometheus y Grafana: Guía DevOps para SysAdmins Linux
