diff --git a/gunicorn.conf.py b/gunicorn.conf.py new file mode 100644 index 0000000..53424c9 --- /dev/null +++ b/gunicorn.conf.py @@ -0,0 +1,80 @@ +""" +Gunicorn configuration for StudSpider webhook server +Production-ready settings for running with nginx reverse proxy +""" + +import multiprocessing +import os + +# Server socket +bind = "127.0.0.1:7433" # nginx will proxy to this +backlog = 2048 + +# Worker processes +workers = multiprocessing.cpu_count() * 2 + 1 # Recommended formula +worker_class = "uvicorn.workers.UvicornWorker" # Async worker for FastAPI +worker_connections = 1000 +max_requests = 1000 # Restart workers after this many requests +max_requests_jitter = 50 # Add randomness to prevent all workers restarting at once + +# Timeouts +timeout = 30 # Workers silent for more than this are killed +keepalive = 2 # Wait this long for requests on Keep-Alive connections + +# Logging +accesslog = "./logs/webhook_access.log" +errorlog = "./logs/webhook_error.log" +loglevel = "info" +access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s' + +# Process naming +proc_name = "studspider-webhook" + +# Server mechanics +daemon = False # Run in foreground (systemd will handle daemonization) +pidfile = "./logs/webhook.pid" +umask = 0 +user = None # Run as current user (systemd will set user) +group = None +tmp_upload_dir = None + +# SSL (not needed with nginx, but here for reference) +# keyfile = None +# certfile = None + +# Security +limit_request_line = 4096 +limit_request_fields = 100 +limit_request_field_size = 8190 + +# Debugging (disable in production) +reload = os.getenv("GUNICORN_RELOAD", "false").lower() == "true" +reload_engine = "auto" + + +# Hooks +def on_starting(server): + """Called just before the master process is initialized.""" + print("🚀 StudSpider Webhook Server starting...") + + +def on_reload(server): + """Called to recycle workers during a reload via SIGHUP.""" + print("🔄 Reloading workers...") + + +def when_ready(server): + """Called just after the server is started.""" + print(f"✅ Server ready on {bind}") + print(f"📊 Workers: {workers}") + print(f"🔧 Worker class: {worker_class}") + + +def worker_int(worker): + """Called just after a worker exited on SIGINT or SIGQUIT.""" + print(f"⚠️ Worker {worker.pid} received INT or QUIT signal") + + +def worker_abort(worker): + """Called when a worker received the SIGABRT signal.""" + print(f"❌ Worker {worker.pid} aborted")