uwsgi
uwsgi copied to clipboard
uWSGI does not terminate if a hook fails and attach-daemon is used
uwsgi.ini for a minimal repro case:
[uwsgi]
http = 127.0.0.1:8000
hook-pre-app = exec:./does-not-exist
attach-daemon = sleep 5
Output:
[uWSGI] getting INI configuration from uwsgi.ini
*** Starting uWSGI 2.0.30 (64bit) on [Thu Sep 25 14:41:38 2025] ***
compiled with version: 13.3.0 on 20 August 2025 06:12:59
os: Linux-6.8.0-79-generic #79-Ubuntu SMP PREEMPT_DYNAMIC Tue Aug 12 14:42:46 UTC 2025
nodename: oreo
machine: x86_64
clock source: unix
pcre jit disabled
detected number of CPU cores: 8
current working directory: /tmp/a
detected binary path: /tmp/a/.venv/bin/uwsgi
your processes number limit is 126691
your memory page size is 4096 bytes
detected max file descriptor number: 1024
lock engine: pthread robust mutexes
thunder lock: disabled (you can enable it with --thunder-lock)
uWSGI http bound on 127.0.0.1:8000 fd 4
uwsgi socket 0 bound to TCP address 127.0.0.1:35225 (port auto-assigned) fd 3
Python version: 3.12.3 (main, Aug 14 2025, 17:47:21) [GCC 13.3.0]
Python main interpreter initialized at 0x7fc54eca39e8
python threads support enabled
your server socket listen backlog is limited to 100 connections
your mercy for graceful operations on workers is 60 seconds
mapped 145840 bytes (142 KB) for 1 cores
*** Operational MODE: single process ***
running "exec:./does-not-exist" (pre app)...
/bin/sh: 1: ./does-not-exist: not found
command "./does-not-exist" exited with non-zero code: 127
Thu Sep 25 14:41:38 2025 - FATAL hook failed, destroying instance
SIGINT/SIGTERM received...killing workers...
*** no app loaded. going in full dynamic mode ***
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI master process (pid: 59803)
spawned uWSGI worker 1 (pid: 59806, cores: 1)
spawned uWSGI http 1 (pid: 59807)
[uwsgi-daemons] spawning "sleep 5" (uid: 1000 gid: 1000)
daemon "sleep 5" (pid: 59808) annihilated
^Cworker 1 buried after 0 seconds
goodbye to uWSGI.
In the above output, since the ./does-not-exist command failed, uwsgi should have terminated, but it kept running until I pressed Ctrl+C (see ^C in the output).
If I remove the attach-daemon line, then uwsgi successfully quits after the hook failure. There seems to be some interplay with the shutdown process and with the external daemons.
An interesting thing to note is that the sleep 5 daemon was annihilated, and was not restarted. It seems this is because uwsgi is reloading and dying, and makes sense.
For context, the real-world situation where I ran into this was the following:
- I use uWSGI to run a Django app.
- The ini file uses
hook-pre-appto run database migrations (Django'smanage.py migratemanagement command) - It also uses
attach-daemonto run a background processing job (a long-runningmanage.pycommand). - On one occasion, the
hook-pre-appcommand failed (because database connection failed for whatever reason) - But the HTTP listener still and the external daemon still started up
- After running for some time, the external daemon crashed for unrelated reason. It was annihilated and not restarted (because the uWSGI instance was in a perpetual "dying" state)
- At this point from end user's perspective the app still appeared to work – the HTTP interface was still up. But the external daemon was not running any more, making the app appear semi-broken.