WhatsApp

  

Cómo crear un demonio (daemon) en Linux — guía práctica y amigable

¿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

  1. Corre en primer plano: facilita supervisión por systemd.

  2. Manejo de señales (SIGTERM, SIGINT) para un apagado limpio.

  3. Logging estructurado: usa journald (escribe en stdout/stderr) o archivos rotados; evita llenar /var/log sin rotación.

  4. No privileges by default: ejecuta con un usuario dedicado y mínimo privilegio.

  5. Control de recursos: configura límites en systemd (MemoryMax, CPUQuota).

  6. Reintentos controlados: Restart=on-failure + RestartSec.

  7. Health checks: implementa endpoints o ready-files si necesitas checks externas.

  8. Actualizaciones seguras: usa prácticas de despliegue atómicas (p. ej. systemctl stop → actualizar binario → systemctl start).

  9. 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:

  1. Implementar una aplicación que corra de forma confiable en primer plano (manejo de errores y señales).

  2. Proveer un unit file de systemd para gestión (arranque, reinicio, logs, límites).

  3. Pensar en seguridad, logs y actualizaciones desde el inicio.


 


Cómo crear un demonio (daemon) en Linux — guía práctica y amigable
Paris Minero 16 septiembre, 2025
Compartir
Iniciar sesión dejar un comentario

  
Modulo1 GIT Introducción
Introducción