From 98e29b8f212aed1cc3472c70f6a844a5c774e877 Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Mon, 29 Dec 2025 17:41:58 +0100 Subject: [PATCH 1/7] Use psycopg[binary] instead of psycopg[c] for faster Docker builds Switch from psycopg[c] to psycopg[binary] to use pre-compiled wheels instead of compiling the C extension from source. This significantly speeds up Docker image builds. Changes: - requirements.txt: psycopg[c] -> psycopg[binary] - Remove libpq-dev from all Dockerfiles (build and release stages) The psycopg[binary] package bundles its own libpq, so system libpq-dev is no longer needed for either compilation or runtime. Both options provide the same performance (fast C implementation). Reference: https://www.psycopg.org/psycopg3/docs/basic/install.html --- Dockerfile.django-alpine | 1 - Dockerfile.django-debian | 2 -- Dockerfile.nginx-alpine | 1 - requirements.txt | 2 +- 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Dockerfile.django-alpine b/Dockerfile.django-alpine index 828a15b786f..eac7fa9013d 100644 --- a/Dockerfile.django-alpine +++ b/Dockerfile.django-alpine @@ -22,7 +22,6 @@ RUN \ openssl \ libffi-dev \ python3-dev \ - libpq-dev \ && \ rm -rf /var/cache/apk/* && \ true diff --git a/Dockerfile.django-debian b/Dockerfile.django-debian index 779dbcba13d..39067206ffc 100644 --- a/Dockerfile.django-debian +++ b/Dockerfile.django-debian @@ -14,7 +14,6 @@ RUN \ gcc \ build-essential \ dnsutils \ - libpq-dev \ postgresql-client \ xmlsec1 \ git \ @@ -50,7 +49,6 @@ RUN \ xmlsec1 \ git \ uuid-runtime \ - libpq-dev \ # only required for the dbshell (used by the initializer job) postgresql-client \ # libcurl4-openssl-dev is required for installing pycurl python package diff --git a/Dockerfile.nginx-alpine b/Dockerfile.nginx-alpine index bfdf4a4114a..145c1f51b2b 100644 --- a/Dockerfile.nginx-alpine +++ b/Dockerfile.nginx-alpine @@ -22,7 +22,6 @@ RUN \ openssl \ libffi-dev \ python3-dev \ - libpq-dev \ && \ rm -rf /var/cache/apk/* && \ true diff --git a/requirements.txt b/requirements.txt index a90a791d746..31d73057c8a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,7 +29,7 @@ lxml==6.0.2 Markdown==3.10 openpyxl==3.1.5 Pillow==12.0.0 # required by django-imagekit -psycopg[c]==3.3.2 +psycopg[binary]==3.3.2 cryptography==46.0.3 python-dateutil==2.9.0.post0 redis==7.1.0 From 81265eafc64420e85b799a01f14adf1f3ec0b28d Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Mon, 29 Dec 2025 18:15:35 +0100 Subject: [PATCH 2/7] Use system uWSGI packages instead of pip installation Replace pip-installed uWSGI with OS-provided system packages to eliminate compilation during Docker builds. Changes: - Remove uWSGI from requirements.txt - Debian: Install uwsgi + uwsgi-plugin-python3 from apt - Alpine: Install uwsgi + uwsgi-python3 from apk - Remove build dependencies (gcc, build-essential, python3-dev, libffi-dev) - Remove CPUCOUNT=1 workaround (no longer needed) Benefits: - Eliminates uWSGI compilation (major build time improvement) - Removes need for build toolchain in Docker images - Smaller build stage (no gcc, build-essential, etc.) - Faster Docker image builds Version notes: - Debian Trixie: uwsgi 2.0.28 (vs pip 2.0.31) - Alpine 3.22: uwsgi 2.0.30 (vs pip 2.0.31) System packages are maintained by official distribution teams and receive security backports within their release cycle. --- Dockerfile.django-alpine | 11 ++++------- Dockerfile.django-debian | 9 ++++----- Dockerfile.nginx-alpine | 10 ++-------- requirements.txt | 3 ++- 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/Dockerfile.django-alpine b/Dockerfile.django-alpine index eac7fa9013d..15205495e98 100644 --- a/Dockerfile.django-alpine +++ b/Dockerfile.django-alpine @@ -11,8 +11,6 @@ WORKDIR /app RUN \ apk update && \ apk add --no-cache \ - gcc \ - build-base \ bind-tools \ postgresql16-client \ xmlsec \ @@ -20,16 +18,12 @@ RUN \ util-linux \ curl-dev \ openssl \ - libffi-dev \ - python3-dev \ && \ rm -rf /var/cache/apk/* && \ true COPY requirements.txt ./ -# CPUCOUNT=1 is needed, otherwise the wheel for uwsgi won't always be build succesfully -# https://github.com/unbit/uwsgi/issues/1318#issuecomment-542238096 RUN export PYCURL_SSL_LIBRARY=openssl && \ - CPUCOUNT=1 pip3 wheel --wheel-dir=/tmp/wheels -r ./requirements.txt + pip3 wheel --wheel-dir=/tmp/wheels -r ./requirements.txt FROM base AS release WORKDIR /app @@ -50,6 +44,9 @@ RUN \ postgresql16-client \ curl-dev \ openssl \ + # uwsgi from system packages (avoids compilation) + uwsgi \ + uwsgi-python3 \ # needed for integration-tests bash \ && \ diff --git a/Dockerfile.django-debian b/Dockerfile.django-debian index 39067206ffc..f245c155928 100644 --- a/Dockerfile.django-debian +++ b/Dockerfile.django-debian @@ -11,8 +11,6 @@ WORKDIR /app RUN \ apt-get -y update && \ apt-get -y install --no-install-recommends \ - gcc \ - build-essential \ dnsutils \ postgresql-client \ xmlsec1 \ @@ -25,10 +23,8 @@ RUN \ rm -rf /var/lib/apt/lists && \ true COPY requirements.txt ./ -# CPUCOUNT=1 is needed, otherwise the wheel for uwsgi won't always be build succesfully -# https://github.com/unbit/uwsgi/issues/1318#issuecomment-542238096 RUN export PYCURL_SSL_LIBRARY=openssl && \ - CPUCOUNT=1 pip3 wheel --wheel-dir=/tmp/wheels -r ./requirements.txt + pip3 wheel --wheel-dir=/tmp/wheels -r ./requirements.txt FROM base AS release WORKDIR /app @@ -53,6 +49,9 @@ RUN \ postgresql-client \ # libcurl4-openssl-dev is required for installing pycurl python package libcurl4-openssl-dev \ + # uwsgi from system packages (avoids compilation) + uwsgi \ + uwsgi-plugin-python3 \ && \ apt-get clean && \ rm -rf /var/lib/apt/lists && \ diff --git a/Dockerfile.nginx-alpine b/Dockerfile.nginx-alpine index 145c1f51b2b..99185c3f3f6 100644 --- a/Dockerfile.nginx-alpine +++ b/Dockerfile.nginx-alpine @@ -11,8 +11,6 @@ WORKDIR /app RUN \ apk update && \ apk add --no-cache \ - gcc \ - build-base \ bind-tools \ postgresql16-client \ xmlsec \ @@ -20,17 +18,13 @@ RUN \ util-linux \ curl-dev \ openssl \ - libffi-dev \ - python3-dev \ && \ rm -rf /var/cache/apk/* && \ true COPY requirements.txt requirements-dev.txt ./ -# CPUCOUNT=1 is needed, otherwise the wheel for uwsgi won't always be build succesfully -# https://github.com/unbit/uwsgi/issues/1318#issuecomment-542238096 -RUN CPUCOUNT=1 pip3 wheel --wheel-dir=/tmp/wheels -r ./requirements.txt +RUN pip3 wheel --wheel-dir=/tmp/wheels -r ./requirements.txt # needed for static files debug toolbar -RUN CPUCOUNT=1 pip3 wheel --wheel-dir=/tmp/wheels -r ./requirements-dev.txt +RUN pip3 wheel --wheel-dir=/tmp/wheels -r ./requirements-dev.txt FROM build AS collectstatic diff --git a/requirements.txt b/requirements.txt index 31d73057c8a..7e120a5cd3c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -36,7 +36,8 @@ redis==7.1.0 requests==2.32.5 sqlalchemy==2.0.45 # Required by Celery broker transport urllib3==2.6.2 -uWSGI==2.0.31 +# uWSGI is installed via system packages (uwsgi + uwsgi-plugin-python3 on Debian, +# uwsgi + uwsgi-python3 on Alpine) to avoid compilation during Docker builds and to have official binaries vobject==0.9.9 whitenoise==5.2.0 titlecase==2.4.1 From 05ffcc64b168a300998cf629339eb8cba3ed40a3 Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Mon, 29 Dec 2025 17:44:04 +0100 Subject: [PATCH 3/7] Use system pycurl packages instead of pip installation Remove pycurl from requirements.txt and use OS-provided packages instead: - Debian: python3-pycurl (7.45.6) - Alpine: py3-curl (7.45.6) Benefits: - Eliminates pycurl compilation during Docker builds - Removes need for curl-dev/libcurl4-openssl-dev build dependencies - Faster Docker image builds - Consistent pycurl version across Debian and Alpine images pycurl is only needed for Celery SQS broker support. It is not directly imported by DefectDojo code - it's a transitive dependency that Celery uses when configured with an SQS broker (DD_CELERY_BROKER_SCHEME=sqs). The system packages provide the same functionality without requiring compilation toolchains in the Docker build stage. --- Dockerfile.django-alpine | 7 +++---- Dockerfile.django-debian | 9 +++------ Dockerfile.nginx-alpine | 4 ++-- requirements.txt | 3 ++- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/Dockerfile.django-alpine b/Dockerfile.django-alpine index 15205495e98..9c6068b0332 100644 --- a/Dockerfile.django-alpine +++ b/Dockerfile.django-alpine @@ -16,14 +16,12 @@ RUN \ xmlsec \ git \ util-linux \ - curl-dev \ openssl \ && \ rm -rf /var/cache/apk/* && \ true COPY requirements.txt ./ -RUN export PYCURL_SSL_LIBRARY=openssl && \ - pip3 wheel --wheel-dir=/tmp/wheels -r ./requirements.txt +RUN pip3 wheel --wheel-dir=/tmp/wheels -r ./requirements.txt FROM base AS release WORKDIR /app @@ -42,8 +40,9 @@ RUN \ git \ util-linux \ postgresql16-client \ - curl-dev \ openssl \ + # py3-curl for Celery SQS broker support (avoids compilation) + py3-curl \ # uwsgi from system packages (avoids compilation) uwsgi \ uwsgi-python3 \ diff --git a/Dockerfile.django-debian b/Dockerfile.django-debian index f245c155928..12d3602d163 100644 --- a/Dockerfile.django-debian +++ b/Dockerfile.django-debian @@ -16,15 +16,12 @@ RUN \ xmlsec1 \ git \ uuid-runtime \ - # libcurl4-openssl-dev is required for installing pycurl python package - libcurl4-openssl-dev \ && \ apt-get clean && \ rm -rf /var/lib/apt/lists && \ true COPY requirements.txt ./ -RUN export PYCURL_SSL_LIBRARY=openssl && \ - pip3 wheel --wheel-dir=/tmp/wheels -r ./requirements.txt +RUN pip3 wheel --wheel-dir=/tmp/wheels -r ./requirements.txt FROM base AS release WORKDIR /app @@ -47,8 +44,8 @@ RUN \ uuid-runtime \ # only required for the dbshell (used by the initializer job) postgresql-client \ - # libcurl4-openssl-dev is required for installing pycurl python package - libcurl4-openssl-dev \ + # python3-pycurl for Celery SQS broker support (avoids compilation) + python3-pycurl \ # uwsgi from system packages (avoids compilation) uwsgi \ uwsgi-plugin-python3 \ diff --git a/Dockerfile.nginx-alpine b/Dockerfile.nginx-alpine index 99185c3f3f6..8be00a5814d 100644 --- a/Dockerfile.nginx-alpine +++ b/Dockerfile.nginx-alpine @@ -16,7 +16,6 @@ RUN \ xmlsec \ git \ util-linux \ - curl-dev \ openssl \ && \ rm -rf /var/cache/apk/* && \ @@ -28,7 +27,8 @@ RUN pip3 wheel --wheel-dir=/tmp/wheels -r ./requirements-dev.txt FROM build AS collectstatic -RUN apk add nodejs npm +# py3-curl for Celery SQS broker support (avoids compilation) +RUN apk add nodejs npm py3-curl RUN npm install -g yarn --force diff --git a/requirements.txt b/requirements.txt index 7e120a5cd3c..7cb77fd167b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -63,7 +63,8 @@ drf-spectacular-sidecar==2025.12.1 django-ratelimit==4.1.0 argon2-cffi==25.1.0 blackduck==1.1.3 -pycurl==7.45.7 # Required for Celery Broker AWS (SQS) support +# pycurl is required for Celery SQS broker support but is installed via system +# packages (python3-pycurl on Debian, py3-curl on Alpine) to avoid compilation boto3==1.41.5 # Required for Celery Broker AWS (SQS) support netaddr==1.3.0 vulners==3.1.3 From b4f9b1c46c68724907912fce3cc2e13d1a606581 Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Mon, 29 Dec 2025 22:40:23 +0100 Subject: [PATCH 4/7] fix uwsgi parameters --- docker/entrypoint-uwsgi-dev.sh | 6 +++++- docker/entrypoint-uwsgi.sh | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docker/entrypoint-uwsgi-dev.sh b/docker/entrypoint-uwsgi-dev.sh index 7051ccadc00..f6dfc50e83c 100755 --- a/docker/entrypoint-uwsgi-dev.sh +++ b/docker/entrypoint-uwsgi-dev.sh @@ -31,10 +31,14 @@ watchmedo shell-command \ /app/dojo & +echo -n "Starting uwsgi" + exec uwsgi \ "--${DD_UWSGI_MODE}" "${DD_UWSGI_ENDPOINT}" \ --protocol uwsgi \ - --wsgi dojo.wsgi:application \ + --plugin python3 \ + --home /usr/local \ + --module dojo.wsgi:application \ --enable-threads \ --processes "${DD_UWSGI_NUM_OF_PROCESSES:-4}" \ --threads "${DD_UWSGI_NUM_OF_THREADS:-4}" \ diff --git a/docker/entrypoint-uwsgi.sh b/docker/entrypoint-uwsgi.sh index a9ca7bf49e6..0a7d9a124d5 100755 --- a/docker/entrypoint-uwsgi.sh +++ b/docker/entrypoint-uwsgi.sh @@ -34,13 +34,17 @@ if [ -n "${DD_UWSGI_MAX_FD}" ]; then DD_UWSGI_EXTRA_ARGS="${DD_UWSGI_EXTRA_ARGS} --max-fd ${DD_UWSGI_MAX_FD}" fi +echo -n "Starting uwsgi" + exec uwsgi \ "--${DD_UWSGI_MODE}" "${DD_UWSGI_ENDPOINT}" \ --protocol uwsgi \ + --plugin python3 \ + --home /usr/local \ + --module dojo.wsgi:application \ --enable-threads \ --processes "${DD_UWSGI_NUM_OF_PROCESSES:-4}" \ --threads "${DD_UWSGI_NUM_OF_THREADS:-4}" \ - --wsgi dojo.wsgi:application \ --buffer-size="${DD_UWSGI_BUFFER_SIZE:-8192}" \ --http 0.0.0.0:8081 --http-to "${DD_UWSGI_ENDPOINT}" \ --logformat "${DD_UWSGI_LOGFORMAT:-$DD_UWSGI_LOGFORMAT_DEFAULT}" \ From c4178cf478c651f26c410ed7e5dd77acda276213 Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Mon, 29 Dec 2025 23:29:01 +0100 Subject: [PATCH 5/7] try python 3.13 fix --- docker/entrypoint-uwsgi-dev.sh | 3 +-- docker/entrypoint-uwsgi.sh | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docker/entrypoint-uwsgi-dev.sh b/docker/entrypoint-uwsgi-dev.sh index f6dfc50e83c..0fd22157090 100755 --- a/docker/entrypoint-uwsgi-dev.sh +++ b/docker/entrypoint-uwsgi-dev.sh @@ -36,8 +36,7 @@ echo -n "Starting uwsgi" exec uwsgi \ "--${DD_UWSGI_MODE}" "${DD_UWSGI_ENDPOINT}" \ --protocol uwsgi \ - --plugin python3 \ - --home /usr/local \ + --plugin python313 \ --module dojo.wsgi:application \ --enable-threads \ --processes "${DD_UWSGI_NUM_OF_PROCESSES:-4}" \ diff --git a/docker/entrypoint-uwsgi.sh b/docker/entrypoint-uwsgi.sh index 0a7d9a124d5..87b3e098818 100755 --- a/docker/entrypoint-uwsgi.sh +++ b/docker/entrypoint-uwsgi.sh @@ -39,8 +39,7 @@ echo -n "Starting uwsgi" exec uwsgi \ "--${DD_UWSGI_MODE}" "${DD_UWSGI_ENDPOINT}" \ --protocol uwsgi \ - --plugin python3 \ - --home /usr/local \ + --plugin python313 \ --module dojo.wsgi:application \ --enable-threads \ --processes "${DD_UWSGI_NUM_OF_PROCESSES:-4}" \ From d9cec4ac37feea438425b41f08c427a6873b8cf7 Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Tue, 30 Dec 2025 00:07:03 +0100 Subject: [PATCH 6/7] fix alpine --- Dockerfile.django-alpine | 7 +++---- docker-compose.override.dev.yml | 2 +- docker/entrypoint-uwsgi-dev.sh | 12 ++++++++++-- docker/entrypoint-uwsgi.sh | 12 ++++++++++-- requirements.txt | 6 ++++-- 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/Dockerfile.django-alpine b/Dockerfile.django-alpine index 9c6068b0332..b5682e317ce 100644 --- a/Dockerfile.django-alpine +++ b/Dockerfile.django-alpine @@ -43,9 +43,6 @@ RUN \ openssl \ # py3-curl for Celery SQS broker support (avoids compilation) py3-curl \ - # uwsgi from system packages (avoids compilation) - uwsgi \ - uwsgi-python3 \ # needed for integration-tests bash \ && \ @@ -58,7 +55,9 @@ RUN \ --no-cache-dir \ --no-index \ --find-links=/tmp/wheels \ - -r ./requirements.txt + -r ./requirements.txt && \ + # Install uwsgi via pip for Alpine (system package is compiled for Python 3.12, but we use Python 3.13) + pip3 install --no-cache-dir uwsgi==2.0.28 COPY \ docker/entrypoint-celery-beat.sh \ diff --git a/docker-compose.override.dev.yml b/docker-compose.override.dev.yml index bc31139a352..d3d3a3cd4c1 100644 --- a/docker-compose.override.dev.yml +++ b/docker-compose.override.dev.yml @@ -3,7 +3,7 @@ services: uwsgi: build: context: . - dockerfile: Dockerfile.django-debian + dockerfile: Dockerfile.django-${DEFECT_DOJO_OS:-debian} target: development entrypoint: ['/wait-for-it.sh', '${DD_DATABASE_HOST:-postgres}:${DD_DATABASE_PORT:-5432}', '-t', '30', '--', '/entrypoint-uwsgi-dev.sh'] volumes: diff --git a/docker/entrypoint-uwsgi-dev.sh b/docker/entrypoint-uwsgi-dev.sh index 0fd22157090..b54ace14453 100755 --- a/docker/entrypoint-uwsgi-dev.sh +++ b/docker/entrypoint-uwsgi-dev.sh @@ -33,10 +33,18 @@ watchmedo shell-command \ echo -n "Starting uwsgi" -exec uwsgi \ +# Only use --home on Debian; pip-installed uwsgi on Alpine doesn't need it +if [ ! -f /etc/alpine-release ]; then + UWSGI_HOME_OPT="--home /usr/local" +else + UWSGI_HOME_OPT="" +fi + +uwsgi \ "--${DD_UWSGI_MODE}" "${DD_UWSGI_ENDPOINT}" \ --protocol uwsgi \ - --plugin python313 \ + --plugin python3 \ + ${UWSGI_HOME_OPT} \ --module dojo.wsgi:application \ --enable-threads \ --processes "${DD_UWSGI_NUM_OF_PROCESSES:-4}" \ diff --git a/docker/entrypoint-uwsgi.sh b/docker/entrypoint-uwsgi.sh index 87b3e098818..b5cb82ece7f 100755 --- a/docker/entrypoint-uwsgi.sh +++ b/docker/entrypoint-uwsgi.sh @@ -36,10 +36,18 @@ fi echo -n "Starting uwsgi" -exec uwsgi \ +# Only use --home on Debian; pip-installed uwsgi on Alpine doesn't need it +if [ ! -f /etc/alpine-release ]; then + UWSGI_HOME_OPT="--home /usr/local" +else + UWSGI_HOME_OPT="" +fi + +uwsgi \ "--${DD_UWSGI_MODE}" "${DD_UWSGI_ENDPOINT}" \ --protocol uwsgi \ - --plugin python313 \ + --plugin python3 \ + ${UWSGI_HOME_OPT} \ --module dojo.wsgi:application \ --enable-threads \ --processes "${DD_UWSGI_NUM_OF_PROCESSES:-4}" \ diff --git a/requirements.txt b/requirements.txt index 32ae0e2f3f0..10f624011f7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -36,8 +36,10 @@ redis==7.1.0 requests==2.32.5 sqlalchemy==2.0.45 # Required by Celery broker transport urllib3==2.6.2 -# uWSGI is installed via system packages (uwsgi + uwsgi-plugin-python3 on Debian, -# uwsgi + uwsgi-python3 on Alpine) to avoid compilation during Docker builds and to have official binaries +# uWSGI is installed via system packages on Debian (uwsgi + uwsgi-plugin-python3) to avoid compilation. +# On Alpine, uwsgi is installed via pip because the system package (uwsgi-python3) is compiled for Python 3.12 +# but the container uses Python 3.13.7 from the official Python Docker image. +# uwsgi==2.0.28 vobject==0.9.9 whitenoise==5.2.0 titlecase==2.4.1 From c39a68b730a09321da5bcac70c40ede84235888d Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Tue, 30 Dec 2025 00:11:20 +0100 Subject: [PATCH 7/7] fix alpine --- Dockerfile.django-alpine | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Dockerfile.django-alpine b/Dockerfile.django-alpine index b5682e317ce..a92f95fc46a 100644 --- a/Dockerfile.django-alpine +++ b/Dockerfile.django-alpine @@ -55,9 +55,17 @@ RUN \ --no-cache-dir \ --no-index \ --find-links=/tmp/wheels \ - -r ./requirements.txt && \ - # Install uwsgi via pip for Alpine (system package is compiled for Python 3.12, but we use Python 3.13) - pip3 install --no-cache-dir uwsgi==2.0.28 + -r ./requirements.txt + +# Install uwsgi via pip for Alpine (system package is compiled for Python 3.12, but we use Python 3.13) +# uwsgi needs build dependencies to compile +RUN apk add --no-cache --virtual .uwsgi-build-deps \ + gcc \ + musl-dev \ + linux-headers \ + python3-dev \ + && pip3 install --no-cache-dir uwsgi==2.0.28 \ + && apk del .uwsgi-build-deps COPY \ docker/entrypoint-celery-beat.sh \