Repository navigation
Add APACHE_MAX_REQUEST_WORKERS to simplerisk-minimal - #178
Merged
Merged
Conversation
…che workers Every request holds a database connection, and Apache's default of 150 workers can exceed a small database's max_connections (a db.t3.micro allows about 60); several containers sharing one database multiply it. The right number depends on the database and replica count, which the image cannot know, so the entrypoint accepts APACHE_MAX_REQUEST_WORKERS (1-2000) and writes it to conf-enabled, raising ServerLimit when it exceeds 256. Unset keeps Apache's default; invalid values are ignored with a warning. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- A digit string longer than 19 digits wrapped around in bash arithmetic and
could be accepted (18446744073709553616 evaluated to 2000): strip leading
zeros and bound the length before any arithmetic
- A failed write (for example a read-only root filesystem) still logged
"Apache will serve at most N requests at once", because a failed redirect on
a { } group does not report failure: write with a single printf, log an
error and keep the default instead
- Test: boundaries (1, 256, 257, 2000), the exact file, adversarial and
overflow input, the unwritable path, and the call placement in the
entrypoint; loosen the awk extraction pattern
- Docs: prefork-only, memory, session connection and cron caveats
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
jsokol
marked this pull request as ready for review
October 27, 2026 18:19
jsokol
enabled auto-merge
October 27, 2026 18:19
jsokol
disabled auto-merge
October 27, 2026 18:19
8 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds an optional
APACHE_MAX_REQUEST_WORKERSenvironment variable tosimplerisk-minimal. The entrypoint validates it (whole number, 1-2000) and writes it to/etc/apache2/conf-enabled/simplerisk-mpm.conf, raisingServerLimittoo when the value is above 256 (Apache silently lowersMaxRequestWorkerstoServerLimitotherwise). Unset keeps Apache's default of 150; an invalid value is ignored with a warning; unsetting it on a restarted container removes the file again.Why: every SimpleRisk request holds a database connection. On 2026-10-26/27 two hosted instances on
db.t3.microdatabases (max_connections = 60) ran out of connections (SQLSTATE[HY000] [1040] Too many connections), and Apache's 150-worker default makes that easy to hit. This image runs in many environments (EKS with 2 replicas per tenant against a shared database, dedicated ECS tasks withdb.t4g.microdefaults, self-hosted), so there is no single right number; the operator sets it per deployment. A rule of thumb is in the README:replicas x APACHE_MAX_REQUEST_WORKERSunder the database'smax_connections, with room for cron and the second connection used for DB-backed sessions.Companion to code-development #2551, which adds a connection retry and an optional per-host cap in the application itself. Only the minimal image changes here; the full-stack image runs its own local MySQL.
What changed
simplerisk-minimal/common/entrypoint.sh: newset_apache_workers, called on every start before cron setup (independent of the database variables).simplerisk-minimal/README.mdandCLAUDE.md: document the variable.simplerisk-minimal/test_set_apache_workers.shplus a step incontainer-validation.yml: 48 checks (boundaries 1/256/257/2000, the exact generated file, leading zeros, removal on unset, rejection of non-numeric, zero, over-limit, decimal, negative, whitespace, shell-metacharacter, unicode and 64-bit-overflow values, the unwritable-path case, and that the entrypoint calls the function beforeexec).Testing
test_set_apache_workers.sh: all checks pass; ShellCheck (-S error) clean on both scripts.php:8.3-apacheimage: the generated file is included aftermods-enabled/mpm_prefork.conf,apachectl -treportsSyntax OK, and starting Apache with 300 workers and noServerLimitlogsAH00180/AH00181 ... decreasing MaxRequestWorkers to 256, while the file this change writes (withServerLimit 300) starts with no warning.docker build --build-arg php_version=8.3, ~3.5 min, builds cleanly). Verified in running containers:APACHE_MAX_REQUEST_WORKERS=40together withDB_SETUP=automaticagainst a real MySQL 8.0: the entrypoint logsApache will serve at most 40 requests at once., writes/etc/apache2/conf-enabled/simplerisk-mpm.conf, the database is set up and the app answers (HTTP 302 from the login redirect).APACHE_MAX_REQUEST_WORKERS=abc: logs the warning, writes no file, Apache starts with its default.APACHE_MAX_REQUEST_WORKERS=8they took 32.0 s (five waves of 8), peaked at 9 Apache processes (parent + 8 children), all returned 200, and Apache loggedserver reached MaxRequestWorkers. With the variable unset they took 11.8 s, peaked at 69 processes, and logged no saturation.container-validationmatrix (all five variants, Dockle, Grype); it runs on this PR. Only the php 8.3 variant was built here.test_set_apache_workers.sh, not in a running container). Note that restarting a container started withDB_SETUP=automaticre-runs the database setup and exits, which is existing behaviour unrelated to this change.Review
Reviewed for security and for correctness/tests (two reviewers, read-only, running the function with adversarial input). No must-fix findings. Two real bugs were found and fixed in the second commit:
APACHE_MAX_REQUEST_WORKERS=18446744073709553616evaluated to 2000 and was accepted. Leading zeros are now stripped and the length bounded before any arithmetic.Apache will serve at most N requests at once, because a failed redirect on a{ }group does not report failure. The file is now written with a singleprintf; a failure logs an error and keeps Apache's default.Both are now covered by the test script, and the old code fails those new checks. After the fix the image was rebuilt and re-run: the cap still holds (40 parallel 6 s requests with a cap of 8: 31 s, peak 9 Apache processes), the overflow value is rejected with a warning, and an unwritable path logs an error while the container still starts.
Left as is: the
APACHE_MPM_CONF_PATHtest override is honoured in production (the operator already controls the container environment and the process is not root), and 2000 is a sanity bound rather than a memory-safe one, which the README now says.Targets
testingper the usual flow. It does not touch the Dockerfile, so it does not triggerpromote-latest.🤖 Generated with Claude Code