docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes - #2226

Merged
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version
Sep 2, 2026
Merged

docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes#2226
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version

Conversation

@rnitzan

Copy link
Copy Markdown
Contributor

The Debian/Ubuntu instructions for installing a specific App Protect DoS version pin app-protect-dos and nginx-plus-module-appprotectdos but leave nginx-plus unpinned. That only works while the newest nginx-plus in the repository happens to belong to the release the module was built for.

The problem

Each nginx-plus-module-appprotectdos package depends on a virtual package named nginx-plus-r<release>, which only that release of nginx-plus provides. apt considers only a package's candidate (newest) version and will not select an older one to satisfy another package's dependency. So as soon as the repository carries a newer nginx-plus release, the documented command fails:

nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.

This is the current state of the repository. Observed on all four supported Debian/Ubuntu suites:

Candidate: 37.1.0-1~bookworm <- the only version apt will consider
37.1.0 Provides: nginx-plus-r37.1
37.0.5 Provides: nginx-plus-r37.0 <- what the module needs; present, not considered
nginx-plus-module-appprotectdos 37+4.9.6 Depends: nginx-plus-r37.0

A perfectly good pair is sitting in the repository, but following the page as written cannot install it. The RPM instructions are not affected in the same way, and the plus RPM repository has not published r37.1 yet, so I have left those sections alone rather than change them speculatively.

Changes

  • Add nginx-plus=<version> to all four apt-get install examples.
  • Document the nginx-plus-r<release> dependency, and add the two commands that reveal which release a module requires. apt-cache policy app-protect-dos, which the page currently suggests for choosing a version, does not show that dependency, so it cannot lead the reader to a working command.
  • Refresh the examples from 35+4.7.3 to the current 37+4.9.6.
  • Fix the indentation of the Ubuntu 22.04 and 24.04 code fences (they were indented one space further than the surrounding blocks).

Verification

The version strings are taken from apt-cache madison output on live VMs, not composed by hand:

suitenginx-plus 37.0.5module 37+4.9.6
bullseyeconfirmed presentconfirmed present
bookwormconfirmed presentconfirmed present
jammyr37.0 line confirmed (37.0.4 installed successfully)confirmed present
nobler37.0 line confirmed (37.0.4 installed successfully)confirmed present

markdownlint -c .markdownlint.yaml reports the same 34 pre-existing errors in this file before and after the change; no new ones are introduced, and none of the pre-existing ones are in the edited region.

Found while building an automated install-verification pipeline across Amazon Linux 2023, RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04, where every Debian and Ubuntu target failed to install until nginx-plus was pinned alongside the module.

@rnitzan
rnitzan requested a review from a team as a code ownerAugust 23, 2026 18:43
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation product/dos Issues related to F5 DoS for NGINX labels Aug 23, 2026
@github-actions

Copy link
Copy Markdown

Deploy Preview will be available once build job completes!

NameLink
😎 Deploy Previewhttps://frontdoor-test-docs.nginx.com/previews/docs/2226/

@rnitzan
rnitzan marked this pull request as draft August 24, 2026 09:26
@rnitzan
rnitzan marked this pull request as ready for review August 27, 2026 12:01
@rnitzanrnitzan changed the title fix: pin nginx-plus when installing a specific App Protect DoS versiondocs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notesAug 27, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates F5 DoS for NGINX documentation to reflect the current 4.9.6 and 4.10.0 releases, and to prevent Debian/Ubuntu installation failures by documenting and demonstrating correct version pinning for NGINX Plus and related packages.

Changes:

  • Add F5 DoS for NGINX 4.10 release notes and refresh 4.9 release notes to current package versions.
  • Expand the deployment guide with guidance/commands for identifying the required NGINX Plus release and pinning nginx-plus alongside app-protect-dos and the module on Debian/Ubuntu.
  • Update Kubernetes build guidance and Dockerfile include snippets to support pinning package versions (including eBPF Manager alignment guidance).

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
content/nap-dos/releases/about-4.9.mdUpdates 4.9 release notes wording and supported package table to current 4.9.6 / R37.0 details.
content/nap-dos/releases/about-4.10.mdAdds new 4.10 release notes page including R37.1 support and Debian/Ubuntu pinning notes.
content/nap-dos/deployment-guide/learn-about-deployment.mdAdds RPM/APK/APT examples for 4.9.6 and 4.10.0 and documents Debian/Ubuntu pinning + dependency inspection steps.
content/nap-dos/deployment-guide/kubernetes.mdAdds guidance and an example showing how to build images with pinned versions (where supported).
content/nap-dos/deployment-guide/kubernetes-with-L4-accelerated-mitigation.mdAdds guidance and examples for version-pinned builds and aligning the eBPF Manager image version.
content/includes/dos/dockerfiles/ubuntu-plus-dos.mdAdds build args and pins package installs for Ubuntu-based images.
content/includes/dos/dockerfiles/ubuntu-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.
content/includes/dos/dockerfiles/debian-plus-dos.mdAdds build args and pins package installs for Debian-based images.
content/includes/dos/dockerfiles/debian-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threadcontent/includes/dos/dockerfiles/debian-plus-dos.md Outdated
Comment threadcontent/includes/dos/dockerfiles/ubuntu-plus-dos.md Outdated
Comment threadcontent/nap-dos/deployment-guide/kubernetes.md Outdated
Comment threadcontent/nap-dos/deployment-guide/learn-about-deployment.md Outdated
@rnitzan

Copy link
Copy Markdown
ContributorAuthor

Thanks — C3, C4 and C5 were all correct and are fixed. On C1/C2 the conclusion is right but the stated cause is not, so I want to record the correction rather than let it stand.

C1/C2 — apt-get remove --purge --auto-remove -y does not fail the build.

apt has no minimum-argument guard on remove. InstallPackages in apt-private/private-install.cc short-circuits to success when nothing is queued:

if (Cache->DelCount() ==0&&Cache->InstCount() ==0&&Cache->BadCount() ==0)
returnRunScripts("APT::Install::Post-Invoke-Success");

It exits 0. The real defect is different: the build-time packages were installed explicitly, so apt marks them manually installed and --auto-remove can never reap them. Only orphaned automatically-installed dependencies were eligible, of which there are none here — so the line was a no-op, not a failure.

Fixed by naming the four packages, matching the form already used in content/includes/waf/dockerfiles/official-oss.md. ca-certificates and the archive keyrings are deliberately kept, since NGINX Plus needs the CA bundle at runtime for licensing and telemetry. All four purged packages are used earlier in the same RUN and nothing after the purge depends on them.

C3/C4 — build args only existed on Debian/Ubuntu. Correct, and the paragraph sat above every OS section. Rather than narrow the prose, DOS_VERSION is now on all twelve remaining Dockerfiles (NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8/9/10 and Rocky Linux 9), all defaulting to empty so current behaviour is unchanged. NGINX_PLUS_VERSION stays Debian/Ubuntu-only on purpose: dnf and apk can both select an older nginx-plus to satisfy the module's nginx-plus-r<release> dependency, so pinning it elsewhere would imply the apt limitation is universal. Version syntax differs by package manager, so each Dockerfile documents its own (=<version> for apt/apk, -<version> for dnf).

Worth noting: the Alpine Dockerfiles declare ARG OS_VERbeforeFROM, which scopes it to FROM alone. DOS_VERSION is therefore declared after FROM in every file so it stays visible to RUN.

C5 — suite mismatch. Correct. Replaced the hardcoded -1~bookworm with `lsb_release -cs`, the idiom already used by the repository setup steps earlier in that section, so one command is right on all four suites and its position no longer implies one.


Caveat for reviewers: I could not build these images, so the ${DOS_VERSION} concatenation and the apk/dnf version syntax rest on the deployment guide's own documented commands rather than a live build. One pinned build per package manager before merge would close that gap.

