Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ ENV TZ=UTC
# Expose port
EXPOSE 9705

# Add health check for Docker
# Add health check for Docker using Python to avoid spawning curl processes
# The SIGCHLD handler in main.py will reap any terminated health check processes
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost:9705/health || exit 1
CMD ["python3", "-c", "import requests; import sys; r = requests.get('http://localhost:9705/api/health', timeout=5); sys.exit(0 if r.status_code == 200 else 1)"]

# Run the main application using the new entry point
CMD ["python3", "main.py"]
24 changes: 22 additions & 2 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,11 +324,28 @@ def run_web_server():
if not stop_event.is_set():
stop_event.set()

def sigchld_handler(signum, frame):
"""Handle SIGCHLD to prevent zombie processes."""
# Reap all terminated child processes without blocking
while True:
try:
# WNOHANG returns immediately if no child has exited
pid, status = os.waitpid(-1, os.WNOHANG)
if pid == 0:
# No more zombie children
break
except ChildProcessError:
# No child processes
break
except OSError:
# Error occurred, stop trying
break

def main_shutdown_handler(signum, frame):
"""Gracefully shut down the application."""
global _global_shutdown_flag
_global_shutdown_flag = True # Set global shutdown flag immediately

signal_name = "SIGINT" if signum == signal.SIGINT else "SIGTERM" if signum == signal.SIGTERM else f"Signal {signum}"
huntarr_logger.info(f"Received {signal_name}. Initiating graceful shutdown...")

Expand Down Expand Up @@ -440,7 +457,10 @@ def main():
# Register signal handlers for graceful shutdown in the main process
signal.signal(signal.SIGINT, main_shutdown_handler)
signal.signal(signal.SIGTERM, main_shutdown_handler)


# Register SIGCHLD handler to prevent zombie processes (Docker healthchecks)
signal.signal(signal.SIGCHLD, sigchld_handler)

# Register cleanup handler
atexit.register(cleanup_handler)

Expand Down