systemd en Profundidad: Units, Targets y Dependencias
systemd es el sistema de inicio y gestor de servicios estándar en la mayoría de distribuciones Linux modernas, responsable de arrancar el sistema, gestionar servicios, montar sistemas de archivos y controlar recursos. Más allá del uso básico con systemctl start/stop, systemd ofrece capacidades avanzadas como activación por socket, timers como alternativa a cron, y control de recursos con cgroups. Esta guía cubre en profundidad los tipos de units, la gestión de targets, dependencias y la creación de servicios personalizados.
Requisitos Previos
- Linux con systemd: Ubuntu 16.04+, CentOS 7+, Debian 9+, Rocky Linux 8+
- Conocimientos básicos de
systemctly edición de archivos de texto - Acceso root o privilegios sudo
Tipos de Units en systemd
systemd gestiona el sistema mediante units, cada una con un tipo específico:
| Tipo | Extensión | Propósito |
|---|---|---|
| Service | .service | Procesos y daemons |
| Socket | .socket | Activación por socket IPC/red |
| Target | .target | Agrupación de units (como runlevels) |
| Timer | .timer | Ejecución programada |
| Mount | .mount | Puntos de montaje |
| Path | .path | Monitoreo de rutas del sistema de archivos |
| Slice | .slice | Jerarquía cgroups para recursos |
| Scope | .scope | Procesos externos (no iniciados por systemd) |
# Listar todas las units activas por tipo
systemctl list-units --type=service --state=running
systemctl list-units --type=timer
systemctl list-units --type=socket
# Ver todas las units (incluidas las inactivas y con fallos)
systemctl list-units --all
# Ver units fallidas
systemctl --failed
# Ver información detallada de una unit
systemctl cat nginx.service
systemctl show nginx.service
Anatomía de un archivo .service
# Ver la estructura de un servicio del sistema
systemctl cat sshd.service
# O directamente
cat /lib/systemd/system/ssh.service
Las secciones principales son:
[Unit]
Description=Descripción legible del servicio
After=network.target # Iniciar después de
Requires=other.service # Dependencia estricta
Wants=optional.service # Dependencia suave
[Service]
Type=simple # simple, forking, oneshot, notify, dbus, idle
ExecStart=/usr/bin/mi-app --opciones
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
User=www-data
WorkingDirectory=/var/www
[Install]
WantedBy=multi-user.target
Gestión de Targets
Los targets son unidades que agrupan otras units, equivalentes a los runlevels en SysV init:
# Ver el target por defecto
systemctl get-default
# Cambiar el target por defecto
systemctl set-default multi-user.target # Sin interfaz gráfica
systemctl set-default graphical.target # Con interfaz gráfica
# Targets principales y sus equivalencias
# poweroff.target = runlevel 0 (apagar)
# rescue.target = runlevel 1 (modo rescate, un solo usuario)
# multi-user.target = runlevel 3 (multiusuario sin GUI)
# graphical.target = runlevel 5 (multiusuario con GUI)
# reboot.target = runlevel 6 (reiniciar)
# Cambiar al modo de rescate temporalmente
systemctl isolate rescue.target
# Ver units incluidas en un target
systemctl list-dependencies multi-user.target --no-pager
# Crear un target personalizado
cat > /etc/systemd/system/mi-app.target << 'EOF'
[Unit]
Description=Entorno Mi Aplicación
Requires=multi-user.target
After=multi-user.target
AllowIsolate=yes
EOF
Dependencias entre Units
systemd gestiona el orden de inicio y las dependencias entre units con varias directivas:
Directivas de Dependencia
# Ejemplo completo de dependencias
cat > /etc/systemd/system/ejemplo-dependencias.service << 'EOF'
[Unit]
Description=Ejemplo de dependencias systemd
# ORDEN (no implica dependencia, solo secuencia)
After=network-online.target postgresql.service # Iniciar después de estas units
Before=nginx.service # Iniciar antes que nginx
# DEPENDENCIAS ESTRICTAS (si fallan, esta unit también falla)
Requires=network-online.target # DEBE estar activa
BindsTo=backend.service # Se detiene si backend se detiene
# DEPENDENCIAS SUAVES (intenta iniciarlas, pero no falla si no están disponibles)
Wants=redis.service # Intenta iniciar redis
PartOf=mi-app.target # Parte de este target
# CONFLICTOS (no puede estar activa al mismo tiempo)
Conflicts=modo-mantenimiento.service
[Service]
Type=simple
ExecStart=/usr/bin/mi-aplicacion
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
Visualizar el Grafo de Dependencias
# Ver dependencias de una unit
systemctl list-dependencies nginx.service
systemctl list-dependencies nginx.service --reverse # Ver qué depende de nginx
# Generar grafo de dependencias en formato DOT (requiere graphviz)
systemd-analyze dot nginx.service | dot -Tsvg > /tmp/nginx-deps.svg
# Analizar tiempo de arranque
systemd-analyze
systemd-analyze blame # Tiempo por cada service
systemd-analyze critical-chain # Cadena crítica del arranque
Crear Servicios Personalizados
Servicio Simple (Tipo simple)
# Crear un servicio para una aplicación web Python/Node
cat > /etc/systemd/system/mi-webapp.service << 'EOF'
[Unit]
Description=Mi Aplicación Web
Documentation=https://mi-empresa.com/docs
After=network.target
Wants=network-online.target
[Service]
Type=simple
# Ejecutar como usuario no privilegiado
User=webapp
Group=webapp
WorkingDirectory=/opt/mi-webapp
# Variables de entorno
Environment=NODE_ENV=production
Environment=PORT=3000
EnvironmentFile=-/opt/mi-webapp/.env # El - ignora si no existe
# Comandos
ExecStartPre=/opt/mi-webapp/scripts/pre-start.sh
ExecStart=/usr/bin/node /opt/mi-webapp/server.js
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -TERM $MAINPID
# Reinicio automático
Restart=always
RestartSec=10s
StartLimitInterval=60s
StartLimitBurst=3
# Seguridad
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/opt/mi-webapp/data /var/log/mi-webapp
# Logs
StandardOutput=journal
StandardError=journal
SyslogIdentifier=mi-webapp
[Install]
WantedBy=multi-user.target
EOF
# Habilitar y arrancar el servicio
systemctl daemon-reload
systemctl enable --now mi-webapp.service
systemctl status mi-webapp.service
Servicio Oneshot (Tareas de un solo disparo)
# Útil para scripts de inicialización
cat > /etc/systemd/system/inicializar-db.service << 'EOF'
[Unit]
Description=Inicializar base de datos
After=postgresql.service
Requires=postgresql.service
[Service]
Type=oneshot
RemainAfterExit=yes # El servicio queda como "activo" después de ejecutar
User=postgres
ExecStart=/usr/local/bin/init-database.sh
[Install]
WantedBy=multi-user.target
EOF
Service con Instancias (@)
# Un servicio parametrizado con instancias múltiples
cat > /etc/systemd/system/[email protected] << 'EOF'
[Unit]
Description=Worker %i de la aplicación
After=network.target
[Service]
Type=simple
User=worker
ExecStart=/opt/app/worker.py --id=%i --queue=queue_%i
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
# Iniciar múltiples instancias del worker
systemctl enable --now [email protected]
systemctl enable --now [email protected]
systemctl enable --now [email protected]
# Ver estado de todas las instancias
systemctl status 'worker@*.service'
Activación por Socket
La activación por socket permite que systemd escuche en un socket e inicie el servicio solo cuando llegue una conexión, reduciendo el uso de recursos:
# Crear el socket unit
cat > /etc/systemd/system/mi-api.socket << 'EOF'
[Unit]
Description=Socket para Mi API
PartOf=mi-api.service
[Socket]
ListenStream=127.0.0.1:8080 # Puerto TCP
Accept=false # Un servicio maneja todas las conexiones
[Install]
WantedBy=sockets.target
EOF
# El servicio correspondiente
cat > /etc/systemd/system/mi-api.service << 'EOF'
[Unit]
Description=Mi API (activada por socket)
Requires=mi-api.socket
After=mi-api.socket
[Service]
Type=simple
ExecStart=/opt/mi-api/server
StandardInput=socket # Recibir conexiones del socket
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
# Habilitar solo el socket (inicia el servicio automáticamente al haber conexiones)
systemctl enable --now mi-api.socket
systemd Timers
Los timers de systemd son una alternativa poderosa a cron con mejor integración y logging:
# Crear un timer para backup diario
cat > /etc/systemd/system/backup-diario.service << 'EOF'
[Unit]
Description=Backup diario del sistema
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=backup
EOF
cat > /etc/systemd/system/backup-diario.timer << 'EOF'
[Unit]
Description=Timer para backup diario
Requires=backup-diario.service
[Timer]
# Ejecutar todos los días a las 2:30 AM
OnCalendar=*-*-* 02:30:00
# Añadir aleatoriedad para evitar picos (hasta 30 min de retraso)
RandomizedDelaySec=1800
# Ejecutar si se perdió el momento (el sistema estaba apagado)
Persistent=true
Unit=backup-diario.service
[Install]
WantedBy=timers.target
EOF
# Habilitar el timer
systemctl enable --now backup-diario.timer
# Ver todos los timers y cuándo se ejecutarán
systemctl list-timers
# Sintaxis de OnCalendar
# minutely = *-*-* *:*:00
# hourly = *-*-* *:00:00
# daily = *-*-* 00:00:00
# weekly = Mon *-*-* 00:00:00
# monthly = *-*-01 00:00:00
Control de Recursos con systemd
systemd integra cgroups para limitar recursos por servicio:
# Limitar recursos de un servicio
cat > /etc/systemd/system/mi-servicio-limitado.service << 'EOF'
[Unit]
Description=Servicio con límites de recursos
[Service]
Type=simple
ExecStart=/opt/mi-app/server
# Límites de CPU
CPUQuota=50% # Máximo 50% de un núcleo CPU
CPUWeight=100 # Peso relativo (defecto: 100)
# Límites de memoria
MemoryMax=512M # Máximo 512 MB de RAM
MemorySwapMax=0 # Sin swap
# Límites de I/O
IOWeight=100 # Peso de I/O relativo
IOReadBandwidthMax=/dev/sda 50M # Máximo 50 MB/s de lectura
IOWriteBandwidthMax=/dev/sda 10M # Máximo 10 MB/s de escritura
# Límites de procesos/hilos
TasksMax=50 # Máximo 50 tareas (threads + procesos)
EOF
# Modificar límites sin editar el archivo (override temporal)
systemctl set-property mi-servicio-limitado.service CPUQuota=25%
systemctl set-property mi-servicio-limitado.service MemoryMax=256M
# Ver uso de recursos actual
systemctl status mi-servicio-limitado.service
systemd-cgtop # Monitor en tiempo real de recursos por cgroup
Solución de Problemas
Servicio que no inicia y no hay logs claros:
# Ver el estado detallado
systemctl status mi-servicio.service -l
# Ver todos los logs del servicio desde el inicio
journalctl -u mi-servicio.service --no-pager
# Ver solo los últimos errores
journalctl -u mi-servicio.service -p err -n 50
# Verificar que el archivo de servicio no tiene errores de sintaxis
systemd-analyze verify mi-servicio.service
Error "Unit not found" al usar systemctl:
# Verificar que el archivo existe
ls -la /etc/systemd/system/mi-servicio.service
# Recargar la configuración de systemd
systemctl daemon-reload
# Ver si systemd reconoce el servicio
systemctl list-unit-files | grep mi-servicio
Servicio que se reinicia en bucle:
# Ver el historial de inicios y fallos
journalctl -u mi-servicio.service -n 100
# Ajustar la política de reinicio para diagnóstico
systemctl edit mi-servicio.service
# Añadir: [Service]
# Restart=no
# Reintentar manualmente
systemctl start mi-servicio.service
Timer no se ejecuta en el momento esperado:
systemctl list-timers --all
# Verificar la sintaxis OnCalendar
systemd-analyze calendar "*-*-* 02:30:00"
# Verificar que el timer está habilitado y activo
systemctl is-enabled backup-diario.timer
systemctl is-active backup-diario.timer
Conclusión
systemd es mucho más que un gestor de servicios: es un framework completo para la gestión del sistema Linux que incluye resolución de dependencias, activación por demanda, timers avanzados y control de recursos integrado. Dominar los tipos de units, las directivas de dependencia y las opciones de seguridad de servicios permite crear infraestructuras más robustas y fáciles de mantener. La integración nativa con journald hace que el diagnóstico de problemas sea significativamente más eficiente que con sistemas de init anteriores.


