python-daemon
python-daemon is a Python library that implements the well-behaved Unix daemon process specification outlined in PEP 3143. It provides a `DaemonContext` class to manage the process environment for a program becoming a daemon, handling aspects like forking, changing directories, setting umask, and redirecting standard file descriptors. The current version is 3.1.2, and it is actively maintained with releases approximately every few months, though code changes are less frequent.
Warnings
- deprecated The `daemon.runner.DaemonRunner` class is deprecated. Many older examples and tutorials might still use it. It's recommended to use `daemon.DaemonContext` directly as a context manager for a more modern and flexible approach.
- gotcha By default, `DaemonContext` closes all open file descriptors and redirects `stdin`, `stdout`, `stderr` to `/dev/null`. This means any `print()` statements or unhandled exceptions will not be visible, leading to silent failures or perceived 'lack of output'.
- gotcha The library is designed for Unix-like operating systems (Linux, macOS) and will not correctly daemonize a process on Windows. Attempts to use it on Windows will generally not result in a background daemon process.
- gotcha By default, `DaemonContext` sets the `umask` to `0` (0o000), which allows maximal file permissions. While this is done to ensure the daemon can create files with desired permissions, it might be too permissive for certain security contexts.
- gotcha PID file management (creating, locking, releasing, and cleaning up the PID file) is crucial for a well-behaved daemon to prevent multiple instances and allow graceful shutdown. Simply creating a file with the PID is not sufficient; a robust locking mechanism is needed.
Install
-
pip install python-daemon
Imports
- DaemonContext
from daemon import DaemonContext
- runner
from daemon import runner
- FileLock
from lockfile import FileLock
Quickstart
import daemon
import time
import logging
import sys
import os
# Optional: for robust PID file management
try:
from lockfile import pidlockfile
except ImportError:
pidlockfile = None
LOG_FILE = '/tmp/my_daemon.log'
PID_FILE = '/tmp/my_daemon.pid'
def do_program_work():
logging.basicConfig(
filename=LOG_FILE,
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger()
logger.info("Daemon started.")
# Example: Keep track of open log file descriptor
# so daemon does not close it.
log_handler_file = None
for handler in logger.handlers:
if hasattr(handler, 'stream') and hasattr(handler.stream, 'fileno'):
log_handler_file = handler.stream.fileno()
break
while True:
logger.info(f"Daemon still running at {time.ctime()}")
time.sleep(5)
if __name__ == '__main__':
# Prepare a PID file object if lockfile is installed
pid_file = None
if pidlockfile:
pid_file = pidlockfile.TimeoutPIDLockFile(PID_FILE)
# Open the daemon context
# Explicitly keep stdout/stderr for debugging, typically redirect to /dev/null or log file
with daemon.DaemonContext(
working_directory='/',
umask=0o002, # Set umask explicitly; 0o022 or 0o027 are common
pidfile=pid_file,
stdout=sys.stdout, # For demonstration, usually redirect to a log file or /dev/null
stderr=sys.stderr, # For demonstration, usually redirect to a log file or /dev/null
files_preserve=[log_handler_file] if log_handler_file else [] # Preserve log file descriptor
):
do_program_work()