1 Daemon Mode
Virgil edited this page 2026-02-23 04:54:00 +00:00

Daemon Mode

Execution Modes

mode := cli.DetectMode()
// cli.ModeInteractive — TTY attached, colours enabled
// cli.ModePipe         — stdout piped, colours disabled
// cli.ModeDaemon       — CORE_DAEMON=1, log-only output

cli.IsTTY()       // stdout is terminal?
cli.IsStdinTTY()  // stdin is terminal?

Simple Daemon

func runDaemon(cmd *cli.Command, args []string) error {
    ctx := cli.Context() // Cancelled on SIGINT/SIGTERM
    // ... start work ...
    return cli.Run(ctx) // Blocks until signal
}

Full Daemon with Health Checks

daemon := cli.NewDaemon(cli.DaemonOptions{
    PIDFile:         "/var/run/myapp.pid",
    ShutdownTimeout: 30 * time.Second,
    HealthAddr:      ":9090",
    HealthChecks:    []cli.HealthCheck{checkDB},
})

if err := daemon.Start(); err != nil {
    return err // "another instance is running (PID 1234)"
}

daemon.SetReady(true)
return daemon.Run(cli.Context()) // Blocks, handles shutdown

Health Endpoints

  • GET /health — Liveness (200 if server up, 503 if checks fail)
  • GET /ready — Readiness (200 if ready, 503 if not)

PID File

pid := cli.NewPIDFile("/tmp/myapp.pid")
if err := pid.Acquire(); err != nil {
    // "another instance is running (PID 1234)"
}
defer pid.Release()

Shutdown with Timeout

cli.Init(cli.Options{AppName: "worker"})
shutdown := cli.RunWithTimeout(30 * time.Second)
defer shutdown() // Replaces cli.Shutdown()
cli.Run(cli.Context())

Signal Handling

Built into the runtime — SIGINT/SIGTERM cancel cli.Context(). Optional SIGHUP for config reload:

cli.Init(cli.Options{
    AppName: "daemon",
    OnReload: func() error {
        return reloadConfig()
    },
})