¿Qué es un demonio (daemon)?
Un daemon es un programa que se ejecuta en segundo plano, típicamente sin interacción directa con un usuario, y suele iniciarse en el arranque del sistema o cuando lo necesite otro proceso. Ejemplos: servidores web, servicios de impresión, agentes de sincronización, colectores de métricas, etc.
¿Por qué crear un daemon y cuándo usarlo?
-
Necesitas un servicio siempre disponible (monitoreo, recolección de datos, servidor pequeño).
-
Quieres que algo se ejecute automáticamente al inicio y se reinicie si falla.
-
Requieres control de arranque/parada, logs centralizados y separación de privilegios.
Si sólo vas a ejecutar un script puntual o un cron job ocasional, quizá un daemon sea overkill. Pero para servicios duraderos, es la opción correcta.
Enfoque recomendado (hoy): usa systemd
En la mayoría de distribuciones modernas (Debian, Ubuntu, Fedora, Arch...), el gestor de servicios es systemd. La práctica recomendada no es implementar manualmente el doble-fork clásico, sino escribir un programa claro y añadirle una unidad (.service) para que systemd lo maneje (arranque, reinicios, logs).
Ejemplo completo: daemon en Python + unit file systemd
1) Script Python simple (/usr/local/bin/mi_demonio.py)
#!/usr/bin/env python3 import time import signal import sys import logging LOGFILE = "/var/log/mi_demonio.log" logging.basicConfig(filename=LOGFILE, level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") running = True def handle_sigterm(signum, frame): global running logging.info("Recibido SIGTERM, cerrando...") running = False signal.signal(signal.SIGTERM, handle_sigterm) signal.signal(signal.SIGINT, handle_sigterm) def main(): logging.info("mi_demonio arrancó") while running: # Aquí va la lógica principal del daemon: # p. ej. leer sensores, procesar cola, exponer puerto, etc. logging.info(" trabajando... (ejemplo)") time.sleep(10) logging.info("mi_demonio deteniéndose") if __name__ == "__main__": try: main() except Exception as e: logging.exception("Error inesperado: %s", e) sys.exit(1)
-
Hazlo ejecutable: sudo chmod +x /usr/local/bin/mi_demonio.py
-
Regla: evita daemonizar (double-fork) en el código; deja que systemd supervise el proceso.
2) Unit file systemd (/etc/systemd/system/mi_demonio.service)
[Unit] Description=Mi demonio de ejemplo After=network.target [Service] Type=simple User=miusuario Group=miusuario ExecStart=/usr/bin/env python3 /usr/local/bin/mi_demonio.py Restart=on-failure RestartSec=5 StandardOutput=syslog StandardError=syslog # Optional: limitar recursos # MemoryMax=200M # CPUQuota=50% [Install] WantedBy=multi-user.target
-
Type=simple asume que el proceso corre en primer plano (correcto para el script anterior).
-
Ajusta User/Group a un usuario sin privilegios creado para el servicio.
3) Instalar y probar
sudo systemctl daemon-reload sudo systemctl enable mi_demonio.service # arranque automático sudo systemctl start mi_demonio.service sudo systemctl status mi_demonio.service # Ver logs: journalctl -u mi_demonio.service -f
Si prefieres C: ejemplo mínimo (no daemonizar manualmente)
Es aconsejable tampoco hacer double-fork: compila un binario que corra en primer plano y deja que systemd lo maneje.
// mi_demonio.c (compilar con: gcc -o mi_demonio mi_demonio.c) #include <stdio.h> #include <unistd.h> #include <signal.h> #include <stdatomic.h> static _Atomic int running = 1; void sigint_handler(int s){ running = 0; } int main(void){ signal(SIGINT, sigint_handler); signal(SIGTERM, sigint_handler); while(running){ // trabajo sleep(5); } return 0; }
Empaqueta con un unit file similar.
Buenas prácticas al diseñar un daemon
-
Corre en primer plano: facilita supervisión por systemd.
-
Manejo de señales (SIGTERM, SIGINT) para un apagado limpio.
-
Logging estructurado: usa journald (escribe en stdout/stderr) o archivos rotados; evita llenar /var/log sin rotación.
-
No privileges by default: ejecuta con un usuario dedicado y mínimo privilegio.
-
Control de recursos: configura límites en systemd (MemoryMax, CPUQuota).
-
Reintentos controlados: Restart=on-failure + RestartSec.
-
Health checks: implementa endpoints o ready-files si necesitas checks externas.
-
Actualizaciones seguras: usa prácticas de despliegue atómicas (p. ej. systemctl stop → actualizar binario → systemctl start).
-
Supervisión y métricas: exponer métricas Prometheus o logs estructurados ayuda mucho.
Depuración y testing
-
sudo journalctl -u mi_demonio.service -b para ver logs desde el boot.
-
systemctl status mi_demonio.service muestra última salida y código de retorno.
-
Para pruebas locales, ejecuta el binario/script directamente en tu shell para ver stdout/stderr.
-
Usa systemd-run --user --unit=test mi_comando para pruebas temporales.
Seguridad
-
Evita correr como root. Si necesitas privilegios, usa capacidades puntuales (p. ej. AmbientCapabilities=) o setcap en binarios.
-
Restringe el acceso a ficheros, sockets y puertos.
-
Considera PrivateTmp=true, NoNewPrivileges=true, ProtectSystem=full en el unit file para aislar el servicio.
-
Mantén dependencias actualizadas y valida entradas externas (si tu daemon abre un puerto).
Ejemplo de opciones de hardening en unit:
ProtectSystem=full ProtectHome=yes NoNewPrivileges=true PrivateTmp=true PrivateDevices=true
Windows? (breve)
En Windows el equivalente son los Windows Services. Se crean típicamente en C#/C++ o con sc.exe create y se manejan desde el Service Control Manager. Para Python existe pywin32 y utilidades como nssm para ejecutar scripts como servicio. Pero el flujo, seguridad y herramientas son diferentes; dímelo si quieres un ejemplo para Windows.
Errores comunes
-
Programas que se daemonizan manualmente y luego systemd no puede hacer KillMode/restart correctamente.
-
Logs que crecen sin rotación.
-
Ejecutar como root innecesariamente.
-
No manejar señales → procesos que no terminan bien con systemctl stop.
Conclusión
Crear un daemon hoy significa:
-
Implementar una aplicación que corra de forma confiable en primer plano (manejo de errores y señales).
-
Proveer un unit file de systemd para gestión (arranque, reinicio, logs, límites).
-
Pensar en seguridad, logs y actualizaciones desde el inicio.
Cómo crear un demonio (daemon) en Linux — guía práctica y amigable