From 23c80bcda950af387ffdaccc42f14c9255d5f7b7 Mon Sep 17 00:00:00 2001 From: Ayush Gupta Date: Sat, 28 Feb 2026 05:23:41 +0000 Subject: [PATCH 1/4] feat: migrate from uWSGI to Gunicorn - Replace uWSGI/uwsgitop with Gunicorn in project requirements. - Add Gunicorn configuration with 12 workers and 2 threads for high concurrency. - Update Nginx to use HTTP proxying instead of uWSGI protocol. - Refactor entrypoint with exec for proper signal handling. - Modernize healthchecks to use HTTP-based validation. - Retain 'uwsgi' service naming for backward compatibility. --- configuration/gunicorn.conf.py | 23 +++++++++++++++++++++ configuration/intel_owl.ini | 29 --------------------------- configuration/nginx/locations.conf | 28 ++++++++++++++++---------- docker/Dockerfile | 4 ++++ docker/default.yml | 4 ++-- docker/entrypoints/uwsgi.sh | 6 +++--- requirements/project-requirements.txt | 3 +-- 7 files changed, 50 insertions(+), 47 deletions(-) create mode 100644 configuration/gunicorn.conf.py delete mode 100644 configuration/intel_owl.ini diff --git a/configuration/gunicorn.conf.py b/configuration/gunicorn.conf.py new file mode 100644 index 0000000000..f36fdf759b --- /dev/null +++ b/configuration/gunicorn.conf.py @@ -0,0 +1,23 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +# Gunicorn configuration file for IntelOwl + +bind = "0.0.0.0:8001" +worker_class = "gthread" + +workers = 12 +threads = 2 + +timeout = 600 + +max_requests = 1000 +max_requests_jitter = 50 +keepalive = 5 +preload_app = True + +accesslog = "/var/log/intel_owl/gunicorn/intel_owl_access.log" +errorlog = "/var/log/intel_owl/gunicorn/intel_owl_errors.log" +loglevel = "info" + +proc_name = "intel_owl_gunicorn" diff --git a/configuration/intel_owl.ini b/configuration/intel_owl.ini deleted file mode 100644 index b0bed5cdce..0000000000 --- a/configuration/intel_owl.ini +++ /dev/null @@ -1,29 +0,0 @@ -[uwsgi] -project = intel_owl -base = /opt/deploy/intel_owl - -chdir = %(base) -module = %(project).wsgi:application - -master = true -processes = 12 -enable-threads = true - -socket = 0.0.0.0:8001 -chown = www-data:www-data -vacuum = true -single-interpreter = true -harakiri = 300 - -logto = /var/log/intel_owl/uwsgi/intel_owl.log -uid = www-data -gid = www-data - -max-requests = 1000 -max-worker-lifetime = 3600 -reload-on-rss = 2048 -worker-reload-mercy = 3600 - -buffer-size = 32768 - -need-app = true \ No newline at end of file diff --git a/configuration/nginx/locations.conf b/configuration/nginx/locations.conf index 0ce1815107..0cd99d5ec5 100644 --- a/configuration/nginx/locations.conf +++ b/configuration/nginx/locations.conf @@ -7,14 +7,17 @@ location /hc { return 200; } -# All requests to the Django/UWSGI server. +# All requests to the Django/Gunicorn server. location / { root /; - uwsgi_pass django; - uwsgi_pass_header Authorization; - uwsgi_pass_request_headers on; - uwsgi_read_timeout 600; - include uwsgi_params; + proxy_pass http://django; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_redirect off; + proxy_read_timeout 600; client_max_body_size 100m; } @@ -23,11 +26,14 @@ location / { location ^~/admin { limit_req zone=adminlimit; - uwsgi_pass django; - uwsgi_pass_header Authorization; - uwsgi_pass_request_headers on; - uwsgi_read_timeout 600; - include uwsgi_params; + proxy_pass http://django; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_redirect off; + proxy_read_timeout 600; client_max_body_size 100m; } diff --git a/docker/Dockerfile b/docker/Dockerfile index b6a02e2f12..dd274e3ff0 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -31,6 +31,7 @@ SHELL ["/bin/bash", "-c"] RUN mkdir -p ${LOG_PATH} \ ${LOG_PATH}/django \ ${LOG_PATH}/uwsgi \ + ${LOG_PATH}/gunicorn \ ${LOG_PATH}/asgi \ /opt/deploy/files_required /opt/deploy/configuration /opt/deploy/files_required/blint /opt/deploy/files_required/yara @@ -81,4 +82,7 @@ COPY --from=frontend-build /build /var/www/reactapp ENV HOME="${PYTHONPATH}" ENV XDG_CACHE_HOME="${PYTHONPATH}/.cache" +# Default command to run Gunicorn +CMD ["gunicorn", "intel_owl.wsgi:application", "-c", "/opt/deploy/intel_owl/configuration/gunicorn.conf.py"] + diff --git a/docker/default.yml b/docker/default.yml index 9f4007cfd9..b564eb431e 100644 --- a/docker/default.yml +++ b/docker/default.yml @@ -7,7 +7,7 @@ services: image: intelowlproject/intelowl:${REACT_APP_INTELOWL_VERSION} container_name: intelowl_uwsgi volumes: - - ../configuration/intel_owl.ini:/etc/uwsgi/sites/intel_owl.ini # uwsgi configuration file + - ../configuration/gunicorn.conf.py:/opt/deploy/intel_owl/configuration/gunicorn.conf.py - ../configuration:/opt/deploy/intel_owl/configuration - generic_logs:/var/log/intel_owl - static_content:/opt/deploy/intel_owl/static @@ -20,7 +20,7 @@ services: - env_file_app - .env healthcheck: - test: [ "CMD-SHELL", "nc -z localhost 8001 || exit 1" ] + test: [ "CMD-SHELL", "curl -f http://localhost:8001/hc || exit 1" ] interval: 5s timeout: 2s start_period: 300s diff --git a/docker/entrypoints/uwsgi.sh b/docker/entrypoints/uwsgi.sh index c1255beda2..9375e042ce 100755 --- a/docker/entrypoints/uwsgi.sh +++ b/docker/entrypoints/uwsgi.sh @@ -4,8 +4,8 @@ until cd /opt/deploy/intel_owl do echo "Waiting for server volume..." done -mkdir -p /var/log/intel_owl/django /var/log/intel_owl/uwsgi /var/log/intel_owl/asgi /opt/deploy/files_required/blint /opt/deploy/files_required/yara -chown -R www-data:www-data /var/log/intel_owl/django /var/log/intel_owl/uwsgi /var/log/intel_owl/asgi /opt/deploy/files_required/blint /opt/deploy/files_required/yara +mkdir -p /var/log/intel_owl/django /var/log/intel_owl/uwsgi /var/log/intel_owl/gunicorn /var/log/intel_owl/asgi /opt/deploy/files_required/blint /opt/deploy/files_required/yara +chown -R www-data:www-data /var/log/intel_owl/django /var/log/intel_owl/uwsgi /var/log/intel_owl/gunicorn /var/log/intel_owl/asgi /opt/deploy/files_required/blint /opt/deploy/files_required/yara # Apply database migrations echo "Waiting for db to be ready..." @@ -49,5 +49,5 @@ then else $CHANGELOG_NOTIFICATION_COMMAND $ELASTIC_TEMPLATE_COMMAND - /usr/local/bin/uwsgi --ini /etc/uwsgi/sites/intel_owl.ini --stats 127.0.0.1:1717 --stats-http + exec gunicorn intel_owl.wsgi:application -c /opt/deploy/intel_owl/configuration/gunicorn.conf.py fi diff --git a/requirements/project-requirements.txt b/requirements/project-requirements.txt index 131c5da28d..a4e9e3c684 100644 --- a/requirements/project-requirements.txt +++ b/requirements/project-requirements.txt @@ -29,8 +29,7 @@ dataclasses==0.6 # https://github.com/advisories/GHSA-q4qm-xhf9-4p8f # unpatched CVE: noproblem, we just use this for debugging purposes flower==2.0.0 -uWSGI==2.0.28 -uwsgitop==0.12 +gunicorn==23.0.0 whitenoise==6.9.0 daphne==4.2.1 channels==4.1.0 From 9037fc6104f13a08cc4d9b87af9019d57abccca4 Mon Sep 17 00:00:00 2001 From: Ayush Gupta Date: Sat, 28 Feb 2026 12:00:09 +0000 Subject: [PATCH 2/4] Structure Rename uwsgi to Gunicorn --- .../management/commands/elastic_templates.py | 2 +- configuration/nginx/django_server.conf | 4 ++-- configuration/nginx/http.conf | 2 +- configuration/nginx/https.conf | 2 +- docker/Dockerfile | 1 - docker/ci.override.yml | 2 +- docker/default.yml | 14 +++++------ docker/elasticsearch.override.yml | 2 +- docker/entrypoints/{uwsgi.sh => gunicorn.sh} | 4 ++-- docker/flower.override.yml | 4 ++-- docker/multi-queue.override.yml | 8 +++---- docker/nfs.override.yml | 2 +- docker/postgres.override.yml | 2 +- docker/rabbitmq.override.yml | 2 +- docker/redis.override.yml | 2 +- docker/scripts/initdb.sh | 6 ++--- docker/scripts/manage.sh | 2 +- docker/scripts/tail-logs.sh | 4 ++-- docker/sqs.override.yml | 2 +- docker/test.flower.override.yml | 9 ++++++- docker/test.multi-queue.override.yml | 5 ++++ docker/test.override.yml | 2 +- integrations/bbot/compose.yml | 2 +- integrations/cyberchef/compose.yml | 2 +- .../malware_tools_analyzers/compose.yml | 2 +- integrations/nuclei_analyzer/compose.yml | 2 +- integrations/pcap_analyzers/compose.yml | 2 +- integrations/phishing_analyzers/compose.yml | 2 +- integrations/phoneinfoga/compose.yml | 24 +++++++++---------- integrations/phunter/compose.yml | 2 +- integrations/thug/compose.yml | 2 +- integrations/tor_analyzers/compose.yml | 2 +- intel_owl/settings/websocket.py | 2 +- 33 files changed, 69 insertions(+), 58 deletions(-) rename docker/entrypoints/{uwsgi.sh => gunicorn.sh} (87%) diff --git a/api_app/management/commands/elastic_templates.py b/api_app/management/commands/elastic_templates.py index afb6495d1c..e401b45fae 100644 --- a/api_app/management/commands/elastic_templates.py +++ b/api_app/management/commands/elastic_templates.py @@ -9,7 +9,7 @@ class Command(BaseCommand): - # NOTE: this command is runned by uwsgi startup script + # NOTE: this command is run by gunicorn startup script help = "Create or update the index templates in Elasticsearch" diff --git a/configuration/nginx/django_server.conf b/configuration/nginx/django_server.conf index 8a799489ca..e5398f295c 100644 --- a/configuration/nginx/django_server.conf +++ b/configuration/nginx/django_server.conf @@ -10,14 +10,14 @@ server { log_not_found off; } - # All requests to the Django/UWSGI server. + # All requests to the Django/Gunicorn server. location / { proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Url-Scheme $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; - proxy_pass http://uwsgi:8001; + proxy_pass http://gunicorn:8001; client_max_body_size 100m; } diff --git a/configuration/nginx/http.conf b/configuration/nginx/http.conf index ba2da6069f..93e398dfe4 100644 --- a/configuration/nginx/http.conf +++ b/configuration/nginx/http.conf @@ -1,6 +1,6 @@ # the upstream component nginx needs to connect to upstream django { - server uwsgi:8001 fail_timeout=30s; + server gunicorn:8001 fail_timeout=30s; } limit_req_zone $binary_remote_addr zone=adminlimit:10m rate=1r/s; diff --git a/configuration/nginx/https.conf b/configuration/nginx/https.conf index 5abfbad906..fe4beeb15c 100644 --- a/configuration/nginx/https.conf +++ b/configuration/nginx/https.conf @@ -1,6 +1,6 @@ # the upstream component nginx needs to connect to upstream django { - server uwsgi:8001 fail_timeout=30s; + server gunicorn:8001 fail_timeout=30s; } server { diff --git a/docker/Dockerfile b/docker/Dockerfile index dd274e3ff0..2788e1749f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -30,7 +30,6 @@ SHELL ["/bin/bash", "-c"] RUN mkdir -p ${LOG_PATH} \ ${LOG_PATH}/django \ - ${LOG_PATH}/uwsgi \ ${LOG_PATH}/gunicorn \ ${LOG_PATH}/asgi \ /opt/deploy/files_required /opt/deploy/configuration /opt/deploy/files_required/blint /opt/deploy/files_required/yara diff --git a/docker/ci.override.yml b/docker/ci.override.yml index 834068424c..fcd4ecc546 100644 --- a/docker/ci.override.yml +++ b/docker/ci.override.yml @@ -8,7 +8,7 @@ services: cpus: '1' memory: 2000M - uwsgi: + gunicorn: build: context: .. dockerfile: docker/Dockerfile diff --git a/docker/default.yml b/docker/default.yml index b564eb431e..ae3274caf2 100644 --- a/docker/default.yml +++ b/docker/default.yml @@ -3,9 +3,9 @@ x-no-healthcheck: &no-healthcheck disable: true services: - uwsgi: + gunicorn: image: intelowlproject/intelowl:${REACT_APP_INTELOWL_VERSION} - container_name: intelowl_uwsgi + container_name: intelowl_gunicorn volumes: - ../configuration/gunicorn.conf.py:/opt/deploy/intel_owl/configuration/gunicorn.conf.py - ../configuration:/opt/deploy/intel_owl/configuration @@ -13,7 +13,7 @@ services: - static_content:/opt/deploy/intel_owl/static - shared_files:/opt/deploy/files_required entrypoint: - - ./docker/entrypoints/uwsgi.sh + - ./docker/entrypoints/gunicorn.sh expose: - "8001" env_file: @@ -46,7 +46,7 @@ services: start_period: 2s retries: 6 depends_on: - uwsgi: + gunicorn: condition: service_healthy nginx: @@ -61,7 +61,7 @@ services: - nginx_logs:/var/log/nginx - static_content:/var/www/static depends_on: - uwsgi: + gunicorn: condition: service_healthy daphne: condition: service_healthy @@ -80,7 +80,7 @@ services: - env_file_app <<: *no-healthcheck depends_on: - uwsgi: + gunicorn: condition: service_healthy @@ -103,7 +103,7 @@ services: env_file: - env_file_app depends_on: - uwsgi: + gunicorn: condition: service_healthy <<: *no-healthcheck diff --git a/docker/elasticsearch.override.yml b/docker/elasticsearch.override.yml index c17b76ccac..ee36923af3 100644 --- a/docker/elasticsearch.override.yml +++ b/docker/elasticsearch.override.yml @@ -1,5 +1,5 @@ services: - uwsgi: + gunicorn: depends_on: elasticsearch: condition: service_healthy diff --git a/docker/entrypoints/uwsgi.sh b/docker/entrypoints/gunicorn.sh similarity index 87% rename from docker/entrypoints/uwsgi.sh rename to docker/entrypoints/gunicorn.sh index 9375e042ce..2a435051dd 100755 --- a/docker/entrypoints/uwsgi.sh +++ b/docker/entrypoints/gunicorn.sh @@ -4,8 +4,8 @@ until cd /opt/deploy/intel_owl do echo "Waiting for server volume..." done -mkdir -p /var/log/intel_owl/django /var/log/intel_owl/uwsgi /var/log/intel_owl/gunicorn /var/log/intel_owl/asgi /opt/deploy/files_required/blint /opt/deploy/files_required/yara -chown -R www-data:www-data /var/log/intel_owl/django /var/log/intel_owl/uwsgi /var/log/intel_owl/gunicorn /var/log/intel_owl/asgi /opt/deploy/files_required/blint /opt/deploy/files_required/yara +mkdir -p /var/log/intel_owl/django /var/log/intel_owl/gunicorn /var/log/intel_owl/asgi /opt/deploy/files_required/blint /opt/deploy/files_required/yara +chown -R www-data:www-data /var/log/intel_owl/django /var/log/intel_owl/gunicorn /var/log/intel_owl/asgi /opt/deploy/files_required/blint /opt/deploy/files_required/yara # Apply database migrations echo "Waiting for db to be ready..." diff --git a/docker/flower.override.yml b/docker/flower.override.yml index 3415def544..a2de3d6de2 100644 --- a/docker/flower.override.yml +++ b/docker/flower.override.yml @@ -1,5 +1,5 @@ services: - uwsgi: + gunicorn: environment: - BROKER_URL_API=http://guest:guest@rabbitmq:15672/api/ @@ -25,7 +25,7 @@ services: - "5555" depends_on: - rabbitmq - - uwsgi + - gunicorn entrypoint: - ./docker/entrypoints/flower.sh env_file: diff --git a/docker/multi-queue.override.yml b/docker/multi-queue.override.yml index 7b60ca5123..8fa5a9cd0b 100644 --- a/docker/multi-queue.override.yml +++ b/docker/multi-queue.override.yml @@ -3,7 +3,7 @@ x-no-healthcheck: &no-healthcheck disable: true services: - uwsgi: + gunicorn: environment: - CELERY_QUEUES=default,local,long @@ -22,7 +22,7 @@ services: env_file: - env_file_app depends_on: - uwsgi: + gunicorn: condition: service_healthy <<: *no-healthcheck @@ -41,7 +41,7 @@ services: env_file: - env_file_app depends_on: - uwsgi: + gunicorn: condition: service_healthy <<: *no-healthcheck @@ -60,6 +60,6 @@ services: env_file: - env_file_app depends_on: - uwsgi: + gunicorn: condition: service_healthy <<: *no-healthcheck diff --git a/docker/nfs.override.yml b/docker/nfs.override.yml index 2978962330..eea6f5d458 100644 --- a/docker/nfs.override.yml +++ b/docker/nfs.override.yml @@ -1,5 +1,5 @@ services: - uwsgi: + gunicorn: volumes: - nfs_files:/opt/deploy/files_required environment: diff --git a/docker/postgres.override.yml b/docker/postgres.override.yml index 9f074ffc03..001c91b059 100644 --- a/docker/postgres.override.yml +++ b/docker/postgres.override.yml @@ -15,7 +15,7 @@ services: start_period: 3s - uwsgi: + gunicorn: depends_on: postgres: condition: service_healthy diff --git a/docker/rabbitmq.override.yml b/docker/rabbitmq.override.yml index b947862c27..14f0d23063 100644 --- a/docker/rabbitmq.override.yml +++ b/docker/rabbitmq.override.yml @@ -7,7 +7,7 @@ services: logging: driver: none - uwsgi: + gunicorn: environment: - BROKER_URL="amqp://guest:guest@rabbitmq:5672" depends_on: diff --git a/docker/redis.override.yml b/docker/redis.override.yml index 95d0d7a115..5cc6d37883 100644 --- a/docker/redis.override.yml +++ b/docker/redis.override.yml @@ -1,5 +1,5 @@ services: - uwsgi: + gunicorn: depends_on: redis: condition: service_healthy diff --git a/docker/scripts/initdb.sh b/docker/scripts/initdb.sh index 928292fc60..89a2659c58 100755 --- a/docker/scripts/initdb.sh +++ b/docker/scripts/initdb.sh @@ -1,5 +1,5 @@ #!/bin/bash -docker exec intelowl_uwsgi python3 manage.py makemigrations -docker exec intelowl_uwsgi python3 manage.py migrate -docker exec -ti intelowl_uwsgi python3 manage.py createsuperuser \ +docker exec intelowl_gunicorn python3 manage.py makemigrations +docker exec intelowl_gunicorn python3 manage.py migrate +docker exec -ti intelowl_gunicorn python3 manage.py createsuperuser \ --username admin --email admin@admin.com --first_name admin --last_name admin diff --git a/docker/scripts/manage.sh b/docker/scripts/manage.sh index c9e7c0b3dd..2b37c9405b 100755 --- a/docker/scripts/manage.sh +++ b/docker/scripts/manage.sh @@ -1,2 +1,2 @@ #!/bin/bash -docker exec -ti intelowl_uwsgi python3 manage.py "$@" +docker exec -ti intelowl_gunicorn python3 manage.py "$@" diff --git a/docker/scripts/tail-logs.sh b/docker/scripts/tail-logs.sh index d3a2e5a343..9143bfa782 100755 --- a/docker/scripts/tail-logs.sh +++ b/docker/scripts/tail-logs.sh @@ -1,4 +1,4 @@ #!/bin/bash -docker exec intelowl_uwsgi ls -al /var/log/intel_owl/"$1" -docker exec -ti intelowl_uwsgi tail -f /var/log/intel_owl/"$1" +docker exec intelowl_gunicorn ls -al /var/log/intel_owl/"$1" +docker exec -ti intelowl_gunicorn tail -f /var/log/intel_owl/"$1" diff --git a/docker/sqs.override.yml b/docker/sqs.override.yml index c32f7ab2a4..fc94d659b3 100644 --- a/docker/sqs.override.yml +++ b/docker/sqs.override.yml @@ -1,5 +1,5 @@ services: - uwsgi: + gunicorn: environment: - AWS_SQS=True - BROKER_URL=sqs:// diff --git a/docker/test.flower.override.yml b/docker/test.flower.override.yml index 3633deb195..3dd8fb0f6d 100644 --- a/docker/test.flower.override.yml +++ b/docker/test.flower.override.yml @@ -1,5 +1,12 @@ services: + gunicorn: + image: intelowlproject/intelowl:test + volumes: + - ../:/opt/deploy/intel_owl + flower: image: intelowlproject/intelowl:test volumes: - - ../:/opt/deploy/intel_owl \ No newline at end of file + - ../:/opt/deploy/intel_owl + depends_on: + - gunicorn \ No newline at end of file diff --git a/docker/test.multi-queue.override.yml b/docker/test.multi-queue.override.yml index 20521ece09..1a26bb1724 100644 --- a/docker/test.multi-queue.override.yml +++ b/docker/test.multi-queue.override.yml @@ -1,4 +1,9 @@ services: + gunicorn: + image: intelowlproject/intelowl:test + volumes: + - ../:/opt/deploy/intel_owl + celery_worker_local: image: intelowlproject/intelowl:test volumes: diff --git a/docker/test.override.yml b/docker/test.override.yml index 1fb7b8955f..5656e9435c 100644 --- a/docker/test.override.yml +++ b/docker/test.override.yml @@ -1,5 +1,5 @@ services: - uwsgi: + gunicorn: build: context: .. dockerfile: docker/Dockerfile diff --git a/integrations/bbot/compose.yml b/integrations/bbot/compose.yml index 8df7e1ab8d..3e8fa849c4 100644 --- a/integrations/bbot/compose.yml +++ b/integrations/bbot/compose.yml @@ -8,4 +8,4 @@ services: volumes: - generic_logs:/var/log/intel_owl depends_on: - - uwsgi + - gunicorn diff --git a/integrations/cyberchef/compose.yml b/integrations/cyberchef/compose.yml index a9c6e60610..0ecf0c43ea 100644 --- a/integrations/cyberchef/compose.yml +++ b/integrations/cyberchef/compose.yml @@ -6,4 +6,4 @@ services: expose: - '3000' depends_on: - - uwsgi + - gunicorn diff --git a/integrations/malware_tools_analyzers/compose.yml b/integrations/malware_tools_analyzers/compose.yml index 102f5a7dbe..3d2a890a7b 100644 --- a/integrations/malware_tools_analyzers/compose.yml +++ b/integrations/malware_tools_analyzers/compose.yml @@ -16,5 +16,5 @@ services: - ../integrations/malware_tools_analyzers/qiling/rootfs:/opt/deploy/rootfs - ../integrations/malware_tools_analyzers/qiling/profiles:/opt/deploy/profiles depends_on: - - uwsgi + - gunicorn diff --git a/integrations/nuclei_analyzer/compose.yml b/integrations/nuclei_analyzer/compose.yml index ee8ef25407..b190edc30d 100644 --- a/integrations/nuclei_analyzer/compose.yml +++ b/integrations/nuclei_analyzer/compose.yml @@ -12,4 +12,4 @@ services: volumes: - generic_logs:/var/log/intel_owl depends_on: - - uwsgi + - gunicorn diff --git a/integrations/pcap_analyzers/compose.yml b/integrations/pcap_analyzers/compose.yml index 263edee732..4f17b65de0 100644 --- a/integrations/pcap_analyzers/compose.yml +++ b/integrations/pcap_analyzers/compose.yml @@ -14,4 +14,4 @@ services: - ../integrations/pcap_analyzers/config/suricata/etc:/etc/suricata:ro - ../integrations/pcap_analyzers/config/suricata/rules:/var/lib/suricata/rules/ depends_on: - - uwsgi + - gunicorn diff --git a/integrations/phishing_analyzers/compose.yml b/integrations/phishing_analyzers/compose.yml index 2d5afa29e4..a64df276c2 100644 --- a/integrations/phishing_analyzers/compose.yml +++ b/integrations/phishing_analyzers/compose.yml @@ -13,7 +13,7 @@ services: volumes: - generic_logs:/var/log/intel_owl depends_on: - - uwsgi + - gunicorn chromium-webdriver: # tagging convention for chrome webdriver diff --git a/integrations/phoneinfoga/compose.yml b/integrations/phoneinfoga/compose.yml index 8211ff0964..435435acde 100644 --- a/integrations/phoneinfoga/compose.yml +++ b/integrations/phoneinfoga/compose.yml @@ -1,14 +1,14 @@ services: - phoneinfoga: - container_name: intelowl_phoneinfoga - restart: unless-stopped - image: sundowndev/phoneinfoga:v2.11.0 - command: - - "serve" - - "--no-client" - expose: - - "5000" - env_file: + phoneinfoga: + container_name: intelowl_phoneinfoga + restart: unless-stopped + image: sundowndev/phoneinfoga:v2.11.0 + command: + - "serve" + - "--no-client" + expose: + - "5000" + env_file: - env_file_integrations - depends_on: - - uwsgi \ No newline at end of file + depends_on: + - gunicorn \ No newline at end of file diff --git a/integrations/phunter/compose.yml b/integrations/phunter/compose.yml index 385c03c68d..f0e6cbf2f9 100644 --- a/integrations/phunter/compose.yml +++ b/integrations/phunter/compose.yml @@ -8,4 +8,4 @@ services: volumes: - generic_logs:/var/log/intel_owl depends_on: - - uwsgi \ No newline at end of file + - gunicorn \ No newline at end of file diff --git a/integrations/thug/compose.yml b/integrations/thug/compose.yml index 75b87b6a5c..86461134b8 100644 --- a/integrations/thug/compose.yml +++ b/integrations/thug/compose.yml @@ -10,5 +10,5 @@ services: volumes: - generic_logs:/var/log/intel_owl depends_on: - - uwsgi + - gunicorn diff --git a/integrations/tor_analyzers/compose.yml b/integrations/tor_analyzers/compose.yml index 8b2574454a..c6ecbda3f4 100644 --- a/integrations/tor_analyzers/compose.yml +++ b/integrations/tor_analyzers/compose.yml @@ -12,4 +12,4 @@ services: volumes: - generic_logs:/var/log/intel_owl depends_on: - - uwsgi + - gunicorn diff --git a/intel_owl/settings/websocket.py b/intel_owl/settings/websocket.py index c5d1e7a6d4..3ad37a6af7 100644 --- a/intel_owl/settings/websocket.py +++ b/intel_owl/settings/websocket.py @@ -4,7 +4,7 @@ websockets_url = secrets.get_secret("WEBSOCKETS_URL", "redis://redis:6379/0") if not websockets_url: - if socket.gethostname() in ["uwsgi", "daphne"]: + if socket.gethostname() in ["gunicorn", "daphne"]: raise RuntimeError("Unable to configure websockets. Please set WEBSOCKETS_URL") else: ASGI_APPLICATION = "intel_owl.asgi.application" From 33ba20a4355eb7b1f436778a91ed75e4e1af69b3 Mon Sep 17 00:00:00 2001 From: Ayush Gupta Date: Sat, 28 Feb 2026 12:12:33 +0000 Subject: [PATCH 3/4] cli update container name to gunicorn in workflow --- .github/workflows/pull_request_automation.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pull_request_automation.yml b/.github/workflows/pull_request_automation.yml index ecebd4a036..2149fd3aa3 100644 --- a/.github/workflows/pull_request_automation.yml +++ b/.github/workflows/pull_request_automation.yml @@ -101,19 +101,19 @@ jobs: if: always() run: | docker ps -a - docker logs intelowl_uwsgi + docker logs intelowl_gunicorn docker logs intelowl_daphne - name: Setup coverage run: | - docker exec intelowl_uwsgi pip3 install coverage + docker exec intelowl_gunicorn pip3 install coverage - name: Run test run: | - docker exec intelowl_uwsgi coverage run manage.py test --keepdb tests + docker exec intelowl_gunicorn coverage run manage.py test --keepdb tests - name: Run async tests run: | - docker exec intelowl_uwsgi coverage run manage.py test --keepdb async_tests + docker exec intelowl_gunicorn coverage run manage.py test --keepdb async_tests frontend-tests: runs-on: ubuntu-latest From ad62ab4f8eccc65aa3d75956bd877ca0fb75f33f Mon Sep 17 00:00:00 2001 From: Ayush Gupta Date: Mon, 2 Mar 2026 10:35:54 +0000 Subject: [PATCH 4/4] refactor: implement production-grade gunicorn optimizations Applied architectural refinements for Idea #9: - Dynamic worker calculation based on CPU cores for better resource utilization. - Switch to stdout/stderr logging for superior Docker observability. - Configure forwarded_allow_ips for accurate remote IP logging through Nginx. - Finalize L7 HTTP-based health checks. --- configuration/gunicorn.conf.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/configuration/gunicorn.conf.py b/configuration/gunicorn.conf.py index f36fdf759b..ae76edffc3 100644 --- a/configuration/gunicorn.conf.py +++ b/configuration/gunicorn.conf.py @@ -1,12 +1,15 @@ # This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl # See the file 'LICENSE' for copying permission. +import multiprocessing + # Gunicorn configuration file for IntelOwl bind = "0.0.0.0:8001" worker_class = "gthread" -workers = 12 +# Dynamic worker calculation: (2 x num_cores) + 1 +workers = multiprocessing.cpu_count() * 2 + 1 threads = 2 timeout = 600 @@ -16,8 +19,13 @@ keepalive = 5 preload_app = True -accesslog = "/var/log/intel_owl/gunicorn/intel_owl_access.log" -errorlog = "/var/log/intel_owl/gunicorn/intel_owl_errors.log" +# Standard Docker logging to stdout/stderr +accesslog = "-" +errorlog = "-" loglevel = "info" proc_name = "intel_owl_gunicorn" + +# Handle headers from Nginx correctly +forwarded_allow_ips = "*" +proxy_allow_ips = "*"