🎨 Sysprovider Code
Sysprovider LogoWiki
🇪🇸Hosting español para ecommerce

Integración de Systemd con Servicios Contenerizados (Podman/Docker)

Actualizado el 25 de noviembre de 2025

La gestión de contenedores en entornos de producción ha evolucionado hacia una madurez que exige integraciones nativas con el sistema operativo. En 2025, cualquier SysAdmin que se precie debe dominar la simbiosis entre systemd y los motores de contenedores como Podman y Docker. Ya no basta con lanzar docker run; necesitamos servicios que arranquen con el sistema, se reinicien automáticamente, registren logs correctamente y respeten los límites de recursos. Este artículo es una guía práctica y profunda sobre cómo lograr esa integración.

El contexto: Systemd como el pegamento del sistema

Systemd no es solo un gestor de servicios; es el corazón del arranque y la supervisión de procesos en la mayoría de las distribuciones Linux modernas. Su capacidad para manejar dependencias, reinicios, aislamiento de recursos (cgroups) y logging lo convierte en el compañero ideal para la contenerización.

La diferencia clave entre Docker y Podman radica en su arquitectura: Docker usa un daemon central (dockerd), mientras que Podman es daemonless y ejecuta contenedores como procesos hijos directos del usuario o de systemd. Esta característica hace que Podman sea especialmente atractivo para entornos donde se busca minimizar la superficie de ataque y la complejidad.

[INFO] A partir de 2025, Red Hat ha consolidado Podman como el motor de contenedores predeterminado en RHEL y Fedora. Docker sigue siendo omnipresente, pero la tendencia apunta a la integración nativa con systemd.

Generación automática de unidades systemd para contenedores

La forma más directa de integrar un contenedor con systemd es crear un archivo de unidad .service. Tanto Podman como Docker ofrecen herramientas para facilitar este proceso.

Con Podman: podman generate systemd

Podman incluye un comando específico para generar unidades systemd listas para usar.

# Crear un contenedor que se ejecutará como servicio
podman run -d --name mi-web --restart=always -p 8080:80 nginx:alpine

# Generar la unidad systemd para el usuario (sin sudo)
podman generate systemd --name mi-web --new --files

# Para sistema (root), usar sudo
sudo podman generate systemd --name mi-web --new --files

El flag --new es crucial: le indica a systemd que debe crear un nuevo contenedor cada vez que se inicia el servicio, en lugar de reutilizar uno existente. Esto garantiza un estado limpio en cada arranque.

El archivo generado (/etc/systemd/system/container-mi-web.service o ~/.config/systemd/user/container-mi-web.service) se ve así:

[Unit]
Description=Podman container-mi-web.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStartPre=/bin/rm -f %t/%n.ctr-id
ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --sdnotify=conmon --replace --name mi-web -p 8080:80 nginx:alpine
ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id
ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id
Type=notify
NotifyAccess=all

[Install]
WantedBy=default.target

[TIP] Usa --cgroups=no-conmon para evitar la creación de un cgroup adicional por parte de Podman. Systemd ya gestiona los cgroups del servicio, y esta opción evita conflictos de jerarquía.

Con Docker: Generación manual o herramientas externas

Docker no incluye un generador nativo de unidades systemd. Sin embargo, puedes crearlas manualmente o usar herramientas de terceros como docker-systemd (un script Python).

Una unidad típica para Docker sería:

[Unit]
Description=Docker container mi-app
Requires=docker.service
After=docker.service

[Service]
Restart=always
ExecStart=/usr/bin/docker start -a mi-app
ExecStop=/usr/bin/docker stop -t 10 mi-app
ExecStopPost=/usr/bin/docker rm -f mi-app

[Install]
WantedBy=multi-user.target

Aquí, docker start -a adjunta la salida estándar del contenedor al servicio, permitiendo que systemd capture los logs. Es fundamental que el contenedor ya exista (creado con docker create).

Gestión de logs y supervisión con journald

Una de las grandes ventajas de integrar contenedores con systemd es que los logs se envían automáticamente a journald. Ya no necesitas docker logs o podman logs; puedes usar journalctl para centralizar la información.

# Ver logs del servicio Podman
sudo journalctl -u container-mi-web.service -f

# Filtrar por prioridad (errores)
sudo journalctl -u container-mi-web.service -p err

# Ver logs en tiempo real con formato JSON
sudo journalctl -u container-mi-web.service -o json-pretty -f

Para que los logs lleguen correctamente, el contenedor debe enviar sus salidas a stdout/stderr. La mayoría de las imágenes modernas ya lo hacen. Si no, puedes redirigir manualmente:

# En el Dockerfile
CMD ["sh", "-c", "comando 2>&1 | tee /proc/1/fd/1"]

[WARNING] Si usas docker logs o podman logs junto con systemd, puedes duplicar la salida de logs. Decide una estrategia: o confías en journald o en el motor de contenedores, pero no en ambos simultáneamente.

Notificaciones de estado (sd_notify) y healthchecks

Systemd puede saber cuándo un servicio está realmente listo gracias a sd_notify. Podman soporta esto de forma nativa mediante --sdnotify=conmon. Cuando el contenedor envía la señal READY=1, systemd marca el servicio como activo.

Para Docker, necesitas un pequeño script dentro del contenedor que envíe la notificación:

#!/bin/bash
# Dentro del contenedor
/usr/local/bin/aplicacion &
PID=$!
# Esperar a que la app esté lista (ej: puerto abierto)
while ! nc -z localhost 8080; do sleep 1; done
# Notificar a systemd
kill -s USR1 1
wait $PID