The Debian/Ubuntu instructions for installing a specific App Protect DoS version
pin `app-protect-dos` and `nginx-plus-module-appprotectdos` but leave `nginx-plus`
unpinned. That only works while the newest `nginx-plus` in the repository happens to
belong to the release the module was built for.
Each `nginx-plus-module-appprotectdos` package depends on a virtual package named
`nginx-plus-r<release>`, which only that release of `nginx-plus` provides. `apt`
considers only the newest `nginx-plus` available and does not select an older one to
satisfy another package's dependency, so as soon as the repository carries a newer
release the documented command fails:
nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.
This happens today. The repository offers `nginx-plus` 37.1.0, which provides
`nginx-plus-r37.1`, while the current DoS module `37+4.9.6` requires
`nginx-plus-r37.0`. A suitable `nginx-plus` 37.0.5 is present but is never
considered, so following the documentation as written cannot install it.
Add `nginx-plus` to each example, and document the `nginx-plus-r<release>`
dependency along with the commands that reveal which release a module requires.
`apt-cache policy app-protect-dos`, which the page currently suggests for choosing a
version, does not show that dependency, so it cannot lead the reader to a working
command.
Also refresh the examples from `35+4.7.3` to the current `37+4.9.6`, and correct the
indentation of the Ubuntu code fences.
The four examples pinned nginx-plus=37.0.5, which is a pre-release build. On the
repository customers actually use, app-protect-dos 37+4.9.6 pairs with nginx-plus
37.0.4, so the commands as written could not succeed. Verified on Debian 11/12/13 and
Ubuntu 22.04/24.04: an unpinned install from the production repository resolves to
nginx-plus 37.0.4 with nginx-plus-module-appprotectdos 37+4.9.6 and app-protect-dos
37+4.9.6.
Also scope the surrounding explanation. Pinning nginx-plus is needed when the App
Protect DoS version being installed was built for an earlier NGINX Plus release, which
is the situation this "install a specific version" section describes. It is not needed
for the plain `apt-get install app-protect-dos` shown above it, where every package
resolves to its newest version and the dependency on nginx-plus-r<release> is satisfied
without help. Saying so explicitly keeps the pin from being read as a general
requirement.
Version 4.10 adds NGINX Plus R37.1 support, which is the whole of its feature content
per the product changelog (BADOS-2046, 2026-08-26), plus accumulated bug fixes.
Follows the structure of the 4.9 notes, with weight 20 to keep the releases section in
descending order. Uses an "Important notes" section rather than a call-out shortcode,
because that heading is the established convention here and no other release note in
this section uses call-out.
Package strings verified per distribution against installs on Amazon Linux 2023,
RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04. The two Alpine rows are inferred
from the naming pattern the 4.8 and 4.9 notes use, since Alpine is not covered by that
test matrix; they should be checked against the published artifacts before release.
The important-notes section records the release coupling explicitly: every
nginx-plus-module-appprotectdos package depends on a virtual nginx-plus-r<release>
package that only its own NGINX Plus release provides, so 4.10 needs R37.1 where 4.9
needs R37.0, and the two must move together.
The "install a specific version" examples named a single version, and outside the
Debian/Ubuntu section that version was long stale: RHEL 8 and RHEL 9 still showed
35+4.7.3, Amazon Linux 2023 showed 34+4.6.0, and Alpine showed 33+4.5.0-r1.
Each section now shows the two current versions with the NGINX Plus release each one
requires, so a reader can see at a glance that the choice of App Protect DoS version
determines the NGINX Plus version:
37+4.10.0 -> NGINX Plus R37.1 (nginx-plus 37.1.0)
37+4.9.6 -> NGINX Plus R37.0 (nginx-plus 37.0.4)
The Debian/Ubuntu section also gains that mapping as a table, because it is the one
place where getting it wrong produces a confusing failure rather than a clear one:
apt considers only the newest nginx-plus and will not select an older one on its own.
The same nginx-plus-r<release> coupling exists on the RPM side - the metadata carries
nginx-plus-r37.0 and nginx-plus-r37.1 exactly as the Debian packages do - so the dnf
and apk examples name the required release in a comment. They do not add an explicit
nginx-plus version, because there is no evidence here that dnf or apk need one, and
prescribing a pin that has not been observed to be necessary would be a guess.
All package strings verified against installs on Amazon Linux 2023, RHEL 8/9/10,
Debian 11/12/13 and Ubuntu 22.04/24.04. The Alpine strings follow the naming pattern
recorded in the 4.8 and 4.9 release notes; Alpine is not covered by that matrix.
Tested under real apt: apt accepts version globs in pkg=version syntax — both
37.0.* and 37.1.* resolve correctly (rc=0), verified in a Debian bullseye container.
Changes:
- All 8 apt install commands: nginx-plus=37.0.4-1~ → nginx-plus=37.0.*-1~
and nginx-plus=37.1.0-1~ → nginx-plus=37.1.*-1~
- Compatibility table: exact patch versions → version ranges, column header updated
- about-4.9.md supported-packages table: "NGINX Plus R37" → "NGINX Plus R37.0",
since R37.0 and R37.1 are now separate lines and the ambiguity would mislead
The install-a-specific-version instructions pinned app-protect-dos,
nginx-plus-module-appprotectdos and nginx-plus, but left
app-protect-dos-ebpf-manager unpinned in all five OS sections. Because
that command sits above the specific-version subsection, following the
L4 accelerated mitigation path for 4.9.6 installed a 4.10.0 eBPF
manager next to it. Each section now shows how to list the available
eBPF manager versions and pin the matching one.
Also in this commit:
- Replace `apt-cache madison nginx-plus` with `apt-cache showpkg
nginx-plus-r<release>`. madison lists versions and origins only and
never shows the nginx-plus-r<release> virtual package, so it could
not answer the question the surrounding text asked of it.
- Quote the version arguments containing `*` so that the shell does not
expand them as filename patterns.
- Qualify the claim that the unpinned install needs no pin. That holds
only while the newest app-protect-dos is built for the newest NGINX
Plus release, which is not true between an NGINX Plus release and the
DoS release that supports it.
- Split the pinning paragraph and move the apt behaviour into a note.
- Drop a duplicated `sudo` and a stale `_r36` example image tag.
The Kubernetes guides render the Dockerfile includes, all of which
installed app-protect-dos and app-protect-dos-ebpf-manager unpinned.
There was no way to build an image for a specific version, and the
NGINX Plus and eBPF Manager images could drift apart because they are
built separately.
The Debian and Ubuntu Dockerfiles now accept DOS_VERSION and
NGINX_PLUS_VERSION build arguments. Both default to empty, so the
existing latest-version behaviour is unchanged. Both Kubernetes guides
explain version selection and show the matching --build-arg commands,
and the L4 guide calls out that the NGINX Plus and eBPF Manager images
must be built from the same version.
This also fixes two pre-existing faults in the same files:
- Neither Dockerfile ran `apt-get update` after adding the NGINX
repositories, so the install could not locate the packages.
- ubuntu-plus-dos.md had a dangling `RUN apt-get update && \` followed
by a comment and a second RUN, which is not valid Dockerfile syntax.
- Drop "includes bug fixes" from the 4.10 summary, since the New
features list no longer mentions them.
- Remove the leftover "(Trixie)" from the 4.9 feature list so that it
matches the supported packages table.
- Use "F5 DoS for NGINX" rather than "App Protect DoS".
- Note that the eBPF manager needs pinning as well.
- Align the supported packages tables.
The example pinned the Debian 12 suite (-1~bookworm) but sat directly
after the Ubuntu 24.04 command, so copying it on Ubuntu gave apt a
version that does not exist there.
Substitute the suite with `lsb_release -cs`, the idiom already used by
the repository setup steps earlier in this section. One command is now
correct on all four supported suites, and its position no longer
implies a particular one.
The previous commit added DOS_VERSION and NGINX_PLUS_VERSION to the
Debian and Ubuntu Dockerfiles only, but the surrounding paragraph sits
above every operating system section and read as though it applied to
all of them. A reader on Alpine, Amazon Linux, RHEL or Rocky Linux
would have passed --build-arg DOS_VERSION and had it ignored.
Add DOS_VERSION to the remaining twelve Dockerfiles, covering both the
NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8, 9
and 10, and Rocky Linux 9. All default to empty, so the existing
latest-version behaviour is unchanged.
Only Debian and Ubuntu take NGINX_PLUS_VERSION. dnf and apk can both
select an older nginx-plus to satisfy the module's nginx-plus-r<release>
dependency, so pinning it there would wrongly suggest the apt
limitation is universal. Each Dockerfile documents its own version
syntax, which differs by package manager: apt and apk use "=<version>",
dnf uses "-<version>".
The Alpine Dockerfiles declare ARG OS_VER before FROM, which scopes it
to the FROM instruction alone, so DOS_VERSION is declared after FROM
in every file to keep it visible to the RUN instruction.
`apt-get remove --purge --auto-remove -y` was called with no package
names. It does not fail, but it cannot do what it was written to do:
the build-time packages were installed explicitly, so apt marks them
manually installed and --auto-remove never reaps them. Only orphaned
automatically-installed dependencies were eligible, of which there are
none here, making the line a no-op.
Name the four build-time packages instead, matching the form already
used in content/includes/waf/dockerfiles/official-oss.md.
ca-certificates and the archive keyring packages are deliberately kept:
NGINX Plus needs the CA bundle at runtime for licensing and telemetry.
All four purged packages are used earlier in the same RUN instruction
and nothing after the purge depends on them.
@ohad-perets
ohad-peretsforce-pushed the dos/pin-nginx-plus-with-specific-version branch from e5a62ed to 17b62f5CompareSeptember 2, 2026 14:17
@rnitzan
rnitzan merged commit ac0bfd6 into mainSep 2, 2026
9 checks passed
@rnitzan
rnitzan deleted the dos/pin-nginx-plus-with-specific-version branch September 2, 2026 16:03
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationproduct/dosIssues related to F5 DoS for NGINX

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rnitzan@JTorreG@ohad-perets
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes - #2226

Merged
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version
Sep 2, 2026
Merged

docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes#2226
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version

Conversation

@rnitzan

Copy link
Copy Markdown
Contributor

The Debian/Ubuntu instructions for installing a specific App Protect DoS version pin app-protect-dos and nginx-plus-module-appprotectdos but leave nginx-plus unpinned. That only works while the newest nginx-plus in the repository happens to belong to the release the module was built for.

The problem

Each nginx-plus-module-appprotectdos package depends on a virtual package named nginx-plus-r<release>, which only that release of nginx-plus provides. apt considers only a package's candidate (newest) version and will not select an older one to satisfy another package's dependency. So as soon as the repository carries a newer nginx-plus release, the documented command fails:

nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.

This is the current state of the repository. Observed on all four supported Debian/Ubuntu suites:

Candidate: 37.1.0-1~bookworm <- the only version apt will consider
37.1.0 Provides: nginx-plus-r37.1
37.0.5 Provides: nginx-plus-r37.0 <- what the module needs; present, not considered
nginx-plus-module-appprotectdos 37+4.9.6 Depends: nginx-plus-r37.0

A perfectly good pair is sitting in the repository, but following the page as written cannot install it. The RPM instructions are not affected in the same way, and the plus RPM repository has not published r37.1 yet, so I have left those sections alone rather than change them speculatively.

Changes

  • Add nginx-plus=<version> to all four apt-get install examples.
  • Document the nginx-plus-r<release> dependency, and add the two commands that reveal which release a module requires. apt-cache policy app-protect-dos, which the page currently suggests for choosing a version, does not show that dependency, so it cannot lead the reader to a working command.
  • Refresh the examples from 35+4.7.3 to the current 37+4.9.6.
  • Fix the indentation of the Ubuntu 22.04 and 24.04 code fences (they were indented one space further than the surrounding blocks).

Verification

The version strings are taken from apt-cache madison output on live VMs, not composed by hand:

suitenginx-plus 37.0.5module 37+4.9.6
bullseyeconfirmed presentconfirmed present
bookwormconfirmed presentconfirmed present
jammyr37.0 line confirmed (37.0.4 installed successfully)confirmed present
nobler37.0 line confirmed (37.0.4 installed successfully)confirmed present

markdownlint -c .markdownlint.yaml reports the same 34 pre-existing errors in this file before and after the change; no new ones are introduced, and none of the pre-existing ones are in the edited region.

Found while building an automated install-verification pipeline across Amazon Linux 2023, RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04, where every Debian and Ubuntu target failed to install until nginx-plus was pinned alongside the module.

@rnitzan
rnitzan requested a review from a team as a code ownerAugust 23, 2026 18:43
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation product/dos Issues related to F5 DoS for NGINX labels Aug 23, 2026
@github-actions

Copy link
Copy Markdown

Deploy Preview will be available once build job completes!

NameLink
😎 Deploy Previewhttps://frontdoor-test-docs.nginx.com/previews/docs/2226/

@rnitzan
rnitzan marked this pull request as draft August 24, 2026 09:26
@rnitzan
rnitzan marked this pull request as ready for review August 27, 2026 12:01
@rnitzanrnitzan changed the title fix: pin nginx-plus when installing a specific App Protect DoS versiondocs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notesAug 27, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates F5 DoS for NGINX documentation to reflect the current 4.9.6 and 4.10.0 releases, and to prevent Debian/Ubuntu installation failures by documenting and demonstrating correct version pinning for NGINX Plus and related packages.

Changes:

  • Add F5 DoS for NGINX 4.10 release notes and refresh 4.9 release notes to current package versions.
  • Expand the deployment guide with guidance/commands for identifying the required NGINX Plus release and pinning nginx-plus alongside app-protect-dos and the module on Debian/Ubuntu.
  • Update Kubernetes build guidance and Dockerfile include snippets to support pinning package versions (including eBPF Manager alignment guidance).

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
content/nap-dos/releases/about-4.9.mdUpdates 4.9 release notes wording and supported package table to current 4.9.6 / R37.0 details.
content/nap-dos/releases/about-4.10.mdAdds new 4.10 release notes page including R37.1 support and Debian/Ubuntu pinning notes.
content/nap-dos/deployment-guide/learn-about-deployment.mdAdds RPM/APK/APT examples for 4.9.6 and 4.10.0 and documents Debian/Ubuntu pinning + dependency inspection steps.
content/nap-dos/deployment-guide/kubernetes.mdAdds guidance and an example showing how to build images with pinned versions (where supported).
content/nap-dos/deployment-guide/kubernetes-with-L4-accelerated-mitigation.mdAdds guidance and examples for version-pinned builds and aligning the eBPF Manager image version.
content/includes/dos/dockerfiles/ubuntu-plus-dos.mdAdds build args and pins package installs for Ubuntu-based images.
content/includes/dos/dockerfiles/ubuntu-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.
content/includes/dos/dockerfiles/debian-plus-dos.mdAdds build args and pins package installs for Debian-based images.
content/includes/dos/dockerfiles/debian-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threadcontent/includes/dos/dockerfiles/debian-plus-dos.md Outdated
Comment threadcontent/includes/dos/dockerfiles/ubuntu-plus-dos.md Outdated
Comment threadcontent/nap-dos/deployment-guide/kubernetes.md Outdated
Comment threadcontent/nap-dos/deployment-guide/learn-about-deployment.md Outdated
@rnitzan

Copy link
Copy Markdown
ContributorAuthor

Thanks — C3, C4 and C5 were all correct and are fixed. On C1/C2 the conclusion is right but the stated cause is not, so I want to record the correction rather than let it stand.

C1/C2 — apt-get remove --purge --auto-remove -y does not fail the build.

apt has no minimum-argument guard on remove. InstallPackages in apt-private/private-install.cc short-circuits to success when nothing is queued:

if (Cache->DelCount() ==0&&Cache->InstCount() ==0&&Cache->BadCount() ==0)
returnRunScripts("APT::Install::Post-Invoke-Success");

It exits 0. The real defect is different: the build-time packages were installed explicitly, so apt marks them manually installed and --auto-remove can never reap them. Only orphaned automatically-installed dependencies were eligible, of which there are none here — so the line was a no-op, not a failure.

Fixed by naming the four packages, matching the form already used in content/includes/waf/dockerfiles/official-oss.md. ca-certificates and the archive keyrings are deliberately kept, since NGINX Plus needs the CA bundle at runtime for licensing and telemetry. All four purged packages are used earlier in the same RUN and nothing after the purge depends on them.

C3/C4 — build args only existed on Debian/Ubuntu. Correct, and the paragraph sat above every OS section. Rather than narrow the prose, DOS_VERSION is now on all twelve remaining Dockerfiles (NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8/9/10 and Rocky Linux 9), all defaulting to empty so current behaviour is unchanged. NGINX_PLUS_VERSION stays Debian/Ubuntu-only on purpose: dnf and apk can both select an older nginx-plus to satisfy the module's nginx-plus-r<release> dependency, so pinning it elsewhere would imply the apt limitation is universal. Version syntax differs by package manager, so each Dockerfile documents its own (=<version> for apt/apk, -<version> for dnf).

Worth noting: the Alpine Dockerfiles declare ARG OS_VERbeforeFROM, which scopes it to FROM alone. DOS_VERSION is therefore declared after FROM in every file so it stays visible to RUN.

C5 — suite mismatch. Correct. Replaced the hardcoded -1~bookworm with `lsb_release -cs`, the idiom already used by the repository setup steps earlier in that section, so one command is right on all four suites and its position no longer implies one.


Caveat for reviewers: I could not build these images, so the ${DOS_VERSION} concatenation and the apk/dnf version syntax rest on the deployment guide's own documented commands rather than a live build. One pinned build per package manager before merge would close that gap.

The Debian/Ubuntu instructions for installing a specific App Protect DoS version
pin `app-protect-dos` and `nginx-plus-module-appprotectdos` but leave `nginx-plus`
unpinned. That only works while the newest `nginx-plus` in the repository happens to
belong to the release the module was built for.
Each `nginx-plus-module-appprotectdos` package depends on a virtual package named
`nginx-plus-r<release>`, which only that release of `nginx-plus` provides. `apt`
considers only the newest `nginx-plus` available and does not select an older one to
satisfy another package's dependency, so as soon as the repository carries a newer
release the documented command fails:
nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.
This happens today. The repository offers `nginx-plus` 37.1.0, which provides
`nginx-plus-r37.1`, while the current DoS module `37+4.9.6` requires
`nginx-plus-r37.0`. A suitable `nginx-plus` 37.0.5 is present but is never
considered, so following the documentation as written cannot install it.
Add `nginx-plus` to each example, and document the `nginx-plus-r<release>`
dependency along with the commands that reveal which release a module requires.
`apt-cache policy app-protect-dos`, which the page currently suggests for choosing a
version, does not show that dependency, so it cannot lead the reader to a working
command.
Also refresh the examples from `35+4.7.3` to the current `37+4.9.6`, and correct the
indentation of the Ubuntu code fences.
The four examples pinned nginx-plus=37.0.5, which is a pre-release build. On the
repository customers actually use, app-protect-dos 37+4.9.6 pairs with nginx-plus
37.0.4, so the commands as written could not succeed. Verified on Debian 11/12/13 and
Ubuntu 22.04/24.04: an unpinned install from the production repository resolves to
nginx-plus 37.0.4 with nginx-plus-module-appprotectdos 37+4.9.6 and app-protect-dos
37+4.9.6.
Also scope the surrounding explanation. Pinning nginx-plus is needed when the App
Protect DoS version being installed was built for an earlier NGINX Plus release, which
is the situation this "install a specific version" section describes. It is not needed
for the plain `apt-get install app-protect-dos` shown above it, where every package
resolves to its newest version and the dependency on nginx-plus-r<release> is satisfied
without help. Saying so explicitly keeps the pin from being read as a general
requirement.
Version 4.10 adds NGINX Plus R37.1 support, which is the whole of its feature content
per the product changelog (BADOS-2046, 2026-08-26), plus accumulated bug fixes.
Follows the structure of the 4.9 notes, with weight 20 to keep the releases section in
descending order. Uses an "Important notes" section rather than a call-out shortcode,
because that heading is the established convention here and no other release note in
this section uses call-out.
Package strings verified per distribution against installs on Amazon Linux 2023,
RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04. The two Alpine rows are inferred
from the naming pattern the 4.8 and 4.9 notes use, since Alpine is not covered by that
test matrix; they should be checked against the published artifacts before release.
The important-notes section records the release coupling explicitly: every
nginx-plus-module-appprotectdos package depends on a virtual nginx-plus-r<release>
package that only its own NGINX Plus release provides, so 4.10 needs R37.1 where 4.9
needs R37.0, and the two must move together.
The "install a specific version" examples named a single version, and outside the
Debian/Ubuntu section that version was long stale: RHEL 8 and RHEL 9 still showed
35+4.7.3, Amazon Linux 2023 showed 34+4.6.0, and Alpine showed 33+4.5.0-r1.
Each section now shows the two current versions with the NGINX Plus release each one
requires, so a reader can see at a glance that the choice of App Protect DoS version
determines the NGINX Plus version:
37+4.10.0 -> NGINX Plus R37.1 (nginx-plus 37.1.0)
37+4.9.6 -> NGINX Plus R37.0 (nginx-plus 37.0.4)
The Debian/Ubuntu section also gains that mapping as a table, because it is the one
place where getting it wrong produces a confusing failure rather than a clear one:
apt considers only the newest nginx-plus and will not select an older one on its own.
The same nginx-plus-r<release> coupling exists on the RPM side - the metadata carries
nginx-plus-r37.0 and nginx-plus-r37.1 exactly as the Debian packages do - so the dnf
and apk examples name the required release in a comment. They do not add an explicit
nginx-plus version, because there is no evidence here that dnf or apk need one, and
prescribing a pin that has not been observed to be necessary would be a guess.
All package strings verified against installs on Amazon Linux 2023, RHEL 8/9/10,
Debian 11/12/13 and Ubuntu 22.04/24.04. The Alpine strings follow the naming pattern
recorded in the 4.8 and 4.9 release notes; Alpine is not covered by that matrix.
Tested under real apt: apt accepts version globs in pkg=version syntax — both
37.0.* and 37.1.* resolve correctly (rc=0), verified in a Debian bullseye container.
Changes:
- All 8 apt install commands: nginx-plus=37.0.4-1~ → nginx-plus=37.0.*-1~
and nginx-plus=37.1.0-1~ → nginx-plus=37.1.*-1~
- Compatibility table: exact patch versions → version ranges, column header updated
- about-4.9.md supported-packages table: "NGINX Plus R37" → "NGINX Plus R37.0",
since R37.0 and R37.1 are now separate lines and the ambiguity would mislead
The install-a-specific-version instructions pinned app-protect-dos,
nginx-plus-module-appprotectdos and nginx-plus, but left
app-protect-dos-ebpf-manager unpinned in all five OS sections. Because
that command sits above the specific-version subsection, following the
L4 accelerated mitigation path for 4.9.6 installed a 4.10.0 eBPF
manager next to it. Each section now shows how to list the available
eBPF manager versions and pin the matching one.
Also in this commit:
- Replace `apt-cache madison nginx-plus` with `apt-cache showpkg
nginx-plus-r<release>`. madison lists versions and origins only and
never shows the nginx-plus-r<release> virtual package, so it could
not answer the question the surrounding text asked of it.
- Quote the version arguments containing `*` so that the shell does not
expand them as filename patterns.
- Qualify the claim that the unpinned install needs no pin. That holds
only while the newest app-protect-dos is built for the newest NGINX
Plus release, which is not true between an NGINX Plus release and the
DoS release that supports it.
- Split the pinning paragraph and move the apt behaviour into a note.
- Drop a duplicated `sudo` and a stale `_r36` example image tag.
The Kubernetes guides render the Dockerfile includes, all of which
installed app-protect-dos and app-protect-dos-ebpf-manager unpinned.
There was no way to build an image for a specific version, and the
NGINX Plus and eBPF Manager images could drift apart because they are
built separately.
The Debian and Ubuntu Dockerfiles now accept DOS_VERSION and
NGINX_PLUS_VERSION build arguments. Both default to empty, so the
existing latest-version behaviour is unchanged. Both Kubernetes guides
explain version selection and show the matching --build-arg commands,
and the L4 guide calls out that the NGINX Plus and eBPF Manager images
must be built from the same version.
This also fixes two pre-existing faults in the same files:
- Neither Dockerfile ran `apt-get update` after adding the NGINX
repositories, so the install could not locate the packages.
- ubuntu-plus-dos.md had a dangling `RUN apt-get update && \` followed
by a comment and a second RUN, which is not valid Dockerfile syntax.
- Drop "includes bug fixes" from the 4.10 summary, since the New
features list no longer mentions them.
- Remove the leftover "(Trixie)" from the 4.9 feature list so that it
matches the supported packages table.
- Use "F5 DoS for NGINX" rather than "App Protect DoS".
- Note that the eBPF manager needs pinning as well.
- Align the supported packages tables.
The example pinned the Debian 12 suite (-1~bookworm) but sat directly
after the Ubuntu 24.04 command, so copying it on Ubuntu gave apt a
version that does not exist there.
Substitute the suite with `lsb_release -cs`, the idiom already used by
the repository setup steps earlier in this section. One command is now
correct on all four supported suites, and its position no longer
implies a particular one.
The previous commit added DOS_VERSION and NGINX_PLUS_VERSION to the
Debian and Ubuntu Dockerfiles only, but the surrounding paragraph sits
above every operating system section and read as though it applied to
all of them. A reader on Alpine, Amazon Linux, RHEL or Rocky Linux
would have passed --build-arg DOS_VERSION and had it ignored.
Add DOS_VERSION to the remaining twelve Dockerfiles, covering both the
NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8, 9
and 10, and Rocky Linux 9. All default to empty, so the existing
latest-version behaviour is unchanged.
Only Debian and Ubuntu take NGINX_PLUS_VERSION. dnf and apk can both
select an older nginx-plus to satisfy the module's nginx-plus-r<release>
dependency, so pinning it there would wrongly suggest the apt
limitation is universal. Each Dockerfile documents its own version
syntax, which differs by package manager: apt and apk use "=<version>",
dnf uses "-<version>".
The Alpine Dockerfiles declare ARG OS_VER before FROM, which scopes it
to the FROM instruction alone, so DOS_VERSION is declared after FROM
in every file to keep it visible to the RUN instruction.
`apt-get remove --purge --auto-remove -y` was called with no package
names. It does not fail, but it cannot do what it was written to do:
the build-time packages were installed explicitly, so apt marks them
manually installed and --auto-remove never reaps them. Only orphaned
automatically-installed dependencies were eligible, of which there are
none here, making the line a no-op.
Name the four build-time packages instead, matching the form already
used in content/includes/waf/dockerfiles/official-oss.md.
ca-certificates and the archive keyring packages are deliberately kept:
NGINX Plus needs the CA bundle at runtime for licensing and telemetry.
All four purged packages are used earlier in the same RUN instruction
and nothing after the purge depends on them.
@ohad-perets
ohad-peretsforce-pushed the dos/pin-nginx-plus-with-specific-version branch from e5a62ed to 17b62f5CompareSeptember 2, 2026 14:17
@rnitzan
rnitzan merged commit ac0bfd6 into mainSep 2, 2026
9 checks passed
@rnitzan
rnitzan deleted the dos/pin-nginx-plus-with-specific-version branch September 2, 2026 16:03
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationproduct/dosIssues related to F5 DoS for NGINX

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rnitzan@JTorreG@ohad-perets
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes - #2226

Merged
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version
Sep 2, 2026
Merged

docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes#2226
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version

Conversation

@rnitzan

Copy link
Copy Markdown
Contributor

The Debian/Ubuntu instructions for installing a specific App Protect DoS version pin app-protect-dos and nginx-plus-module-appprotectdos but leave nginx-plus unpinned. That only works while the newest nginx-plus in the repository happens to belong to the release the module was built for.

The problem

Each nginx-plus-module-appprotectdos package depends on a virtual package named nginx-plus-r<release>, which only that release of nginx-plus provides. apt considers only a package's candidate (newest) version and will not select an older one to satisfy another package's dependency. So as soon as the repository carries a newer nginx-plus release, the documented command fails:

nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.

This is the current state of the repository. Observed on all four supported Debian/Ubuntu suites:

Candidate: 37.1.0-1~bookworm <- the only version apt will consider
37.1.0 Provides: nginx-plus-r37.1
37.0.5 Provides: nginx-plus-r37.0 <- what the module needs; present, not considered
nginx-plus-module-appprotectdos 37+4.9.6 Depends: nginx-plus-r37.0

A perfectly good pair is sitting in the repository, but following the page as written cannot install it. The RPM instructions are not affected in the same way, and the plus RPM repository has not published r37.1 yet, so I have left those sections alone rather than change them speculatively.

Changes

  • Add nginx-plus=<version> to all four apt-get install examples.
  • Document the nginx-plus-r<release> dependency, and add the two commands that reveal which release a module requires. apt-cache policy app-protect-dos, which the page currently suggests for choosing a version, does not show that dependency, so it cannot lead the reader to a working command.
  • Refresh the examples from 35+4.7.3 to the current 37+4.9.6.
  • Fix the indentation of the Ubuntu 22.04 and 24.04 code fences (they were indented one space further than the surrounding blocks).

Verification

The version strings are taken from apt-cache madison output on live VMs, not composed by hand:

suitenginx-plus 37.0.5module 37+4.9.6
bullseyeconfirmed presentconfirmed present
bookwormconfirmed presentconfirmed present
jammyr37.0 line confirmed (37.0.4 installed successfully)confirmed present
nobler37.0 line confirmed (37.0.4 installed successfully)confirmed present

markdownlint -c .markdownlint.yaml reports the same 34 pre-existing errors in this file before and after the change; no new ones are introduced, and none of the pre-existing ones are in the edited region.

Found while building an automated install-verification pipeline across Amazon Linux 2023, RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04, where every Debian and Ubuntu target failed to install until nginx-plus was pinned alongside the module.

@rnitzan
rnitzan requested a review from a team as a code ownerAugust 23, 2026 18:43
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation product/dos Issues related to F5 DoS for NGINX labels Aug 23, 2026
@github-actions

Copy link
Copy Markdown

Deploy Preview will be available once build job completes!

NameLink
😎 Deploy Previewhttps://frontdoor-test-docs.nginx.com/previews/docs/2226/

@rnitzan
rnitzan marked this pull request as draft August 24, 2026 09:26
@rnitzan
rnitzan marked this pull request as ready for review August 27, 2026 12:01
@rnitzanrnitzan changed the title fix: pin nginx-plus when installing a specific App Protect DoS versiondocs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notesAug 27, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates F5 DoS for NGINX documentation to reflect the current 4.9.6 and 4.10.0 releases, and to prevent Debian/Ubuntu installation failures by documenting and demonstrating correct version pinning for NGINX Plus and related packages.

Changes:

  • Add F5 DoS for NGINX 4.10 release notes and refresh 4.9 release notes to current package versions.
  • Expand the deployment guide with guidance/commands for identifying the required NGINX Plus release and pinning nginx-plus alongside app-protect-dos and the module on Debian/Ubuntu.
  • Update Kubernetes build guidance and Dockerfile include snippets to support pinning package versions (including eBPF Manager alignment guidance).

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
content/nap-dos/releases/about-4.9.mdUpdates 4.9 release notes wording and supported package table to current 4.9.6 / R37.0 details.
content/nap-dos/releases/about-4.10.mdAdds new 4.10 release notes page including R37.1 support and Debian/Ubuntu pinning notes.
content/nap-dos/deployment-guide/learn-about-deployment.mdAdds RPM/APK/APT examples for 4.9.6 and 4.10.0 and documents Debian/Ubuntu pinning + dependency inspection steps.
content/nap-dos/deployment-guide/kubernetes.mdAdds guidance and an example showing how to build images with pinned versions (where supported).
content/nap-dos/deployment-guide/kubernetes-with-L4-accelerated-mitigation.mdAdds guidance and examples for version-pinned builds and aligning the eBPF Manager image version.
content/includes/dos/dockerfiles/ubuntu-plus-dos.mdAdds build args and pins package installs for Ubuntu-based images.
content/includes/dos/dockerfiles/ubuntu-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.
content/includes/dos/dockerfiles/debian-plus-dos.mdAdds build args and pins package installs for Debian-based images.
content/includes/dos/dockerfiles/debian-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threadcontent/includes/dos/dockerfiles/debian-plus-dos.md Outdated
Comment threadcontent/includes/dos/dockerfiles/ubuntu-plus-dos.md Outdated
Comment threadcontent/nap-dos/deployment-guide/kubernetes.md Outdated
Comment threadcontent/nap-dos/deployment-guide/learn-about-deployment.md Outdated
@rnitzan

Copy link
Copy Markdown
ContributorAuthor

Thanks — C3, C4 and C5 were all correct and are fixed. On C1/C2 the conclusion is right but the stated cause is not, so I want to record the correction rather than let it stand.

C1/C2 — apt-get remove --purge --auto-remove -y does not fail the build.

apt has no minimum-argument guard on remove. InstallPackages in apt-private/private-install.cc short-circuits to success when nothing is queued:

if (Cache->DelCount() ==0&&Cache->InstCount() ==0&&Cache->BadCount() ==0)
returnRunScripts("APT::Install::Post-Invoke-Success");

It exits 0. The real defect is different: the build-time packages were installed explicitly, so apt marks them manually installed and --auto-remove can never reap them. Only orphaned automatically-installed dependencies were eligible, of which there are none here — so the line was a no-op, not a failure.

Fixed by naming the four packages, matching the form already used in content/includes/waf/dockerfiles/official-oss.md. ca-certificates and the archive keyrings are deliberately kept, since NGINX Plus needs the CA bundle at runtime for licensing and telemetry. All four purged packages are used earlier in the same RUN and nothing after the purge depends on them.

C3/C4 — build args only existed on Debian/Ubuntu. Correct, and the paragraph sat above every OS section. Rather than narrow the prose, DOS_VERSION is now on all twelve remaining Dockerfiles (NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8/9/10 and Rocky Linux 9), all defaulting to empty so current behaviour is unchanged. NGINX_PLUS_VERSION stays Debian/Ubuntu-only on purpose: dnf and apk can both select an older nginx-plus to satisfy the module's nginx-plus-r<release> dependency, so pinning it elsewhere would imply the apt limitation is universal. Version syntax differs by package manager, so each Dockerfile documents its own (=<version> for apt/apk, -<version> for dnf).

Worth noting: the Alpine Dockerfiles declare ARG OS_VERbeforeFROM, which scopes it to FROM alone. DOS_VERSION is therefore declared after FROM in every file so it stays visible to RUN.

C5 — suite mismatch. Correct. Replaced the hardcoded -1~bookworm with `lsb_release -cs`, the idiom already used by the repository setup steps earlier in that section, so one command is right on all four suites and its position no longer implies one.


Caveat for reviewers: I could not build these images, so the ${DOS_VERSION} concatenation and the apk/dnf version syntax rest on the deployment guide's own documented commands rather than a live build. One pinned build per package manager before merge would close that gap.

The Debian/Ubuntu instructions for installing a specific App Protect DoS version
pin `app-protect-dos` and `nginx-plus-module-appprotectdos` but leave `nginx-plus`
unpinned. That only works while the newest `nginx-plus` in the repository happens to
belong to the release the module was built for.
Each `nginx-plus-module-appprotectdos` package depends on a virtual package named
`nginx-plus-r<release>`, which only that release of `nginx-plus` provides. `apt`
considers only the newest `nginx-plus` available and does not select an older one to
satisfy another package's dependency, so as soon as the repository carries a newer
release the documented command fails:
nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.
This happens today. The repository offers `nginx-plus` 37.1.0, which provides
`nginx-plus-r37.1`, while the current DoS module `37+4.9.6` requires
`nginx-plus-r37.0`. A suitable `nginx-plus` 37.0.5 is present but is never
considered, so following the documentation as written cannot install it.
Add `nginx-plus` to each example, and document the `nginx-plus-r<release>`
dependency along with the commands that reveal which release a module requires.
`apt-cache policy app-protect-dos`, which the page currently suggests for choosing a
version, does not show that dependency, so it cannot lead the reader to a working
command.
Also refresh the examples from `35+4.7.3` to the current `37+4.9.6`, and correct the
indentation of the Ubuntu code fences.
The four examples pinned nginx-plus=37.0.5, which is a pre-release build. On the
repository customers actually use, app-protect-dos 37+4.9.6 pairs with nginx-plus
37.0.4, so the commands as written could not succeed. Verified on Debian 11/12/13 and
Ubuntu 22.04/24.04: an unpinned install from the production repository resolves to
nginx-plus 37.0.4 with nginx-plus-module-appprotectdos 37+4.9.6 and app-protect-dos
37+4.9.6.
Also scope the surrounding explanation. Pinning nginx-plus is needed when the App
Protect DoS version being installed was built for an earlier NGINX Plus release, which
is the situation this "install a specific version" section describes. It is not needed
for the plain `apt-get install app-protect-dos` shown above it, where every package
resolves to its newest version and the dependency on nginx-plus-r<release> is satisfied
without help. Saying so explicitly keeps the pin from being read as a general
requirement.
Version 4.10 adds NGINX Plus R37.1 support, which is the whole of its feature content
per the product changelog (BADOS-2046, 2026-08-26), plus accumulated bug fixes.
Follows the structure of the 4.9 notes, with weight 20 to keep the releases section in
descending order. Uses an "Important notes" section rather than a call-out shortcode,
because that heading is the established convention here and no other release note in
this section uses call-out.
Package strings verified per distribution against installs on Amazon Linux 2023,
RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04. The two Alpine rows are inferred
from the naming pattern the 4.8 and 4.9 notes use, since Alpine is not covered by that
test matrix; they should be checked against the published artifacts before release.
The important-notes section records the release coupling explicitly: every
nginx-plus-module-appprotectdos package depends on a virtual nginx-plus-r<release>
package that only its own NGINX Plus release provides, so 4.10 needs R37.1 where 4.9
needs R37.0, and the two must move together.
The "install a specific version" examples named a single version, and outside the
Debian/Ubuntu section that version was long stale: RHEL 8 and RHEL 9 still showed
35+4.7.3, Amazon Linux 2023 showed 34+4.6.0, and Alpine showed 33+4.5.0-r1.
Each section now shows the two current versions with the NGINX Plus release each one
requires, so a reader can see at a glance that the choice of App Protect DoS version
determines the NGINX Plus version:
37+4.10.0 -> NGINX Plus R37.1 (nginx-plus 37.1.0)
37+4.9.6 -> NGINX Plus R37.0 (nginx-plus 37.0.4)
The Debian/Ubuntu section also gains that mapping as a table, because it is the one
place where getting it wrong produces a confusing failure rather than a clear one:
apt considers only the newest nginx-plus and will not select an older one on its own.
The same nginx-plus-r<release> coupling exists on the RPM side - the metadata carries
nginx-plus-r37.0 and nginx-plus-r37.1 exactly as the Debian packages do - so the dnf
and apk examples name the required release in a comment. They do not add an explicit
nginx-plus version, because there is no evidence here that dnf or apk need one, and
prescribing a pin that has not been observed to be necessary would be a guess.
All package strings verified against installs on Amazon Linux 2023, RHEL 8/9/10,
Debian 11/12/13 and Ubuntu 22.04/24.04. The Alpine strings follow the naming pattern
recorded in the 4.8 and 4.9 release notes; Alpine is not covered by that matrix.
Tested under real apt: apt accepts version globs in pkg=version syntax — both
37.0.* and 37.1.* resolve correctly (rc=0), verified in a Debian bullseye container.
Changes:
- All 8 apt install commands: nginx-plus=37.0.4-1~ → nginx-plus=37.0.*-1~
and nginx-plus=37.1.0-1~ → nginx-plus=37.1.*-1~
- Compatibility table: exact patch versions → version ranges, column header updated
- about-4.9.md supported-packages table: "NGINX Plus R37" → "NGINX Plus R37.0",
since R37.0 and R37.1 are now separate lines and the ambiguity would mislead
The install-a-specific-version instructions pinned app-protect-dos,
nginx-plus-module-appprotectdos and nginx-plus, but left
app-protect-dos-ebpf-manager unpinned in all five OS sections. Because
that command sits above the specific-version subsection, following the
L4 accelerated mitigation path for 4.9.6 installed a 4.10.0 eBPF
manager next to it. Each section now shows how to list the available
eBPF manager versions and pin the matching one.
Also in this commit:
- Replace `apt-cache madison nginx-plus` with `apt-cache showpkg
nginx-plus-r<release>`. madison lists versions and origins only and
never shows the nginx-plus-r<release> virtual package, so it could
not answer the question the surrounding text asked of it.
- Quote the version arguments containing `*` so that the shell does not
expand them as filename patterns.
- Qualify the claim that the unpinned install needs no pin. That holds
only while the newest app-protect-dos is built for the newest NGINX
Plus release, which is not true between an NGINX Plus release and the
DoS release that supports it.
- Split the pinning paragraph and move the apt behaviour into a note.
- Drop a duplicated `sudo` and a stale `_r36` example image tag.
The Kubernetes guides render the Dockerfile includes, all of which
installed app-protect-dos and app-protect-dos-ebpf-manager unpinned.
There was no way to build an image for a specific version, and the
NGINX Plus and eBPF Manager images could drift apart because they are
built separately.
The Debian and Ubuntu Dockerfiles now accept DOS_VERSION and
NGINX_PLUS_VERSION build arguments. Both default to empty, so the
existing latest-version behaviour is unchanged. Both Kubernetes guides
explain version selection and show the matching --build-arg commands,
and the L4 guide calls out that the NGINX Plus and eBPF Manager images
must be built from the same version.
This also fixes two pre-existing faults in the same files:
- Neither Dockerfile ran `apt-get update` after adding the NGINX
repositories, so the install could not locate the packages.
- ubuntu-plus-dos.md had a dangling `RUN apt-get update && \` followed
by a comment and a second RUN, which is not valid Dockerfile syntax.
- Drop "includes bug fixes" from the 4.10 summary, since the New
features list no longer mentions them.
- Remove the leftover "(Trixie)" from the 4.9 feature list so that it
matches the supported packages table.
- Use "F5 DoS for NGINX" rather than "App Protect DoS".
- Note that the eBPF manager needs pinning as well.
- Align the supported packages tables.
The example pinned the Debian 12 suite (-1~bookworm) but sat directly
after the Ubuntu 24.04 command, so copying it on Ubuntu gave apt a
version that does not exist there.
Substitute the suite with `lsb_release -cs`, the idiom already used by
the repository setup steps earlier in this section. One command is now
correct on all four supported suites, and its position no longer
implies a particular one.
The previous commit added DOS_VERSION and NGINX_PLUS_VERSION to the
Debian and Ubuntu Dockerfiles only, but the surrounding paragraph sits
above every operating system section and read as though it applied to
all of them. A reader on Alpine, Amazon Linux, RHEL or Rocky Linux
would have passed --build-arg DOS_VERSION and had it ignored.
Add DOS_VERSION to the remaining twelve Dockerfiles, covering both the
NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8, 9
and 10, and Rocky Linux 9. All default to empty, so the existing
latest-version behaviour is unchanged.
Only Debian and Ubuntu take NGINX_PLUS_VERSION. dnf and apk can both
select an older nginx-plus to satisfy the module's nginx-plus-r<release>
dependency, so pinning it there would wrongly suggest the apt
limitation is universal. Each Dockerfile documents its own version
syntax, which differs by package manager: apt and apk use "=<version>",
dnf uses "-<version>".
The Alpine Dockerfiles declare ARG OS_VER before FROM, which scopes it
to the FROM instruction alone, so DOS_VERSION is declared after FROM
in every file to keep it visible to the RUN instruction.
`apt-get remove --purge --auto-remove -y` was called with no package
names. It does not fail, but it cannot do what it was written to do:
the build-time packages were installed explicitly, so apt marks them
manually installed and --auto-remove never reaps them. Only orphaned
automatically-installed dependencies were eligible, of which there are
none here, making the line a no-op.
Name the four build-time packages instead, matching the form already
used in content/includes/waf/dockerfiles/official-oss.md.
ca-certificates and the archive keyring packages are deliberately kept:
NGINX Plus needs the CA bundle at runtime for licensing and telemetry.
All four purged packages are used earlier in the same RUN instruction
and nothing after the purge depends on them.
@ohad-perets
ohad-peretsforce-pushed the dos/pin-nginx-plus-with-specific-version branch from e5a62ed to 17b62f5CompareSeptember 2, 2026 14:17
@rnitzan
rnitzan merged commit ac0bfd6 into mainSep 2, 2026
9 checks passed
@rnitzan
rnitzan deleted the dos/pin-nginx-plus-with-specific-version branch September 2, 2026 16:03
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationproduct/dosIssues related to F5 DoS for NGINX

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rnitzan@JTorreG@ohad-perets
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes - #2226

Merged
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version
Sep 2, 2026
Merged

docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes#2226
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version

Conversation

@rnitzan

Copy link
Copy Markdown
Contributor

The Debian/Ubuntu instructions for installing a specific App Protect DoS version pin app-protect-dos and nginx-plus-module-appprotectdos but leave nginx-plus unpinned. That only works while the newest nginx-plus in the repository happens to belong to the release the module was built for.

The problem

Each nginx-plus-module-appprotectdos package depends on a virtual package named nginx-plus-r<release>, which only that release of nginx-plus provides. apt considers only a package's candidate (newest) version and will not select an older one to satisfy another package's dependency. So as soon as the repository carries a newer nginx-plus release, the documented command fails:

nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.

This is the current state of the repository. Observed on all four supported Debian/Ubuntu suites:

Candidate: 37.1.0-1~bookworm <- the only version apt will consider
37.1.0 Provides: nginx-plus-r37.1
37.0.5 Provides: nginx-plus-r37.0 <- what the module needs; present, not considered
nginx-plus-module-appprotectdos 37+4.9.6 Depends: nginx-plus-r37.0

A perfectly good pair is sitting in the repository, but following the page as written cannot install it. The RPM instructions are not affected in the same way, and the plus RPM repository has not published r37.1 yet, so I have left those sections alone rather than change them speculatively.

Changes

  • Add nginx-plus=<version> to all four apt-get install examples.
  • Document the nginx-plus-r<release> dependency, and add the two commands that reveal which release a module requires. apt-cache policy app-protect-dos, which the page currently suggests for choosing a version, does not show that dependency, so it cannot lead the reader to a working command.
  • Refresh the examples from 35+4.7.3 to the current 37+4.9.6.
  • Fix the indentation of the Ubuntu 22.04 and 24.04 code fences (they were indented one space further than the surrounding blocks).

Verification

The version strings are taken from apt-cache madison output on live VMs, not composed by hand:

suitenginx-plus 37.0.5module 37+4.9.6
bullseyeconfirmed presentconfirmed present
bookwormconfirmed presentconfirmed present
jammyr37.0 line confirmed (37.0.4 installed successfully)confirmed present
nobler37.0 line confirmed (37.0.4 installed successfully)confirmed present

markdownlint -c .markdownlint.yaml reports the same 34 pre-existing errors in this file before and after the change; no new ones are introduced, and none of the pre-existing ones are in the edited region.

Found while building an automated install-verification pipeline across Amazon Linux 2023, RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04, where every Debian and Ubuntu target failed to install until nginx-plus was pinned alongside the module.

@rnitzan
rnitzan requested a review from a team as a code ownerAugust 23, 2026 18:43
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation product/dos Issues related to F5 DoS for NGINX labels Aug 23, 2026
@github-actions

Copy link
Copy Markdown

Deploy Preview will be available once build job completes!

NameLink
😎 Deploy Previewhttps://frontdoor-test-docs.nginx.com/previews/docs/2226/

@rnitzan
rnitzan marked this pull request as draft August 24, 2026 09:26
@rnitzan
rnitzan marked this pull request as ready for review August 27, 2026 12:01
@rnitzanrnitzan changed the title fix: pin nginx-plus when installing a specific App Protect DoS versiondocs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notesAug 27, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates F5 DoS for NGINX documentation to reflect the current 4.9.6 and 4.10.0 releases, and to prevent Debian/Ubuntu installation failures by documenting and demonstrating correct version pinning for NGINX Plus and related packages.

Changes:

  • Add F5 DoS for NGINX 4.10 release notes and refresh 4.9 release notes to current package versions.
  • Expand the deployment guide with guidance/commands for identifying the required NGINX Plus release and pinning nginx-plus alongside app-protect-dos and the module on Debian/Ubuntu.
  • Update Kubernetes build guidance and Dockerfile include snippets to support pinning package versions (including eBPF Manager alignment guidance).

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
content/nap-dos/releases/about-4.9.mdUpdates 4.9 release notes wording and supported package table to current 4.9.6 / R37.0 details.
content/nap-dos/releases/about-4.10.mdAdds new 4.10 release notes page including R37.1 support and Debian/Ubuntu pinning notes.
content/nap-dos/deployment-guide/learn-about-deployment.mdAdds RPM/APK/APT examples for 4.9.6 and 4.10.0 and documents Debian/Ubuntu pinning + dependency inspection steps.
content/nap-dos/deployment-guide/kubernetes.mdAdds guidance and an example showing how to build images with pinned versions (where supported).
content/nap-dos/deployment-guide/kubernetes-with-L4-accelerated-mitigation.mdAdds guidance and examples for version-pinned builds and aligning the eBPF Manager image version.
content/includes/dos/dockerfiles/ubuntu-plus-dos.mdAdds build args and pins package installs for Ubuntu-based images.
content/includes/dos/dockerfiles/ubuntu-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.
content/includes/dos/dockerfiles/debian-plus-dos.mdAdds build args and pins package installs for Debian-based images.
content/includes/dos/dockerfiles/debian-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threadcontent/includes/dos/dockerfiles/debian-plus-dos.md Outdated
Comment threadcontent/includes/dos/dockerfiles/ubuntu-plus-dos.md Outdated
Comment threadcontent/nap-dos/deployment-guide/kubernetes.md Outdated
Comment threadcontent/nap-dos/deployment-guide/learn-about-deployment.md Outdated
@rnitzan

Copy link
Copy Markdown
ContributorAuthor

Thanks — C3, C4 and C5 were all correct and are fixed. On C1/C2 the conclusion is right but the stated cause is not, so I want to record the correction rather than let it stand.

C1/C2 — apt-get remove --purge --auto-remove -y does not fail the build.

apt has no minimum-argument guard on remove. InstallPackages in apt-private/private-install.cc short-circuits to success when nothing is queued:

if (Cache->DelCount() ==0&&Cache->InstCount() ==0&&Cache->BadCount() ==0)
returnRunScripts("APT::Install::Post-Invoke-Success");

It exits 0. The real defect is different: the build-time packages were installed explicitly, so apt marks them manually installed and --auto-remove can never reap them. Only orphaned automatically-installed dependencies were eligible, of which there are none here — so the line was a no-op, not a failure.

Fixed by naming the four packages, matching the form already used in content/includes/waf/dockerfiles/official-oss.md. ca-certificates and the archive keyrings are deliberately kept, since NGINX Plus needs the CA bundle at runtime for licensing and telemetry. All four purged packages are used earlier in the same RUN and nothing after the purge depends on them.

C3/C4 — build args only existed on Debian/Ubuntu. Correct, and the paragraph sat above every OS section. Rather than narrow the prose, DOS_VERSION is now on all twelve remaining Dockerfiles (NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8/9/10 and Rocky Linux 9), all defaulting to empty so current behaviour is unchanged. NGINX_PLUS_VERSION stays Debian/Ubuntu-only on purpose: dnf and apk can both select an older nginx-plus to satisfy the module's nginx-plus-r<release> dependency, so pinning it elsewhere would imply the apt limitation is universal. Version syntax differs by package manager, so each Dockerfile documents its own (=<version> for apt/apk, -<version> for dnf).

Worth noting: the Alpine Dockerfiles declare ARG OS_VERbeforeFROM, which scopes it to FROM alone. DOS_VERSION is therefore declared after FROM in every file so it stays visible to RUN.

C5 — suite mismatch. Correct. Replaced the hardcoded -1~bookworm with `lsb_release -cs`, the idiom already used by the repository setup steps earlier in that section, so one command is right on all four suites and its position no longer implies one.


Caveat for reviewers: I could not build these images, so the ${DOS_VERSION} concatenation and the apk/dnf version syntax rest on the deployment guide's own documented commands rather than a live build. One pinned build per package manager before merge would close that gap.

The Debian/Ubuntu instructions for installing a specific App Protect DoS version
pin `app-protect-dos` and `nginx-plus-module-appprotectdos` but leave `nginx-plus`
unpinned. That only works while the newest `nginx-plus` in the repository happens to
belong to the release the module was built for.
Each `nginx-plus-module-appprotectdos` package depends on a virtual package named
`nginx-plus-r<release>`, which only that release of `nginx-plus` provides. `apt`
considers only the newest `nginx-plus` available and does not select an older one to
satisfy another package's dependency, so as soon as the repository carries a newer
release the documented command fails:
nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.
This happens today. The repository offers `nginx-plus` 37.1.0, which provides
`nginx-plus-r37.1`, while the current DoS module `37+4.9.6` requires
`nginx-plus-r37.0`. A suitable `nginx-plus` 37.0.5 is present but is never
considered, so following the documentation as written cannot install it.
Add `nginx-plus` to each example, and document the `nginx-plus-r<release>`
dependency along with the commands that reveal which release a module requires.
`apt-cache policy app-protect-dos`, which the page currently suggests for choosing a
version, does not show that dependency, so it cannot lead the reader to a working
command.
Also refresh the examples from `35+4.7.3` to the current `37+4.9.6`, and correct the
indentation of the Ubuntu code fences.
The four examples pinned nginx-plus=37.0.5, which is a pre-release build. On the
repository customers actually use, app-protect-dos 37+4.9.6 pairs with nginx-plus
37.0.4, so the commands as written could not succeed. Verified on Debian 11/12/13 and
Ubuntu 22.04/24.04: an unpinned install from the production repository resolves to
nginx-plus 37.0.4 with nginx-plus-module-appprotectdos 37+4.9.6 and app-protect-dos
37+4.9.6.
Also scope the surrounding explanation. Pinning nginx-plus is needed when the App
Protect DoS version being installed was built for an earlier NGINX Plus release, which
is the situation this "install a specific version" section describes. It is not needed
for the plain `apt-get install app-protect-dos` shown above it, where every package
resolves to its newest version and the dependency on nginx-plus-r<release> is satisfied
without help. Saying so explicitly keeps the pin from being read as a general
requirement.
Version 4.10 adds NGINX Plus R37.1 support, which is the whole of its feature content
per the product changelog (BADOS-2046, 2026-08-26), plus accumulated bug fixes.
Follows the structure of the 4.9 notes, with weight 20 to keep the releases section in
descending order. Uses an "Important notes" section rather than a call-out shortcode,
because that heading is the established convention here and no other release note in
this section uses call-out.
Package strings verified per distribution against installs on Amazon Linux 2023,
RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04. The two Alpine rows are inferred
from the naming pattern the 4.8 and 4.9 notes use, since Alpine is not covered by that
test matrix; they should be checked against the published artifacts before release.
The important-notes section records the release coupling explicitly: every
nginx-plus-module-appprotectdos package depends on a virtual nginx-plus-r<release>
package that only its own NGINX Plus release provides, so 4.10 needs R37.1 where 4.9
needs R37.0, and the two must move together.
The "install a specific version" examples named a single version, and outside the
Debian/Ubuntu section that version was long stale: RHEL 8 and RHEL 9 still showed
35+4.7.3, Amazon Linux 2023 showed 34+4.6.0, and Alpine showed 33+4.5.0-r1.
Each section now shows the two current versions with the NGINX Plus release each one
requires, so a reader can see at a glance that the choice of App Protect DoS version
determines the NGINX Plus version:
37+4.10.0 -> NGINX Plus R37.1 (nginx-plus 37.1.0)
37+4.9.6 -> NGINX Plus R37.0 (nginx-plus 37.0.4)
The Debian/Ubuntu section also gains that mapping as a table, because it is the one
place where getting it wrong produces a confusing failure rather than a clear one:
apt considers only the newest nginx-plus and will not select an older one on its own.
The same nginx-plus-r<release> coupling exists on the RPM side - the metadata carries
nginx-plus-r37.0 and nginx-plus-r37.1 exactly as the Debian packages do - so the dnf
and apk examples name the required release in a comment. They do not add an explicit
nginx-plus version, because there is no evidence here that dnf or apk need one, and
prescribing a pin that has not been observed to be necessary would be a guess.
All package strings verified against installs on Amazon Linux 2023, RHEL 8/9/10,
Debian 11/12/13 and Ubuntu 22.04/24.04. The Alpine strings follow the naming pattern
recorded in the 4.8 and 4.9 release notes; Alpine is not covered by that matrix.
Tested under real apt: apt accepts version globs in pkg=version syntax — both
37.0.* and 37.1.* resolve correctly (rc=0), verified in a Debian bullseye container.
Changes:
- All 8 apt install commands: nginx-plus=37.0.4-1~ → nginx-plus=37.0.*-1~
and nginx-plus=37.1.0-1~ → nginx-plus=37.1.*-1~
- Compatibility table: exact patch versions → version ranges, column header updated
- about-4.9.md supported-packages table: "NGINX Plus R37" → "NGINX Plus R37.0",
since R37.0 and R37.1 are now separate lines and the ambiguity would mislead
The install-a-specific-version instructions pinned app-protect-dos,
nginx-plus-module-appprotectdos and nginx-plus, but left
app-protect-dos-ebpf-manager unpinned in all five OS sections. Because
that command sits above the specific-version subsection, following the
L4 accelerated mitigation path for 4.9.6 installed a 4.10.0 eBPF
manager next to it. Each section now shows how to list the available
eBPF manager versions and pin the matching one.
Also in this commit:
- Replace `apt-cache madison nginx-plus` with `apt-cache showpkg
nginx-plus-r<release>`. madison lists versions and origins only and
never shows the nginx-plus-r<release> virtual package, so it could
not answer the question the surrounding text asked of it.
- Quote the version arguments containing `*` so that the shell does not
expand them as filename patterns.
- Qualify the claim that the unpinned install needs no pin. That holds
only while the newest app-protect-dos is built for the newest NGINX
Plus release, which is not true between an NGINX Plus release and the
DoS release that supports it.
- Split the pinning paragraph and move the apt behaviour into a note.
- Drop a duplicated `sudo` and a stale `_r36` example image tag.
The Kubernetes guides render the Dockerfile includes, all of which
installed app-protect-dos and app-protect-dos-ebpf-manager unpinned.
There was no way to build an image for a specific version, and the
NGINX Plus and eBPF Manager images could drift apart because they are
built separately.
The Debian and Ubuntu Dockerfiles now accept DOS_VERSION and
NGINX_PLUS_VERSION build arguments. Both default to empty, so the
existing latest-version behaviour is unchanged. Both Kubernetes guides
explain version selection and show the matching --build-arg commands,
and the L4 guide calls out that the NGINX Plus and eBPF Manager images
must be built from the same version.
This also fixes two pre-existing faults in the same files:
- Neither Dockerfile ran `apt-get update` after adding the NGINX
repositories, so the install could not locate the packages.
- ubuntu-plus-dos.md had a dangling `RUN apt-get update && \` followed
by a comment and a second RUN, which is not valid Dockerfile syntax.
- Drop "includes bug fixes" from the 4.10 summary, since the New
features list no longer mentions them.
- Remove the leftover "(Trixie)" from the 4.9 feature list so that it
matches the supported packages table.
- Use "F5 DoS for NGINX" rather than "App Protect DoS".
- Note that the eBPF manager needs pinning as well.
- Align the supported packages tables.
The example pinned the Debian 12 suite (-1~bookworm) but sat directly
after the Ubuntu 24.04 command, so copying it on Ubuntu gave apt a
version that does not exist there.
Substitute the suite with `lsb_release -cs`, the idiom already used by
the repository setup steps earlier in this section. One command is now
correct on all four supported suites, and its position no longer
implies a particular one.
The previous commit added DOS_VERSION and NGINX_PLUS_VERSION to the
Debian and Ubuntu Dockerfiles only, but the surrounding paragraph sits
above every operating system section and read as though it applied to
all of them. A reader on Alpine, Amazon Linux, RHEL or Rocky Linux
would have passed --build-arg DOS_VERSION and had it ignored.
Add DOS_VERSION to the remaining twelve Dockerfiles, covering both the
NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8, 9
and 10, and Rocky Linux 9. All default to empty, so the existing
latest-version behaviour is unchanged.
Only Debian and Ubuntu take NGINX_PLUS_VERSION. dnf and apk can both
select an older nginx-plus to satisfy the module's nginx-plus-r<release>
dependency, so pinning it there would wrongly suggest the apt
limitation is universal. Each Dockerfile documents its own version
syntax, which differs by package manager: apt and apk use "=<version>",
dnf uses "-<version>".
The Alpine Dockerfiles declare ARG OS_VER before FROM, which scopes it
to the FROM instruction alone, so DOS_VERSION is declared after FROM
in every file to keep it visible to the RUN instruction.
`apt-get remove --purge --auto-remove -y` was called with no package
names. It does not fail, but it cannot do what it was written to do:
the build-time packages were installed explicitly, so apt marks them
manually installed and --auto-remove never reaps them. Only orphaned
automatically-installed dependencies were eligible, of which there are
none here, making the line a no-op.
Name the four build-time packages instead, matching the form already
used in content/includes/waf/dockerfiles/official-oss.md.
ca-certificates and the archive keyring packages are deliberately kept:
NGINX Plus needs the CA bundle at runtime for licensing and telemetry.
All four purged packages are used earlier in the same RUN instruction
and nothing after the purge depends on them.
@ohad-perets
ohad-peretsforce-pushed the dos/pin-nginx-plus-with-specific-version branch from e5a62ed to 17b62f5CompareSeptember 2, 2026 14:17
@rnitzan
rnitzan merged commit ac0bfd6 into mainSep 2, 2026
9 checks passed
@rnitzan
rnitzan deleted the dos/pin-nginx-plus-with-specific-version branch September 2, 2026 16:03
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationproduct/dosIssues related to F5 DoS for NGINX

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rnitzan@JTorreG@ohad-perets
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes - #2226

Merged
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version
Sep 2, 2026
Merged

docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes#2226
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version

Conversation

@rnitzan

Copy link
Copy Markdown
Contributor

The Debian/Ubuntu instructions for installing a specific App Protect DoS version pin app-protect-dos and nginx-plus-module-appprotectdos but leave nginx-plus unpinned. That only works while the newest nginx-plus in the repository happens to belong to the release the module was built for.

The problem

Each nginx-plus-module-appprotectdos package depends on a virtual package named nginx-plus-r<release>, which only that release of nginx-plus provides. apt considers only a package's candidate (newest) version and will not select an older one to satisfy another package's dependency. So as soon as the repository carries a newer nginx-plus release, the documented command fails:

nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.

This is the current state of the repository. Observed on all four supported Debian/Ubuntu suites:

Candidate: 37.1.0-1~bookworm <- the only version apt will consider
37.1.0 Provides: nginx-plus-r37.1
37.0.5 Provides: nginx-plus-r37.0 <- what the module needs; present, not considered
nginx-plus-module-appprotectdos 37+4.9.6 Depends: nginx-plus-r37.0

A perfectly good pair is sitting in the repository, but following the page as written cannot install it. The RPM instructions are not affected in the same way, and the plus RPM repository has not published r37.1 yet, so I have left those sections alone rather than change them speculatively.

Changes

  • Add nginx-plus=<version> to all four apt-get install examples.
  • Document the nginx-plus-r<release> dependency, and add the two commands that reveal which release a module requires. apt-cache policy app-protect-dos, which the page currently suggests for choosing a version, does not show that dependency, so it cannot lead the reader to a working command.
  • Refresh the examples from 35+4.7.3 to the current 37+4.9.6.
  • Fix the indentation of the Ubuntu 22.04 and 24.04 code fences (they were indented one space further than the surrounding blocks).

Verification

The version strings are taken from apt-cache madison output on live VMs, not composed by hand:

suitenginx-plus 37.0.5module 37+4.9.6
bullseyeconfirmed presentconfirmed present
bookwormconfirmed presentconfirmed present
jammyr37.0 line confirmed (37.0.4 installed successfully)confirmed present
nobler37.0 line confirmed (37.0.4 installed successfully)confirmed present

markdownlint -c .markdownlint.yaml reports the same 34 pre-existing errors in this file before and after the change; no new ones are introduced, and none of the pre-existing ones are in the edited region.

Found while building an automated install-verification pipeline across Amazon Linux 2023, RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04, where every Debian and Ubuntu target failed to install until nginx-plus was pinned alongside the module.

@rnitzan
rnitzan requested a review from a team as a code ownerAugust 23, 2026 18:43
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation product/dos Issues related to F5 DoS for NGINX labels Aug 23, 2026
@github-actions

Copy link
Copy Markdown

Deploy Preview will be available once build job completes!

NameLink
😎 Deploy Previewhttps://frontdoor-test-docs.nginx.com/previews/docs/2226/

@rnitzan
rnitzan marked this pull request as draft August 24, 2026 09:26
@rnitzan
rnitzan marked this pull request as ready for review August 27, 2026 12:01
@rnitzanrnitzan changed the title fix: pin nginx-plus when installing a specific App Protect DoS versiondocs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notesAug 27, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates F5 DoS for NGINX documentation to reflect the current 4.9.6 and 4.10.0 releases, and to prevent Debian/Ubuntu installation failures by documenting and demonstrating correct version pinning for NGINX Plus and related packages.

Changes:

  • Add F5 DoS for NGINX 4.10 release notes and refresh 4.9 release notes to current package versions.
  • Expand the deployment guide with guidance/commands for identifying the required NGINX Plus release and pinning nginx-plus alongside app-protect-dos and the module on Debian/Ubuntu.
  • Update Kubernetes build guidance and Dockerfile include snippets to support pinning package versions (including eBPF Manager alignment guidance).

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
content/nap-dos/releases/about-4.9.mdUpdates 4.9 release notes wording and supported package table to current 4.9.6 / R37.0 details.
content/nap-dos/releases/about-4.10.mdAdds new 4.10 release notes page including R37.1 support and Debian/Ubuntu pinning notes.
content/nap-dos/deployment-guide/learn-about-deployment.mdAdds RPM/APK/APT examples for 4.9.6 and 4.10.0 and documents Debian/Ubuntu pinning + dependency inspection steps.
content/nap-dos/deployment-guide/kubernetes.mdAdds guidance and an example showing how to build images with pinned versions (where supported).
content/nap-dos/deployment-guide/kubernetes-with-L4-accelerated-mitigation.mdAdds guidance and examples for version-pinned builds and aligning the eBPF Manager image version.
content/includes/dos/dockerfiles/ubuntu-plus-dos.mdAdds build args and pins package installs for Ubuntu-based images.
content/includes/dos/dockerfiles/ubuntu-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.
content/includes/dos/dockerfiles/debian-plus-dos.mdAdds build args and pins package installs for Debian-based images.
content/includes/dos/dockerfiles/debian-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threadcontent/includes/dos/dockerfiles/debian-plus-dos.md Outdated
Comment threadcontent/includes/dos/dockerfiles/ubuntu-plus-dos.md Outdated
Comment threadcontent/nap-dos/deployment-guide/kubernetes.md Outdated
Comment threadcontent/nap-dos/deployment-guide/learn-about-deployment.md Outdated
@rnitzan

Copy link
Copy Markdown
ContributorAuthor

Thanks — C3, C4 and C5 were all correct and are fixed. On C1/C2 the conclusion is right but the stated cause is not, so I want to record the correction rather than let it stand.

C1/C2 — apt-get remove --purge --auto-remove -y does not fail the build.

apt has no minimum-argument guard on remove. InstallPackages in apt-private/private-install.cc short-circuits to success when nothing is queued:

if (Cache->DelCount() ==0&&Cache->InstCount() ==0&&Cache->BadCount() ==0)
returnRunScripts("APT::Install::Post-Invoke-Success");

It exits 0. The real defect is different: the build-time packages were installed explicitly, so apt marks them manually installed and --auto-remove can never reap them. Only orphaned automatically-installed dependencies were eligible, of which there are none here — so the line was a no-op, not a failure.

Fixed by naming the four packages, matching the form already used in content/includes/waf/dockerfiles/official-oss.md. ca-certificates and the archive keyrings are deliberately kept, since NGINX Plus needs the CA bundle at runtime for licensing and telemetry. All four purged packages are used earlier in the same RUN and nothing after the purge depends on them.

C3/C4 — build args only existed on Debian/Ubuntu. Correct, and the paragraph sat above every OS section. Rather than narrow the prose, DOS_VERSION is now on all twelve remaining Dockerfiles (NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8/9/10 and Rocky Linux 9), all defaulting to empty so current behaviour is unchanged. NGINX_PLUS_VERSION stays Debian/Ubuntu-only on purpose: dnf and apk can both select an older nginx-plus to satisfy the module's nginx-plus-r<release> dependency, so pinning it elsewhere would imply the apt limitation is universal. Version syntax differs by package manager, so each Dockerfile documents its own (=<version> for apt/apk, -<version> for dnf).

Worth noting: the Alpine Dockerfiles declare ARG OS_VERbeforeFROM, which scopes it to FROM alone. DOS_VERSION is therefore declared after FROM in every file so it stays visible to RUN.

C5 — suite mismatch. Correct. Replaced the hardcoded -1~bookworm with `lsb_release -cs`, the idiom already used by the repository setup steps earlier in that section, so one command is right on all four suites and its position no longer implies one.


Caveat for reviewers: I could not build these images, so the ${DOS_VERSION} concatenation and the apk/dnf version syntax rest on the deployment guide's own documented commands rather than a live build. One pinned build per package manager before merge would close that gap.

The Debian/Ubuntu instructions for installing a specific App Protect DoS version
pin `app-protect-dos` and `nginx-plus-module-appprotectdos` but leave `nginx-plus`
unpinned. That only works while the newest `nginx-plus` in the repository happens to
belong to the release the module was built for.
Each `nginx-plus-module-appprotectdos` package depends on a virtual package named
`nginx-plus-r<release>`, which only that release of `nginx-plus` provides. `apt`
considers only the newest `nginx-plus` available and does not select an older one to
satisfy another package's dependency, so as soon as the repository carries a newer
release the documented command fails:
nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.
This happens today. The repository offers `nginx-plus` 37.1.0, which provides
`nginx-plus-r37.1`, while the current DoS module `37+4.9.6` requires
`nginx-plus-r37.0`. A suitable `nginx-plus` 37.0.5 is present but is never
considered, so following the documentation as written cannot install it.
Add `nginx-plus` to each example, and document the `nginx-plus-r<release>`
dependency along with the commands that reveal which release a module requires.
`apt-cache policy app-protect-dos`, which the page currently suggests for choosing a
version, does not show that dependency, so it cannot lead the reader to a working
command.
Also refresh the examples from `35+4.7.3` to the current `37+4.9.6`, and correct the
indentation of the Ubuntu code fences.
The four examples pinned nginx-plus=37.0.5, which is a pre-release build. On the
repository customers actually use, app-protect-dos 37+4.9.6 pairs with nginx-plus
37.0.4, so the commands as written could not succeed. Verified on Debian 11/12/13 and
Ubuntu 22.04/24.04: an unpinned install from the production repository resolves to
nginx-plus 37.0.4 with nginx-plus-module-appprotectdos 37+4.9.6 and app-protect-dos
37+4.9.6.
Also scope the surrounding explanation. Pinning nginx-plus is needed when the App
Protect DoS version being installed was built for an earlier NGINX Plus release, which
is the situation this "install a specific version" section describes. It is not needed
for the plain `apt-get install app-protect-dos` shown above it, where every package
resolves to its newest version and the dependency on nginx-plus-r<release> is satisfied
without help. Saying so explicitly keeps the pin from being read as a general
requirement.
Version 4.10 adds NGINX Plus R37.1 support, which is the whole of its feature content
per the product changelog (BADOS-2046, 2026-08-26), plus accumulated bug fixes.
Follows the structure of the 4.9 notes, with weight 20 to keep the releases section in
descending order. Uses an "Important notes" section rather than a call-out shortcode,
because that heading is the established convention here and no other release note in
this section uses call-out.
Package strings verified per distribution against installs on Amazon Linux 2023,
RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04. The two Alpine rows are inferred
from the naming pattern the 4.8 and 4.9 notes use, since Alpine is not covered by that
test matrix; they should be checked against the published artifacts before release.
The important-notes section records the release coupling explicitly: every
nginx-plus-module-appprotectdos package depends on a virtual nginx-plus-r<release>
package that only its own NGINX Plus release provides, so 4.10 needs R37.1 where 4.9
needs R37.0, and the two must move together.
The "install a specific version" examples named a single version, and outside the
Debian/Ubuntu section that version was long stale: RHEL 8 and RHEL 9 still showed
35+4.7.3, Amazon Linux 2023 showed 34+4.6.0, and Alpine showed 33+4.5.0-r1.
Each section now shows the two current versions with the NGINX Plus release each one
requires, so a reader can see at a glance that the choice of App Protect DoS version
determines the NGINX Plus version:
37+4.10.0 -> NGINX Plus R37.1 (nginx-plus 37.1.0)
37+4.9.6 -> NGINX Plus R37.0 (nginx-plus 37.0.4)
The Debian/Ubuntu section also gains that mapping as a table, because it is the one
place where getting it wrong produces a confusing failure rather than a clear one:
apt considers only the newest nginx-plus and will not select an older one on its own.
The same nginx-plus-r<release> coupling exists on the RPM side - the metadata carries
nginx-plus-r37.0 and nginx-plus-r37.1 exactly as the Debian packages do - so the dnf
and apk examples name the required release in a comment. They do not add an explicit
nginx-plus version, because there is no evidence here that dnf or apk need one, and
prescribing a pin that has not been observed to be necessary would be a guess.
All package strings verified against installs on Amazon Linux 2023, RHEL 8/9/10,
Debian 11/12/13 and Ubuntu 22.04/24.04. The Alpine strings follow the naming pattern
recorded in the 4.8 and 4.9 release notes; Alpine is not covered by that matrix.
Tested under real apt: apt accepts version globs in pkg=version syntax — both
37.0.* and 37.1.* resolve correctly (rc=0), verified in a Debian bullseye container.
Changes:
- All 8 apt install commands: nginx-plus=37.0.4-1~ → nginx-plus=37.0.*-1~
and nginx-plus=37.1.0-1~ → nginx-plus=37.1.*-1~
- Compatibility table: exact patch versions → version ranges, column header updated
- about-4.9.md supported-packages table: "NGINX Plus R37" → "NGINX Plus R37.0",
since R37.0 and R37.1 are now separate lines and the ambiguity would mislead
The install-a-specific-version instructions pinned app-protect-dos,
nginx-plus-module-appprotectdos and nginx-plus, but left
app-protect-dos-ebpf-manager unpinned in all five OS sections. Because
that command sits above the specific-version subsection, following the
L4 accelerated mitigation path for 4.9.6 installed a 4.10.0 eBPF
manager next to it. Each section now shows how to list the available
eBPF manager versions and pin the matching one.
Also in this commit:
- Replace `apt-cache madison nginx-plus` with `apt-cache showpkg
nginx-plus-r<release>`. madison lists versions and origins only and
never shows the nginx-plus-r<release> virtual package, so it could
not answer the question the surrounding text asked of it.
- Quote the version arguments containing `*` so that the shell does not
expand them as filename patterns.
- Qualify the claim that the unpinned install needs no pin. That holds
only while the newest app-protect-dos is built for the newest NGINX
Plus release, which is not true between an NGINX Plus release and the
DoS release that supports it.
- Split the pinning paragraph and move the apt behaviour into a note.
- Drop a duplicated `sudo` and a stale `_r36` example image tag.
The Kubernetes guides render the Dockerfile includes, all of which
installed app-protect-dos and app-protect-dos-ebpf-manager unpinned.
There was no way to build an image for a specific version, and the
NGINX Plus and eBPF Manager images could drift apart because they are
built separately.
The Debian and Ubuntu Dockerfiles now accept DOS_VERSION and
NGINX_PLUS_VERSION build arguments. Both default to empty, so the
existing latest-version behaviour is unchanged. Both Kubernetes guides
explain version selection and show the matching --build-arg commands,
and the L4 guide calls out that the NGINX Plus and eBPF Manager images
must be built from the same version.
This also fixes two pre-existing faults in the same files:
- Neither Dockerfile ran `apt-get update` after adding the NGINX
repositories, so the install could not locate the packages.
- ubuntu-plus-dos.md had a dangling `RUN apt-get update && \` followed
by a comment and a second RUN, which is not valid Dockerfile syntax.
- Drop "includes bug fixes" from the 4.10 summary, since the New
features list no longer mentions them.
- Remove the leftover "(Trixie)" from the 4.9 feature list so that it
matches the supported packages table.
- Use "F5 DoS for NGINX" rather than "App Protect DoS".
- Note that the eBPF manager needs pinning as well.
- Align the supported packages tables.
The example pinned the Debian 12 suite (-1~bookworm) but sat directly
after the Ubuntu 24.04 command, so copying it on Ubuntu gave apt a
version that does not exist there.
Substitute the suite with `lsb_release -cs`, the idiom already used by
the repository setup steps earlier in this section. One command is now
correct on all four supported suites, and its position no longer
implies a particular one.
The previous commit added DOS_VERSION and NGINX_PLUS_VERSION to the
Debian and Ubuntu Dockerfiles only, but the surrounding paragraph sits
above every operating system section and read as though it applied to
all of them. A reader on Alpine, Amazon Linux, RHEL or Rocky Linux
would have passed --build-arg DOS_VERSION and had it ignored.
Add DOS_VERSION to the remaining twelve Dockerfiles, covering both the
NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8, 9
and 10, and Rocky Linux 9. All default to empty, so the existing
latest-version behaviour is unchanged.
Only Debian and Ubuntu take NGINX_PLUS_VERSION. dnf and apk can both
select an older nginx-plus to satisfy the module's nginx-plus-r<release>
dependency, so pinning it there would wrongly suggest the apt
limitation is universal. Each Dockerfile documents its own version
syntax, which differs by package manager: apt and apk use "=<version>",
dnf uses "-<version>".
The Alpine Dockerfiles declare ARG OS_VER before FROM, which scopes it
to the FROM instruction alone, so DOS_VERSION is declared after FROM
in every file to keep it visible to the RUN instruction.
`apt-get remove --purge --auto-remove -y` was called with no package
names. It does not fail, but it cannot do what it was written to do:
the build-time packages were installed explicitly, so apt marks them
manually installed and --auto-remove never reaps them. Only orphaned
automatically-installed dependencies were eligible, of which there are
none here, making the line a no-op.
Name the four build-time packages instead, matching the form already
used in content/includes/waf/dockerfiles/official-oss.md.
ca-certificates and the archive keyring packages are deliberately kept:
NGINX Plus needs the CA bundle at runtime for licensing and telemetry.
All four purged packages are used earlier in the same RUN instruction
and nothing after the purge depends on them.
@ohad-perets
ohad-peretsforce-pushed the dos/pin-nginx-plus-with-specific-version branch from e5a62ed to 17b62f5CompareSeptember 2, 2026 14:17
@rnitzan
rnitzan merged commit ac0bfd6 into mainSep 2, 2026
9 checks passed
@rnitzan
rnitzan deleted the dos/pin-nginx-plus-with-specific-version branch September 2, 2026 16:03
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationproduct/dosIssues related to F5 DoS for NGINX

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rnitzan@JTorreG@ohad-perets
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes - #2226

Merged
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version
Sep 2, 2026
Merged

docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes#2226
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version

Conversation

@rnitzan

Copy link
Copy Markdown
Contributor

The Debian/Ubuntu instructions for installing a specific App Protect DoS version pin app-protect-dos and nginx-plus-module-appprotectdos but leave nginx-plus unpinned. That only works while the newest nginx-plus in the repository happens to belong to the release the module was built for.

The problem

Each nginx-plus-module-appprotectdos package depends on a virtual package named nginx-plus-r<release>, which only that release of nginx-plus provides. apt considers only a package's candidate (newest) version and will not select an older one to satisfy another package's dependency. So as soon as the repository carries a newer nginx-plus release, the documented command fails:

nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.

This is the current state of the repository. Observed on all four supported Debian/Ubuntu suites:

Candidate: 37.1.0-1~bookworm <- the only version apt will consider
37.1.0 Provides: nginx-plus-r37.1
37.0.5 Provides: nginx-plus-r37.0 <- what the module needs; present, not considered
nginx-plus-module-appprotectdos 37+4.9.6 Depends: nginx-plus-r37.0

A perfectly good pair is sitting in the repository, but following the page as written cannot install it. The RPM instructions are not affected in the same way, and the plus RPM repository has not published r37.1 yet, so I have left those sections alone rather than change them speculatively.

Changes

  • Add nginx-plus=<version> to all four apt-get install examples.
  • Document the nginx-plus-r<release> dependency, and add the two commands that reveal which release a module requires. apt-cache policy app-protect-dos, which the page currently suggests for choosing a version, does not show that dependency, so it cannot lead the reader to a working command.
  • Refresh the examples from 35+4.7.3 to the current 37+4.9.6.
  • Fix the indentation of the Ubuntu 22.04 and 24.04 code fences (they were indented one space further than the surrounding blocks).

Verification

The version strings are taken from apt-cache madison output on live VMs, not composed by hand:

suitenginx-plus 37.0.5module 37+4.9.6
bullseyeconfirmed presentconfirmed present
bookwormconfirmed presentconfirmed present
jammyr37.0 line confirmed (37.0.4 installed successfully)confirmed present
nobler37.0 line confirmed (37.0.4 installed successfully)confirmed present

markdownlint -c .markdownlint.yaml reports the same 34 pre-existing errors in this file before and after the change; no new ones are introduced, and none of the pre-existing ones are in the edited region.

Found while building an automated install-verification pipeline across Amazon Linux 2023, RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04, where every Debian and Ubuntu target failed to install until nginx-plus was pinned alongside the module.

@rnitzan
rnitzan requested a review from a team as a code ownerAugust 23, 2026 18:43
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation product/dos Issues related to F5 DoS for NGINX labels Aug 23, 2026
@github-actions

Copy link
Copy Markdown

Deploy Preview will be available once build job completes!

NameLink
😎 Deploy Previewhttps://frontdoor-test-docs.nginx.com/previews/docs/2226/

@rnitzan
rnitzan marked this pull request as draft August 24, 2026 09:26
@rnitzan
rnitzan marked this pull request as ready for review August 27, 2026 12:01
@rnitzanrnitzan changed the title fix: pin nginx-plus when installing a specific App Protect DoS versiondocs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notesAug 27, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates F5 DoS for NGINX documentation to reflect the current 4.9.6 and 4.10.0 releases, and to prevent Debian/Ubuntu installation failures by documenting and demonstrating correct version pinning for NGINX Plus and related packages.

Changes:

  • Add F5 DoS for NGINX 4.10 release notes and refresh 4.9 release notes to current package versions.
  • Expand the deployment guide with guidance/commands for identifying the required NGINX Plus release and pinning nginx-plus alongside app-protect-dos and the module on Debian/Ubuntu.
  • Update Kubernetes build guidance and Dockerfile include snippets to support pinning package versions (including eBPF Manager alignment guidance).

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
content/nap-dos/releases/about-4.9.mdUpdates 4.9 release notes wording and supported package table to current 4.9.6 / R37.0 details.
content/nap-dos/releases/about-4.10.mdAdds new 4.10 release notes page including R37.1 support and Debian/Ubuntu pinning notes.
content/nap-dos/deployment-guide/learn-about-deployment.mdAdds RPM/APK/APT examples for 4.9.6 and 4.10.0 and documents Debian/Ubuntu pinning + dependency inspection steps.
content/nap-dos/deployment-guide/kubernetes.mdAdds guidance and an example showing how to build images with pinned versions (where supported).
content/nap-dos/deployment-guide/kubernetes-with-L4-accelerated-mitigation.mdAdds guidance and examples for version-pinned builds and aligning the eBPF Manager image version.
content/includes/dos/dockerfiles/ubuntu-plus-dos.mdAdds build args and pins package installs for Ubuntu-based images.
content/includes/dos/dockerfiles/ubuntu-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.
content/includes/dos/dockerfiles/debian-plus-dos.mdAdds build args and pins package installs for Debian-based images.
content/includes/dos/dockerfiles/debian-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threadcontent/includes/dos/dockerfiles/debian-plus-dos.md Outdated
Comment threadcontent/includes/dos/dockerfiles/ubuntu-plus-dos.md Outdated
Comment threadcontent/nap-dos/deployment-guide/kubernetes.md Outdated
Comment threadcontent/nap-dos/deployment-guide/learn-about-deployment.md Outdated
@rnitzan

Copy link
Copy Markdown
ContributorAuthor

Thanks — C3, C4 and C5 were all correct and are fixed. On C1/C2 the conclusion is right but the stated cause is not, so I want to record the correction rather than let it stand.

C1/C2 — apt-get remove --purge --auto-remove -y does not fail the build.

apt has no minimum-argument guard on remove. InstallPackages in apt-private/private-install.cc short-circuits to success when nothing is queued:

if (Cache->DelCount() ==0&&Cache->InstCount() ==0&&Cache->BadCount() ==0)
returnRunScripts("APT::Install::Post-Invoke-Success");

It exits 0. The real defect is different: the build-time packages were installed explicitly, so apt marks them manually installed and --auto-remove can never reap them. Only orphaned automatically-installed dependencies were eligible, of which there are none here — so the line was a no-op, not a failure.

Fixed by naming the four packages, matching the form already used in content/includes/waf/dockerfiles/official-oss.md. ca-certificates and the archive keyrings are deliberately kept, since NGINX Plus needs the CA bundle at runtime for licensing and telemetry. All four purged packages are used earlier in the same RUN and nothing after the purge depends on them.

C3/C4 — build args only existed on Debian/Ubuntu. Correct, and the paragraph sat above every OS section. Rather than narrow the prose, DOS_VERSION is now on all twelve remaining Dockerfiles (NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8/9/10 and Rocky Linux 9), all defaulting to empty so current behaviour is unchanged. NGINX_PLUS_VERSION stays Debian/Ubuntu-only on purpose: dnf and apk can both select an older nginx-plus to satisfy the module's nginx-plus-r<release> dependency, so pinning it elsewhere would imply the apt limitation is universal. Version syntax differs by package manager, so each Dockerfile documents its own (=<version> for apt/apk, -<version> for dnf).

Worth noting: the Alpine Dockerfiles declare ARG OS_VERbeforeFROM, which scopes it to FROM alone. DOS_VERSION is therefore declared after FROM in every file so it stays visible to RUN.

C5 — suite mismatch. Correct. Replaced the hardcoded -1~bookworm with `lsb_release -cs`, the idiom already used by the repository setup steps earlier in that section, so one command is right on all four suites and its position no longer implies one.


Caveat for reviewers: I could not build these images, so the ${DOS_VERSION} concatenation and the apk/dnf version syntax rest on the deployment guide's own documented commands rather than a live build. One pinned build per package manager before merge would close that gap.

The Debian/Ubuntu instructions for installing a specific App Protect DoS version
pin `app-protect-dos` and `nginx-plus-module-appprotectdos` but leave `nginx-plus`
unpinned. That only works while the newest `nginx-plus` in the repository happens to
belong to the release the module was built for.
Each `nginx-plus-module-appprotectdos` package depends on a virtual package named
`nginx-plus-r<release>`, which only that release of `nginx-plus` provides. `apt`
considers only the newest `nginx-plus` available and does not select an older one to
satisfy another package's dependency, so as soon as the repository carries a newer
release the documented command fails:
nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.
This happens today. The repository offers `nginx-plus` 37.1.0, which provides
`nginx-plus-r37.1`, while the current DoS module `37+4.9.6` requires
`nginx-plus-r37.0`. A suitable `nginx-plus` 37.0.5 is present but is never
considered, so following the documentation as written cannot install it.
Add `nginx-plus` to each example, and document the `nginx-plus-r<release>`
dependency along with the commands that reveal which release a module requires.
`apt-cache policy app-protect-dos`, which the page currently suggests for choosing a
version, does not show that dependency, so it cannot lead the reader to a working
command.
Also refresh the examples from `35+4.7.3` to the current `37+4.9.6`, and correct the
indentation of the Ubuntu code fences.
The four examples pinned nginx-plus=37.0.5, which is a pre-release build. On the
repository customers actually use, app-protect-dos 37+4.9.6 pairs with nginx-plus
37.0.4, so the commands as written could not succeed. Verified on Debian 11/12/13 and
Ubuntu 22.04/24.04: an unpinned install from the production repository resolves to
nginx-plus 37.0.4 with nginx-plus-module-appprotectdos 37+4.9.6 and app-protect-dos
37+4.9.6.
Also scope the surrounding explanation. Pinning nginx-plus is needed when the App
Protect DoS version being installed was built for an earlier NGINX Plus release, which
is the situation this "install a specific version" section describes. It is not needed
for the plain `apt-get install app-protect-dos` shown above it, where every package
resolves to its newest version and the dependency on nginx-plus-r<release> is satisfied
without help. Saying so explicitly keeps the pin from being read as a general
requirement.
Version 4.10 adds NGINX Plus R37.1 support, which is the whole of its feature content
per the product changelog (BADOS-2046, 2026-08-26), plus accumulated bug fixes.
Follows the structure of the 4.9 notes, with weight 20 to keep the releases section in
descending order. Uses an "Important notes" section rather than a call-out shortcode,
because that heading is the established convention here and no other release note in
this section uses call-out.
Package strings verified per distribution against installs on Amazon Linux 2023,
RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04. The two Alpine rows are inferred
from the naming pattern the 4.8 and 4.9 notes use, since Alpine is not covered by that
test matrix; they should be checked against the published artifacts before release.
The important-notes section records the release coupling explicitly: every
nginx-plus-module-appprotectdos package depends on a virtual nginx-plus-r<release>
package that only its own NGINX Plus release provides, so 4.10 needs R37.1 where 4.9
needs R37.0, and the two must move together.
The "install a specific version" examples named a single version, and outside the
Debian/Ubuntu section that version was long stale: RHEL 8 and RHEL 9 still showed
35+4.7.3, Amazon Linux 2023 showed 34+4.6.0, and Alpine showed 33+4.5.0-r1.
Each section now shows the two current versions with the NGINX Plus release each one
requires, so a reader can see at a glance that the choice of App Protect DoS version
determines the NGINX Plus version:
37+4.10.0 -> NGINX Plus R37.1 (nginx-plus 37.1.0)
37+4.9.6 -> NGINX Plus R37.0 (nginx-plus 37.0.4)
The Debian/Ubuntu section also gains that mapping as a table, because it is the one
place where getting it wrong produces a confusing failure rather than a clear one:
apt considers only the newest nginx-plus and will not select an older one on its own.
The same nginx-plus-r<release> coupling exists on the RPM side - the metadata carries
nginx-plus-r37.0 and nginx-plus-r37.1 exactly as the Debian packages do - so the dnf
and apk examples name the required release in a comment. They do not add an explicit
nginx-plus version, because there is no evidence here that dnf or apk need one, and
prescribing a pin that has not been observed to be necessary would be a guess.
All package strings verified against installs on Amazon Linux 2023, RHEL 8/9/10,
Debian 11/12/13 and Ubuntu 22.04/24.04. The Alpine strings follow the naming pattern
recorded in the 4.8 and 4.9 release notes; Alpine is not covered by that matrix.
Tested under real apt: apt accepts version globs in pkg=version syntax — both
37.0.* and 37.1.* resolve correctly (rc=0), verified in a Debian bullseye container.
Changes:
- All 8 apt install commands: nginx-plus=37.0.4-1~ → nginx-plus=37.0.*-1~
and nginx-plus=37.1.0-1~ → nginx-plus=37.1.*-1~
- Compatibility table: exact patch versions → version ranges, column header updated
- about-4.9.md supported-packages table: "NGINX Plus R37" → "NGINX Plus R37.0",
since R37.0 and R37.1 are now separate lines and the ambiguity would mislead
The install-a-specific-version instructions pinned app-protect-dos,
nginx-plus-module-appprotectdos and nginx-plus, but left
app-protect-dos-ebpf-manager unpinned in all five OS sections. Because
that command sits above the specific-version subsection, following the
L4 accelerated mitigation path for 4.9.6 installed a 4.10.0 eBPF
manager next to it. Each section now shows how to list the available
eBPF manager versions and pin the matching one.
Also in this commit:
- Replace `apt-cache madison nginx-plus` with `apt-cache showpkg
nginx-plus-r<release>`. madison lists versions and origins only and
never shows the nginx-plus-r<release> virtual package, so it could
not answer the question the surrounding text asked of it.
- Quote the version arguments containing `*` so that the shell does not
expand them as filename patterns.
- Qualify the claim that the unpinned install needs no pin. That holds
only while the newest app-protect-dos is built for the newest NGINX
Plus release, which is not true between an NGINX Plus release and the
DoS release that supports it.
- Split the pinning paragraph and move the apt behaviour into a note.
- Drop a duplicated `sudo` and a stale `_r36` example image tag.
The Kubernetes guides render the Dockerfile includes, all of which
installed app-protect-dos and app-protect-dos-ebpf-manager unpinned.
There was no way to build an image for a specific version, and the
NGINX Plus and eBPF Manager images could drift apart because they are
built separately.
The Debian and Ubuntu Dockerfiles now accept DOS_VERSION and
NGINX_PLUS_VERSION build arguments. Both default to empty, so the
existing latest-version behaviour is unchanged. Both Kubernetes guides
explain version selection and show the matching --build-arg commands,
and the L4 guide calls out that the NGINX Plus and eBPF Manager images
must be built from the same version.
This also fixes two pre-existing faults in the same files:
- Neither Dockerfile ran `apt-get update` after adding the NGINX
repositories, so the install could not locate the packages.
- ubuntu-plus-dos.md had a dangling `RUN apt-get update && \` followed
by a comment and a second RUN, which is not valid Dockerfile syntax.
- Drop "includes bug fixes" from the 4.10 summary, since the New
features list no longer mentions them.
- Remove the leftover "(Trixie)" from the 4.9 feature list so that it
matches the supported packages table.
- Use "F5 DoS for NGINX" rather than "App Protect DoS".
- Note that the eBPF manager needs pinning as well.
- Align the supported packages tables.
The example pinned the Debian 12 suite (-1~bookworm) but sat directly
after the Ubuntu 24.04 command, so copying it on Ubuntu gave apt a
version that does not exist there.
Substitute the suite with `lsb_release -cs`, the idiom already used by
the repository setup steps earlier in this section. One command is now
correct on all four supported suites, and its position no longer
implies a particular one.
The previous commit added DOS_VERSION and NGINX_PLUS_VERSION to the
Debian and Ubuntu Dockerfiles only, but the surrounding paragraph sits
above every operating system section and read as though it applied to
all of them. A reader on Alpine, Amazon Linux, RHEL or Rocky Linux
would have passed --build-arg DOS_VERSION and had it ignored.
Add DOS_VERSION to the remaining twelve Dockerfiles, covering both the
NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8, 9
and 10, and Rocky Linux 9. All default to empty, so the existing
latest-version behaviour is unchanged.
Only Debian and Ubuntu take NGINX_PLUS_VERSION. dnf and apk can both
select an older nginx-plus to satisfy the module's nginx-plus-r<release>
dependency, so pinning it there would wrongly suggest the apt
limitation is universal. Each Dockerfile documents its own version
syntax, which differs by package manager: apt and apk use "=<version>",
dnf uses "-<version>".
The Alpine Dockerfiles declare ARG OS_VER before FROM, which scopes it
to the FROM instruction alone, so DOS_VERSION is declared after FROM
in every file to keep it visible to the RUN instruction.
`apt-get remove --purge --auto-remove -y` was called with no package
names. It does not fail, but it cannot do what it was written to do:
the build-time packages were installed explicitly, so apt marks them
manually installed and --auto-remove never reaps them. Only orphaned
automatically-installed dependencies were eligible, of which there are
none here, making the line a no-op.
Name the four build-time packages instead, matching the form already
used in content/includes/waf/dockerfiles/official-oss.md.
ca-certificates and the archive keyring packages are deliberately kept:
NGINX Plus needs the CA bundle at runtime for licensing and telemetry.
All four purged packages are used earlier in the same RUN instruction
and nothing after the purge depends on them.
@ohad-perets
ohad-peretsforce-pushed the dos/pin-nginx-plus-with-specific-version branch from e5a62ed to 17b62f5CompareSeptember 2, 2026 14:17
@rnitzan
rnitzan merged commit ac0bfd6 into mainSep 2, 2026
9 checks passed
@rnitzan
rnitzan deleted the dos/pin-nginx-plus-with-specific-version branch September 2, 2026 16:03
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationproduct/dosIssues related to F5 DoS for NGINX

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rnitzan@JTorreG@ohad-perets
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes - #2226

Merged
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version
Sep 2, 2026
Merged

docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes#2226
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version

Conversation

@rnitzan

Copy link
Copy Markdown
Contributor

The Debian/Ubuntu instructions for installing a specific App Protect DoS version pin app-protect-dos and nginx-plus-module-appprotectdos but leave nginx-plus unpinned. That only works while the newest nginx-plus in the repository happens to belong to the release the module was built for.

The problem

Each nginx-plus-module-appprotectdos package depends on a virtual package named nginx-plus-r<release>, which only that release of nginx-plus provides. apt considers only a package's candidate (newest) version and will not select an older one to satisfy another package's dependency. So as soon as the repository carries a newer nginx-plus release, the documented command fails:

nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.

This is the current state of the repository. Observed on all four supported Debian/Ubuntu suites:

Candidate: 37.1.0-1~bookworm <- the only version apt will consider
37.1.0 Provides: nginx-plus-r37.1
37.0.5 Provides: nginx-plus-r37.0 <- what the module needs; present, not considered
nginx-plus-module-appprotectdos 37+4.9.6 Depends: nginx-plus-r37.0

A perfectly good pair is sitting in the repository, but following the page as written cannot install it. The RPM instructions are not affected in the same way, and the plus RPM repository has not published r37.1 yet, so I have left those sections alone rather than change them speculatively.

Changes

  • Add nginx-plus=<version> to all four apt-get install examples.
  • Document the nginx-plus-r<release> dependency, and add the two commands that reveal which release a module requires. apt-cache policy app-protect-dos, which the page currently suggests for choosing a version, does not show that dependency, so it cannot lead the reader to a working command.
  • Refresh the examples from 35+4.7.3 to the current 37+4.9.6.
  • Fix the indentation of the Ubuntu 22.04 and 24.04 code fences (they were indented one space further than the surrounding blocks).

Verification

The version strings are taken from apt-cache madison output on live VMs, not composed by hand:

suitenginx-plus 37.0.5module 37+4.9.6
bullseyeconfirmed presentconfirmed present
bookwormconfirmed presentconfirmed present
jammyr37.0 line confirmed (37.0.4 installed successfully)confirmed present
nobler37.0 line confirmed (37.0.4 installed successfully)confirmed present

markdownlint -c .markdownlint.yaml reports the same 34 pre-existing errors in this file before and after the change; no new ones are introduced, and none of the pre-existing ones are in the edited region.

Found while building an automated install-verification pipeline across Amazon Linux 2023, RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04, where every Debian and Ubuntu target failed to install until nginx-plus was pinned alongside the module.

@rnitzan
rnitzan requested a review from a team as a code ownerAugust 23, 2026 18:43
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation product/dos Issues related to F5 DoS for NGINX labels Aug 23, 2026
@github-actions

Copy link
Copy Markdown

Deploy Preview will be available once build job completes!

NameLink
😎 Deploy Previewhttps://frontdoor-test-docs.nginx.com/previews/docs/2226/

@rnitzan
rnitzan marked this pull request as draft August 24, 2026 09:26
@rnitzan
rnitzan marked this pull request as ready for review August 27, 2026 12:01
@rnitzanrnitzan changed the title fix: pin nginx-plus when installing a specific App Protect DoS versiondocs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notesAug 27, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates F5 DoS for NGINX documentation to reflect the current 4.9.6 and 4.10.0 releases, and to prevent Debian/Ubuntu installation failures by documenting and demonstrating correct version pinning for NGINX Plus and related packages.

Changes:

  • Add F5 DoS for NGINX 4.10 release notes and refresh 4.9 release notes to current package versions.
  • Expand the deployment guide with guidance/commands for identifying the required NGINX Plus release and pinning nginx-plus alongside app-protect-dos and the module on Debian/Ubuntu.
  • Update Kubernetes build guidance and Dockerfile include snippets to support pinning package versions (including eBPF Manager alignment guidance).

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
content/nap-dos/releases/about-4.9.mdUpdates 4.9 release notes wording and supported package table to current 4.9.6 / R37.0 details.
content/nap-dos/releases/about-4.10.mdAdds new 4.10 release notes page including R37.1 support and Debian/Ubuntu pinning notes.
content/nap-dos/deployment-guide/learn-about-deployment.mdAdds RPM/APK/APT examples for 4.9.6 and 4.10.0 and documents Debian/Ubuntu pinning + dependency inspection steps.
content/nap-dos/deployment-guide/kubernetes.mdAdds guidance and an example showing how to build images with pinned versions (where supported).
content/nap-dos/deployment-guide/kubernetes-with-L4-accelerated-mitigation.mdAdds guidance and examples for version-pinned builds and aligning the eBPF Manager image version.
content/includes/dos/dockerfiles/ubuntu-plus-dos.mdAdds build args and pins package installs for Ubuntu-based images.
content/includes/dos/dockerfiles/ubuntu-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.
content/includes/dos/dockerfiles/debian-plus-dos.mdAdds build args and pins package installs for Debian-based images.
content/includes/dos/dockerfiles/debian-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threadcontent/includes/dos/dockerfiles/debian-plus-dos.md Outdated
Comment threadcontent/includes/dos/dockerfiles/ubuntu-plus-dos.md Outdated
Comment threadcontent/nap-dos/deployment-guide/kubernetes.md Outdated
Comment threadcontent/nap-dos/deployment-guide/learn-about-deployment.md Outdated
@rnitzan

Copy link
Copy Markdown
ContributorAuthor

Thanks — C3, C4 and C5 were all correct and are fixed. On C1/C2 the conclusion is right but the stated cause is not, so I want to record the correction rather than let it stand.

C1/C2 — apt-get remove --purge --auto-remove -y does not fail the build.

apt has no minimum-argument guard on remove. InstallPackages in apt-private/private-install.cc short-circuits to success when nothing is queued:

if (Cache->DelCount() ==0&&Cache->InstCount() ==0&&Cache->BadCount() ==0)
returnRunScripts("APT::Install::Post-Invoke-Success");

It exits 0. The real defect is different: the build-time packages were installed explicitly, so apt marks them manually installed and --auto-remove can never reap them. Only orphaned automatically-installed dependencies were eligible, of which there are none here — so the line was a no-op, not a failure.

Fixed by naming the four packages, matching the form already used in content/includes/waf/dockerfiles/official-oss.md. ca-certificates and the archive keyrings are deliberately kept, since NGINX Plus needs the CA bundle at runtime for licensing and telemetry. All four purged packages are used earlier in the same RUN and nothing after the purge depends on them.

C3/C4 — build args only existed on Debian/Ubuntu. Correct, and the paragraph sat above every OS section. Rather than narrow the prose, DOS_VERSION is now on all twelve remaining Dockerfiles (NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8/9/10 and Rocky Linux 9), all defaulting to empty so current behaviour is unchanged. NGINX_PLUS_VERSION stays Debian/Ubuntu-only on purpose: dnf and apk can both select an older nginx-plus to satisfy the module's nginx-plus-r<release> dependency, so pinning it elsewhere would imply the apt limitation is universal. Version syntax differs by package manager, so each Dockerfile documents its own (=<version> for apt/apk, -<version> for dnf).

Worth noting: the Alpine Dockerfiles declare ARG OS_VERbeforeFROM, which scopes it to FROM alone. DOS_VERSION is therefore declared after FROM in every file so it stays visible to RUN.

C5 — suite mismatch. Correct. Replaced the hardcoded -1~bookworm with `lsb_release -cs`, the idiom already used by the repository setup steps earlier in that section, so one command is right on all four suites and its position no longer implies one.


Caveat for reviewers: I could not build these images, so the ${DOS_VERSION} concatenation and the apk/dnf version syntax rest on the deployment guide's own documented commands rather than a live build. One pinned build per package manager before merge would close that gap.

The Debian/Ubuntu instructions for installing a specific App Protect DoS version
pin `app-protect-dos` and `nginx-plus-module-appprotectdos` but leave `nginx-plus`
unpinned. That only works while the newest `nginx-plus` in the repository happens to
belong to the release the module was built for.
Each `nginx-plus-module-appprotectdos` package depends on a virtual package named
`nginx-plus-r<release>`, which only that release of `nginx-plus` provides. `apt`
considers only the newest `nginx-plus` available and does not select an older one to
satisfy another package's dependency, so as soon as the repository carries a newer
release the documented command fails:
nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.
This happens today. The repository offers `nginx-plus` 37.1.0, which provides
`nginx-plus-r37.1`, while the current DoS module `37+4.9.6` requires
`nginx-plus-r37.0`. A suitable `nginx-plus` 37.0.5 is present but is never
considered, so following the documentation as written cannot install it.
Add `nginx-plus` to each example, and document the `nginx-plus-r<release>`
dependency along with the commands that reveal which release a module requires.
`apt-cache policy app-protect-dos`, which the page currently suggests for choosing a
version, does not show that dependency, so it cannot lead the reader to a working
command.
Also refresh the examples from `35+4.7.3` to the current `37+4.9.6`, and correct the
indentation of the Ubuntu code fences.
The four examples pinned nginx-plus=37.0.5, which is a pre-release build. On the
repository customers actually use, app-protect-dos 37+4.9.6 pairs with nginx-plus
37.0.4, so the commands as written could not succeed. Verified on Debian 11/12/13 and
Ubuntu 22.04/24.04: an unpinned install from the production repository resolves to
nginx-plus 37.0.4 with nginx-plus-module-appprotectdos 37+4.9.6 and app-protect-dos
37+4.9.6.
Also scope the surrounding explanation. Pinning nginx-plus is needed when the App
Protect DoS version being installed was built for an earlier NGINX Plus release, which
is the situation this "install a specific version" section describes. It is not needed
for the plain `apt-get install app-protect-dos` shown above it, where every package
resolves to its newest version and the dependency on nginx-plus-r<release> is satisfied
without help. Saying so explicitly keeps the pin from being read as a general
requirement.
Version 4.10 adds NGINX Plus R37.1 support, which is the whole of its feature content
per the product changelog (BADOS-2046, 2026-08-26), plus accumulated bug fixes.
Follows the structure of the 4.9 notes, with weight 20 to keep the releases section in
descending order. Uses an "Important notes" section rather than a call-out shortcode,
because that heading is the established convention here and no other release note in
this section uses call-out.
Package strings verified per distribution against installs on Amazon Linux 2023,
RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04. The two Alpine rows are inferred
from the naming pattern the 4.8 and 4.9 notes use, since Alpine is not covered by that
test matrix; they should be checked against the published artifacts before release.
The important-notes section records the release coupling explicitly: every
nginx-plus-module-appprotectdos package depends on a virtual nginx-plus-r<release>
package that only its own NGINX Plus release provides, so 4.10 needs R37.1 where 4.9
needs R37.0, and the two must move together.
The "install a specific version" examples named a single version, and outside the
Debian/Ubuntu section that version was long stale: RHEL 8 and RHEL 9 still showed
35+4.7.3, Amazon Linux 2023 showed 34+4.6.0, and Alpine showed 33+4.5.0-r1.
Each section now shows the two current versions with the NGINX Plus release each one
requires, so a reader can see at a glance that the choice of App Protect DoS version
determines the NGINX Plus version:
37+4.10.0 -> NGINX Plus R37.1 (nginx-plus 37.1.0)
37+4.9.6 -> NGINX Plus R37.0 (nginx-plus 37.0.4)
The Debian/Ubuntu section also gains that mapping as a table, because it is the one
place where getting it wrong produces a confusing failure rather than a clear one:
apt considers only the newest nginx-plus and will not select an older one on its own.
The same nginx-plus-r<release> coupling exists on the RPM side - the metadata carries
nginx-plus-r37.0 and nginx-plus-r37.1 exactly as the Debian packages do - so the dnf
and apk examples name the required release in a comment. They do not add an explicit
nginx-plus version, because there is no evidence here that dnf or apk need one, and
prescribing a pin that has not been observed to be necessary would be a guess.
All package strings verified against installs on Amazon Linux 2023, RHEL 8/9/10,
Debian 11/12/13 and Ubuntu 22.04/24.04. The Alpine strings follow the naming pattern
recorded in the 4.8 and 4.9 release notes; Alpine is not covered by that matrix.
Tested under real apt: apt accepts version globs in pkg=version syntax — both
37.0.* and 37.1.* resolve correctly (rc=0), verified in a Debian bullseye container.
Changes:
- All 8 apt install commands: nginx-plus=37.0.4-1~ → nginx-plus=37.0.*-1~
and nginx-plus=37.1.0-1~ → nginx-plus=37.1.*-1~
- Compatibility table: exact patch versions → version ranges, column header updated
- about-4.9.md supported-packages table: "NGINX Plus R37" → "NGINX Plus R37.0",
since R37.0 and R37.1 are now separate lines and the ambiguity would mislead
The install-a-specific-version instructions pinned app-protect-dos,
nginx-plus-module-appprotectdos and nginx-plus, but left
app-protect-dos-ebpf-manager unpinned in all five OS sections. Because
that command sits above the specific-version subsection, following the
L4 accelerated mitigation path for 4.9.6 installed a 4.10.0 eBPF
manager next to it. Each section now shows how to list the available
eBPF manager versions and pin the matching one.
Also in this commit:
- Replace `apt-cache madison nginx-plus` with `apt-cache showpkg
nginx-plus-r<release>`. madison lists versions and origins only and
never shows the nginx-plus-r<release> virtual package, so it could
not answer the question the surrounding text asked of it.
- Quote the version arguments containing `*` so that the shell does not
expand them as filename patterns.
- Qualify the claim that the unpinned install needs no pin. That holds
only while the newest app-protect-dos is built for the newest NGINX
Plus release, which is not true between an NGINX Plus release and the
DoS release that supports it.
- Split the pinning paragraph and move the apt behaviour into a note.
- Drop a duplicated `sudo` and a stale `_r36` example image tag.
The Kubernetes guides render the Dockerfile includes, all of which
installed app-protect-dos and app-protect-dos-ebpf-manager unpinned.
There was no way to build an image for a specific version, and the
NGINX Plus and eBPF Manager images could drift apart because they are
built separately.
The Debian and Ubuntu Dockerfiles now accept DOS_VERSION and
NGINX_PLUS_VERSION build arguments. Both default to empty, so the
existing latest-version behaviour is unchanged. Both Kubernetes guides
explain version selection and show the matching --build-arg commands,
and the L4 guide calls out that the NGINX Plus and eBPF Manager images
must be built from the same version.
This also fixes two pre-existing faults in the same files:
- Neither Dockerfile ran `apt-get update` after adding the NGINX
repositories, so the install could not locate the packages.
- ubuntu-plus-dos.md had a dangling `RUN apt-get update && \` followed
by a comment and a second RUN, which is not valid Dockerfile syntax.
- Drop "includes bug fixes" from the 4.10 summary, since the New
features list no longer mentions them.
- Remove the leftover "(Trixie)" from the 4.9 feature list so that it
matches the supported packages table.
- Use "F5 DoS for NGINX" rather than "App Protect DoS".
- Note that the eBPF manager needs pinning as well.
- Align the supported packages tables.
The example pinned the Debian 12 suite (-1~bookworm) but sat directly
after the Ubuntu 24.04 command, so copying it on Ubuntu gave apt a
version that does not exist there.
Substitute the suite with `lsb_release -cs`, the idiom already used by
the repository setup steps earlier in this section. One command is now
correct on all four supported suites, and its position no longer
implies a particular one.
The previous commit added DOS_VERSION and NGINX_PLUS_VERSION to the
Debian and Ubuntu Dockerfiles only, but the surrounding paragraph sits
above every operating system section and read as though it applied to
all of them. A reader on Alpine, Amazon Linux, RHEL or Rocky Linux
would have passed --build-arg DOS_VERSION and had it ignored.
Add DOS_VERSION to the remaining twelve Dockerfiles, covering both the
NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8, 9
and 10, and Rocky Linux 9. All default to empty, so the existing
latest-version behaviour is unchanged.
Only Debian and Ubuntu take NGINX_PLUS_VERSION. dnf and apk can both
select an older nginx-plus to satisfy the module's nginx-plus-r<release>
dependency, so pinning it there would wrongly suggest the apt
limitation is universal. Each Dockerfile documents its own version
syntax, which differs by package manager: apt and apk use "=<version>",
dnf uses "-<version>".
The Alpine Dockerfiles declare ARG OS_VER before FROM, which scopes it
to the FROM instruction alone, so DOS_VERSION is declared after FROM
in every file to keep it visible to the RUN instruction.
`apt-get remove --purge --auto-remove -y` was called with no package
names. It does not fail, but it cannot do what it was written to do:
the build-time packages were installed explicitly, so apt marks them
manually installed and --auto-remove never reaps them. Only orphaned
automatically-installed dependencies were eligible, of which there are
none here, making the line a no-op.
Name the four build-time packages instead, matching the form already
used in content/includes/waf/dockerfiles/official-oss.md.
ca-certificates and the archive keyring packages are deliberately kept:
NGINX Plus needs the CA bundle at runtime for licensing and telemetry.
All four purged packages are used earlier in the same RUN instruction
and nothing after the purge depends on them.
@ohad-perets
ohad-peretsforce-pushed the dos/pin-nginx-plus-with-specific-version branch from e5a62ed to 17b62f5CompareSeptember 2, 2026 14:17
@rnitzan
rnitzan merged commit ac0bfd6 into mainSep 2, 2026
9 checks passed
@rnitzan
rnitzan deleted the dos/pin-nginx-plus-with-specific-version branch September 2, 2026 16:03
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationproduct/dosIssues related to F5 DoS for NGINX

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rnitzan@JTorreG@ohad-perets
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes - #2226

Merged
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version
Sep 2, 2026
Merged

docs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notes#2226
rnitzan merged 14 commits into
mainfrom
dos/pin-nginx-plus-with-specific-version

Conversation

@rnitzan

Copy link
Copy Markdown
Contributor

The Debian/Ubuntu instructions for installing a specific App Protect DoS version pin app-protect-dos and nginx-plus-module-appprotectdos but leave nginx-plus unpinned. That only works while the newest nginx-plus in the repository happens to belong to the release the module was built for.

The problem

Each nginx-plus-module-appprotectdos package depends on a virtual package named nginx-plus-r<release>, which only that release of nginx-plus provides. apt considers only a package's candidate (newest) version and will not select an older one to satisfy another package's dependency. So as soon as the repository carries a newer nginx-plus release, the documented command fails:

nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.

This is the current state of the repository. Observed on all four supported Debian/Ubuntu suites:

Candidate: 37.1.0-1~bookworm <- the only version apt will consider
37.1.0 Provides: nginx-plus-r37.1
37.0.5 Provides: nginx-plus-r37.0 <- what the module needs; present, not considered
nginx-plus-module-appprotectdos 37+4.9.6 Depends: nginx-plus-r37.0

A perfectly good pair is sitting in the repository, but following the page as written cannot install it. The RPM instructions are not affected in the same way, and the plus RPM repository has not published r37.1 yet, so I have left those sections alone rather than change them speculatively.

Changes

  • Add nginx-plus=<version> to all four apt-get install examples.
  • Document the nginx-plus-r<release> dependency, and add the two commands that reveal which release a module requires. apt-cache policy app-protect-dos, which the page currently suggests for choosing a version, does not show that dependency, so it cannot lead the reader to a working command.
  • Refresh the examples from 35+4.7.3 to the current 37+4.9.6.
  • Fix the indentation of the Ubuntu 22.04 and 24.04 code fences (they were indented one space further than the surrounding blocks).

Verification

The version strings are taken from apt-cache madison output on live VMs, not composed by hand:

suitenginx-plus 37.0.5module 37+4.9.6
bullseyeconfirmed presentconfirmed present
bookwormconfirmed presentconfirmed present
jammyr37.0 line confirmed (37.0.4 installed successfully)confirmed present
nobler37.0 line confirmed (37.0.4 installed successfully)confirmed present

markdownlint -c .markdownlint.yaml reports the same 34 pre-existing errors in this file before and after the change; no new ones are introduced, and none of the pre-existing ones are in the edited region.

Found while building an automated install-verification pipeline across Amazon Linux 2023, RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04, where every Debian and Ubuntu target failed to install until nginx-plus was pinned alongside the module.

@rnitzan
rnitzan requested a review from a team as a code ownerAugust 23, 2026 18:43
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation product/dos Issues related to F5 DoS for NGINX labels Aug 23, 2026
@github-actions

Copy link
Copy Markdown

Deploy Preview will be available once build job completes!

NameLink
😎 Deploy Previewhttps://frontdoor-test-docs.nginx.com/previews/docs/2226/

@rnitzan
rnitzan marked this pull request as draft August 24, 2026 09:26
@rnitzan
rnitzan marked this pull request as ready for review August 27, 2026 12:01
@rnitzanrnitzan changed the title fix: pin nginx-plus when installing a specific App Protect DoS versiondocs: cover App Protect DoS 4.9.6 and 4.10.0, and add 4.10 release notesAug 27, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates F5 DoS for NGINX documentation to reflect the current 4.9.6 and 4.10.0 releases, and to prevent Debian/Ubuntu installation failures by documenting and demonstrating correct version pinning for NGINX Plus and related packages.

Changes:

  • Add F5 DoS for NGINX 4.10 release notes and refresh 4.9 release notes to current package versions.
  • Expand the deployment guide with guidance/commands for identifying the required NGINX Plus release and pinning nginx-plus alongside app-protect-dos and the module on Debian/Ubuntu.
  • Update Kubernetes build guidance and Dockerfile include snippets to support pinning package versions (including eBPF Manager alignment guidance).

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
content/nap-dos/releases/about-4.9.mdUpdates 4.9 release notes wording and supported package table to current 4.9.6 / R37.0 details.
content/nap-dos/releases/about-4.10.mdAdds new 4.10 release notes page including R37.1 support and Debian/Ubuntu pinning notes.
content/nap-dos/deployment-guide/learn-about-deployment.mdAdds RPM/APK/APT examples for 4.9.6 and 4.10.0 and documents Debian/Ubuntu pinning + dependency inspection steps.
content/nap-dos/deployment-guide/kubernetes.mdAdds guidance and an example showing how to build images with pinned versions (where supported).
content/nap-dos/deployment-guide/kubernetes-with-L4-accelerated-mitigation.mdAdds guidance and examples for version-pinned builds and aligning the eBPF Manager image version.
content/includes/dos/dockerfiles/ubuntu-plus-dos.mdAdds build args and pins package installs for Ubuntu-based images.
content/includes/dos/dockerfiles/ubuntu-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.
content/includes/dos/dockerfiles/debian-plus-dos.mdAdds build args and pins package installs for Debian-based images.
content/includes/dos/dockerfiles/debian-ebpf-manager.mdAdds build arg to pin eBPF Manager package version.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threadcontent/includes/dos/dockerfiles/debian-plus-dos.md Outdated
Comment threadcontent/includes/dos/dockerfiles/ubuntu-plus-dos.md Outdated
Comment threadcontent/nap-dos/deployment-guide/kubernetes.md Outdated
Comment threadcontent/nap-dos/deployment-guide/learn-about-deployment.md Outdated
@rnitzan

Copy link
Copy Markdown
ContributorAuthor

Thanks — C3, C4 and C5 were all correct and are fixed. On C1/C2 the conclusion is right but the stated cause is not, so I want to record the correction rather than let it stand.

C1/C2 — apt-get remove --purge --auto-remove -y does not fail the build.

apt has no minimum-argument guard on remove. InstallPackages in apt-private/private-install.cc short-circuits to success when nothing is queued:

if (Cache->DelCount() ==0&&Cache->InstCount() ==0&&Cache->BadCount() ==0)
returnRunScripts("APT::Install::Post-Invoke-Success");

It exits 0. The real defect is different: the build-time packages were installed explicitly, so apt marks them manually installed and --auto-remove can never reap them. Only orphaned automatically-installed dependencies were eligible, of which there are none here — so the line was a no-op, not a failure.

Fixed by naming the four packages, matching the form already used in content/includes/waf/dockerfiles/official-oss.md. ca-certificates and the archive keyrings are deliberately kept, since NGINX Plus needs the CA bundle at runtime for licensing and telemetry. All four purged packages are used earlier in the same RUN and nothing after the purge depends on them.

C3/C4 — build args only existed on Debian/Ubuntu. Correct, and the paragraph sat above every OS section. Rather than narrow the prose, DOS_VERSION is now on all twelve remaining Dockerfiles (NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8/9/10 and Rocky Linux 9), all defaulting to empty so current behaviour is unchanged. NGINX_PLUS_VERSION stays Debian/Ubuntu-only on purpose: dnf and apk can both select an older nginx-plus to satisfy the module's nginx-plus-r<release> dependency, so pinning it elsewhere would imply the apt limitation is universal. Version syntax differs by package manager, so each Dockerfile documents its own (=<version> for apt/apk, -<version> for dnf).

Worth noting: the Alpine Dockerfiles declare ARG OS_VERbeforeFROM, which scopes it to FROM alone. DOS_VERSION is therefore declared after FROM in every file so it stays visible to RUN.

C5 — suite mismatch. Correct. Replaced the hardcoded -1~bookworm with `lsb_release -cs`, the idiom already used by the repository setup steps earlier in that section, so one command is right on all four suites and its position no longer implies one.


Caveat for reviewers: I could not build these images, so the ${DOS_VERSION} concatenation and the apk/dnf version syntax rest on the deployment guide's own documented commands rather than a live build. One pinned build per package manager before merge would close that gap.

The Debian/Ubuntu instructions for installing a specific App Protect DoS version
pin `app-protect-dos` and `nginx-plus-module-appprotectdos` but leave `nginx-plus`
unpinned. That only works while the newest `nginx-plus` in the repository happens to
belong to the release the module was built for.
Each `nginx-plus-module-appprotectdos` package depends on a virtual package named
`nginx-plus-r<release>`, which only that release of `nginx-plus` provides. `apt`
considers only the newest `nginx-plus` available and does not select an older one to
satisfy another package's dependency, so as soon as the repository carries a newer
release the documented command fails:
nginx-plus-module-appprotectdos : Depends: nginx-plus-r37.0
E: Unable to correct problems, you have held broken packages.
This happens today. The repository offers `nginx-plus` 37.1.0, which provides
`nginx-plus-r37.1`, while the current DoS module `37+4.9.6` requires
`nginx-plus-r37.0`. A suitable `nginx-plus` 37.0.5 is present but is never
considered, so following the documentation as written cannot install it.
Add `nginx-plus` to each example, and document the `nginx-plus-r<release>`
dependency along with the commands that reveal which release a module requires.
`apt-cache policy app-protect-dos`, which the page currently suggests for choosing a
version, does not show that dependency, so it cannot lead the reader to a working
command.
Also refresh the examples from `35+4.7.3` to the current `37+4.9.6`, and correct the
indentation of the Ubuntu code fences.
The four examples pinned nginx-plus=37.0.5, which is a pre-release build. On the
repository customers actually use, app-protect-dos 37+4.9.6 pairs with nginx-plus
37.0.4, so the commands as written could not succeed. Verified on Debian 11/12/13 and
Ubuntu 22.04/24.04: an unpinned install from the production repository resolves to
nginx-plus 37.0.4 with nginx-plus-module-appprotectdos 37+4.9.6 and app-protect-dos
37+4.9.6.
Also scope the surrounding explanation. Pinning nginx-plus is needed when the App
Protect DoS version being installed was built for an earlier NGINX Plus release, which
is the situation this "install a specific version" section describes. It is not needed
for the plain `apt-get install app-protect-dos` shown above it, where every package
resolves to its newest version and the dependency on nginx-plus-r<release> is satisfied
without help. Saying so explicitly keeps the pin from being read as a general
requirement.
Version 4.10 adds NGINX Plus R37.1 support, which is the whole of its feature content
per the product changelog (BADOS-2046, 2026-08-26), plus accumulated bug fixes.
Follows the structure of the 4.9 notes, with weight 20 to keep the releases section in
descending order. Uses an "Important notes" section rather than a call-out shortcode,
because that heading is the established convention here and no other release note in
this section uses call-out.
Package strings verified per distribution against installs on Amazon Linux 2023,
RHEL 8/9/10, Debian 11/12/13 and Ubuntu 22.04/24.04. The two Alpine rows are inferred
from the naming pattern the 4.8 and 4.9 notes use, since Alpine is not covered by that
test matrix; they should be checked against the published artifacts before release.
The important-notes section records the release coupling explicitly: every
nginx-plus-module-appprotectdos package depends on a virtual nginx-plus-r<release>
package that only its own NGINX Plus release provides, so 4.10 needs R37.1 where 4.9
needs R37.0, and the two must move together.
The "install a specific version" examples named a single version, and outside the
Debian/Ubuntu section that version was long stale: RHEL 8 and RHEL 9 still showed
35+4.7.3, Amazon Linux 2023 showed 34+4.6.0, and Alpine showed 33+4.5.0-r1.
Each section now shows the two current versions with the NGINX Plus release each one
requires, so a reader can see at a glance that the choice of App Protect DoS version
determines the NGINX Plus version:
37+4.10.0 -> NGINX Plus R37.1 (nginx-plus 37.1.0)
37+4.9.6 -> NGINX Plus R37.0 (nginx-plus 37.0.4)
The Debian/Ubuntu section also gains that mapping as a table, because it is the one
place where getting it wrong produces a confusing failure rather than a clear one:
apt considers only the newest nginx-plus and will not select an older one on its own.
The same nginx-plus-r<release> coupling exists on the RPM side - the metadata carries
nginx-plus-r37.0 and nginx-plus-r37.1 exactly as the Debian packages do - so the dnf
and apk examples name the required release in a comment. They do not add an explicit
nginx-plus version, because there is no evidence here that dnf or apk need one, and
prescribing a pin that has not been observed to be necessary would be a guess.
All package strings verified against installs on Amazon Linux 2023, RHEL 8/9/10,
Debian 11/12/13 and Ubuntu 22.04/24.04. The Alpine strings follow the naming pattern
recorded in the 4.8 and 4.9 release notes; Alpine is not covered by that matrix.
Tested under real apt: apt accepts version globs in pkg=version syntax — both
37.0.* and 37.1.* resolve correctly (rc=0), verified in a Debian bullseye container.
Changes:
- All 8 apt install commands: nginx-plus=37.0.4-1~ → nginx-plus=37.0.*-1~
and nginx-plus=37.1.0-1~ → nginx-plus=37.1.*-1~
- Compatibility table: exact patch versions → version ranges, column header updated
- about-4.9.md supported-packages table: "NGINX Plus R37" → "NGINX Plus R37.0",
since R37.0 and R37.1 are now separate lines and the ambiguity would mislead
The install-a-specific-version instructions pinned app-protect-dos,
nginx-plus-module-appprotectdos and nginx-plus, but left
app-protect-dos-ebpf-manager unpinned in all five OS sections. Because
that command sits above the specific-version subsection, following the
L4 accelerated mitigation path for 4.9.6 installed a 4.10.0 eBPF
manager next to it. Each section now shows how to list the available
eBPF manager versions and pin the matching one.
Also in this commit:
- Replace `apt-cache madison nginx-plus` with `apt-cache showpkg
nginx-plus-r<release>`. madison lists versions and origins only and
never shows the nginx-plus-r<release> virtual package, so it could
not answer the question the surrounding text asked of it.
- Quote the version arguments containing `*` so that the shell does not
expand them as filename patterns.
- Qualify the claim that the unpinned install needs no pin. That holds
only while the newest app-protect-dos is built for the newest NGINX
Plus release, which is not true between an NGINX Plus release and the
DoS release that supports it.
- Split the pinning paragraph and move the apt behaviour into a note.
- Drop a duplicated `sudo` and a stale `_r36` example image tag.
The Kubernetes guides render the Dockerfile includes, all of which
installed app-protect-dos and app-protect-dos-ebpf-manager unpinned.
There was no way to build an image for a specific version, and the
NGINX Plus and eBPF Manager images could drift apart because they are
built separately.
The Debian and Ubuntu Dockerfiles now accept DOS_VERSION and
NGINX_PLUS_VERSION build arguments. Both default to empty, so the
existing latest-version behaviour is unchanged. Both Kubernetes guides
explain version selection and show the matching --build-arg commands,
and the L4 guide calls out that the NGINX Plus and eBPF Manager images
must be built from the same version.
This also fixes two pre-existing faults in the same files:
- Neither Dockerfile ran `apt-get update` after adding the NGINX
repositories, so the install could not locate the packages.
- ubuntu-plus-dos.md had a dangling `RUN apt-get update && \` followed
by a comment and a second RUN, which is not valid Dockerfile syntax.
- Drop "includes bug fixes" from the 4.10 summary, since the New
features list no longer mentions them.
- Remove the leftover "(Trixie)" from the 4.9 feature list so that it
matches the supported packages table.
- Use "F5 DoS for NGINX" rather than "App Protect DoS".
- Note that the eBPF manager needs pinning as well.
- Align the supported packages tables.
The example pinned the Debian 12 suite (-1~bookworm) but sat directly
after the Ubuntu 24.04 command, so copying it on Ubuntu gave apt a
version that does not exist there.
Substitute the suite with `lsb_release -cs`, the idiom already used by
the repository setup steps earlier in this section. One command is now
correct on all four supported suites, and its position no longer
implies a particular one.
The previous commit added DOS_VERSION and NGINX_PLUS_VERSION to the
Debian and Ubuntu Dockerfiles only, but the surrounding paragraph sits
above every operating system section and read as though it applied to
all of them. A reader on Alpine, Amazon Linux, RHEL or Rocky Linux
would have passed --build-arg DOS_VERSION and had it ignored.
Add DOS_VERSION to the remaining twelve Dockerfiles, covering both the
NGINX Plus and eBPF Manager images for Alpine, Amazon Linux, RHEL 8, 9
and 10, and Rocky Linux 9. All default to empty, so the existing
latest-version behaviour is unchanged.
Only Debian and Ubuntu take NGINX_PLUS_VERSION. dnf and apk can both
select an older nginx-plus to satisfy the module's nginx-plus-r<release>
dependency, so pinning it there would wrongly suggest the apt
limitation is universal. Each Dockerfile documents its own version
syntax, which differs by package manager: apt and apk use "=<version>",
dnf uses "-<version>".
The Alpine Dockerfiles declare ARG OS_VER before FROM, which scopes it
to the FROM instruction alone, so DOS_VERSION is declared after FROM
in every file to keep it visible to the RUN instruction.
`apt-get remove --purge --auto-remove -y` was called with no package
names. It does not fail, but it cannot do what it was written to do:
the build-time packages were installed explicitly, so apt marks them
manually installed and --auto-remove never reaps them. Only orphaned
automatically-installed dependencies were eligible, of which there are
none here, making the line a no-op.
Name the four build-time packages instead, matching the form already
used in content/includes/waf/dockerfiles/official-oss.md.
ca-certificates and the archive keyring packages are deliberately kept:
NGINX Plus needs the CA bundle at runtime for licensing and telemetry.
All four purged packages are used earlier in the same RUN instruction
and nothing after the purge depends on them.
@ohad-perets
ohad-peretsforce-pushed the dos/pin-nginx-plus-with-specific-version branch from e5a62ed to 17b62f5CompareSeptember 2, 2026 14:17
@rnitzan
rnitzan merged commit ac0bfd6 into mainSep 2, 2026
9 checks passed
@rnitzan
rnitzan deleted the dos/pin-nginx-plus-with-specific-version branch September 2, 2026 16:03
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationproduct/dosIssues related to F5 DoS for NGINX

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@rnitzan@JTorreG@ohad-perets