Y en la unidad systemd:

[Service]
Type=notify
NotifyAccess=all
ExecStart=/usr/bin/docker start -a mi-app

Los healthchecks de Docker/Podman también pueden integrarse con systemd. Usa HealthCheck en el Dockerfile o --health-cmd al crear el contenedor. Luego, configura Restart=always y RestartSec=5 para que systemd reinicie el servicio si el healthcheck falla repetidamente.

Gestión de dependencias y orden de arranque

En un entorno con múltiples contenedores (por ejemplo, una base de datos y una aplicación web), necesitas que systemd respete el orden de inicio.

[Unit]
Description=Aplicación web
Requires=container-db.service
After=container-db.service
Wants=network-online.target
After=network-online.target

[Service]
...
  • Requires: la base de datos debe estar activa.
  • After: la aplicación arranca después de la base de datos.
  • Wants: la red debe estar disponible, pero no es crítica.

Para dependencias más complejas, usa PartOf (para detener servicios hijos al detener el padre) o BindsTo (para que el servicio hijo se detenga si el padre falla).

Aislamiento de recursos con systemd (cgroups)

Systemd puede limitar CPU, memoria y E/S de los contenedores sin necesidad de usar los flags de Docker/Podman. Esto es especialmente útil para entornos multiinquilino.

[Service]
MemoryMax=512M
MemoryHigh=400M
CPUQuota=50%
IOWeight=100

Estos límites se aplican a nivel de cgroup del servicio, y el contenedor hereda esas restricciones. Si además defines límites dentro del contenedor (con --memory de Docker), se aplicará el más restrictivo.

[INFO] Systemd usa cgroups v2 por defecto desde 2025. Asegúrate de que tu kernel lo soporte y que Docker/Podman estén configurados para usar cgroupfs o systemd cgroup driver.

Seguridad: hardening de servicios contenerizados

Systemd ofrece potentes opciones de hardening que puedes aplicar a los servicios de contenedores.

[Service]
ProtectSystem=strict
ProtectHome=read-only
PrivateTmp=true
NoNewPrivileges=true
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
SystemCallFilter=@system-service
  • ProtectSystem=strict: el contenedor solo puede escribir en /etc y /var (si se montan como volúmenes).
  • PrivateTmp: cada instancia del servicio tiene su propio /tmp.
  • NoNewPrivileges: impide escalar privilegios dentro del contenedor.
  • CapabilityBoundingSet: limita las capacidades Linux del contenedor.

Para Podman, estas restricciones se suman a las que ya impone podman run (como --cap-drop=ALL). La combinación es muy robusta.

Ejemplo práctico: Despliegue completo con Podman y systemd

Supongamos que queremos desplegar una aplicación web con PostgreSQL, todo gestionado por systemd.

  1. Crear las unidades systemd para cada contenedor:
# PostgreSQL
sudo podman run -d --name postgres-db \
  -e POSTGRES_PASSWORD=secreta \
  -v /data/postgres:/var/lib/postgresql/data \
  docker.io/library/postgres:16

sudo podman generate systemd --name postgres-db --new --files
sudo mv container-postgres-db.service /etc/systemd/system/

# Aplicación web
sudo podman run -d --name web-app \
  --link postgres-db:db \
  -p 80:8080 \
  mi-app:latest

sudo podman generate systemd --name web-app --new --files
sudo mv container-web-app.service /etc/systemd/system/
  1. Editar las unidades para añadir dependencias:

En /etc/systemd/system/container-web-app.service:

[Unit]
Description=Aplicación web
Requires=container-postgres-db.service
After=container-postgres-db.service
  1. Recargar systemd y habilitar servicios:
sudo systemctl daemon-reload
sudo systemctl enable container-postgres-db.service
sudo systemctl enable container-web-app.service
sudo systemctl start container-postgres-db.service
sudo systemctl start container-web-app.service
  1. Verificar el estado:
sudo systemctl status container-web-app.service
sudo journalctl -u container-web-app.service -f

Consideraciones finales para SysAdmin en 2025

La integración de systemd con contenedores no es una moda; es una necesidad para lograr entornos predecibles, seguros y fáciles de mantener. Algunas recomendaciones clave:

  • Prefiere Podman sobre Docker si buscas integración nativa con systemd y un modelo de seguridad más simple.
  • Usa --new en Podman para que systemd cree contenedores frescos en cada inicio, evitando estados corruptos.
  • Centraliza logs en journald y olvídate de los comandos específicos de cada motor.
  • Aplica hardening de systemd a todos los servicios contenerizados, incluso si crees que el contenedor ya es seguro.
  • Prueba la recuperación ante fallos matando el proceso principal del contenedor y verificando que systemd lo reinicia correctamente.

[WARNING] No uses Type=forking en servicios de contenedores. Los contenedores se ejecutan en primer plano, por lo que Type=simple o Type=notify son las opciones correctas.

La gestión de contenedores en Linux ha madurado. Ya no se trata solo de lanzar imágenes, sino de integrarlas profundamente con el sistema operativo. Systemd te da el control que necesitas para que tus contenedores se comporten como servicios de primera clase. Domínalo y tus despliegues serán más robustos, seguros y fáciles de auditar.

¿Necesitas ayuda?Son dos de nuestros técnicos, Agustín y Mikel, y están disponibles para resolver cualquier problema.

Hablar con ellos ahora
Agustín y Mikel