Description of the Change
Problem
Flow workers have a default PID limit, but operators cannot configure CPU bandwidth or RAM ceilings through the environment. Compose resource limits on the pentagi service do not constrain the separate containers created for flows. Caller-supplied HostConfig limits are possible today, but there is no operator ceiling or check before an existing worker is reused.
Solution
Add two optional settings, both defaulting to 0 (disabled):
DOCKER_WORKER_CPUS: fractional CPUs, minimum0.01when enabled.DOCKER_WORKER_MEMORY: bytes, minimum6291456(6 MiB) when enabled; also disables swap by settingMemorySwap = Memory.
Apply the same ceilings before creating flow workers and the startup sandbox probe. Retain stricter caller limits, normalize legacy CPU quota/period settings without conflicting with NanoCPUs, and use exact integer quota arithmetic. Validate configuration and required daemon controllers. Before reusing a running worker, reject missing or excessive limits with an explicit recreation message, preserving its data.
Wire the settings through Compose and document units, swap semantics, daemon requirements, migration and scope. Preserve existing zero-limit behavior, the PID default, capabilities, images and networking.
Closes #484
Type of Change
- 🐛 Bug fix (non-breaking change which fixes an issue)
- 🚀 New feature (non-breaking change which adds functionality)
- 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
- 📚 Documentation update
- 🔧 Configuration change
- 🧪 Test update
- 🛡️ Security update
Areas Affected
- Core Services (Frontend UI/Backend API)
- AI Agents (Researcher/Developer/Executor)
- Security Tools Integration
- Memory System (Vector Store/Knowledge Base)
- Monitoring Stack (Grafana/OpenTelemetry)
- Analytics Platform (Langfuse)
- External Integrations (LLM/Search APIs)
- Documentation
- Infrastructure/DevOps
Testing and Verification
Test Configuration
PentAGI Version: upstream main cffed9485aa1cca70762cd08a57ac62a80406258 + this change
Docker Version: 29.3.0 (rootful and rootless, cgroup v2)
Host OS: Ubuntu in WSL2
LLM Provider: none required
Enabled Features: [worker container lifecycle, startup sandbox probe fixtures]
Go Version: 1.26.10Test Steps
- Generate the installer's embedded descriptors:
cd backend/cmd/installer/files && go run generate.go. - From
backend/, rungo test -race ./...,go vet ./pkg/config ./pkg/docker,golangci-lint run(v2.12.2),go build ./cmd/pentagi,go mod tidyandgo mod verify. - Run the new deterministic HTTP-daemon tests covering validation, missing controllers, nil/custom host configurations, stricter limits, legacy and combined CPU representations, fractional quotas, disabled ceilings, startup probe creation and reuse checks. They are included in step 2 and require no real Docker daemon.
- For a manual integration check on a disposable Linux worker daemon, create an Alpine worker through
NewDockerClient/WorkerSpec/RunContainerwithDOCKER_WORKER_CPUS=0.5andDOCKER_WORKER_MEMORY=134217728. Inspect its limits, read cgroup files inside it, then raise its CPU limit to 1 using the operator's Docker client and confirmIsContainerRunningrefuses reuse. Remove the temporary worker and its volume.
Test Results
- Full backend suite with the race detector: passed. Tests requiring unavailable external integration services retain their normal skip behavior.
- Targeted
go vet, backend build and golangci-lint v2.12.2: passed (0 issues). - Installer descriptors regenerated locally; generated files remain ignored as intended.
go mod tidyleaves module files unchanged;go mod verifypasses. - New tests were first reproduced against missing enforcement; regression cases also cover the separate startup probe path and fractional legacy quotas.
- Manual integration on both rootless and rootful Docker passed:
cpu.max = 50000 100000,memory.max = 134217728,memory.swap.max = 0; new bounded worker accepted and deliberately raised CPU budget refused on reuse. Temporary resources removed. - No TypeScript, GraphQL, dependency or LLM behavior changes. Frontend format, lint, type checking and tests have also passed in the fork workflow linked below. Full application E2E has not run.
Fork CI: lint, frontend/backend tests and cross-platform builds. The upstream PR workflows require maintainer approval for this contribution.
Security Considerations
This is an optional per-worker availability control. It introduces no dependencies, mounts, capabilities or Docker API permissions. Enabling a ceiling fails if the daemon reports missing required controllers, rather than proceeding with an unenforceable budget. A caller cannot raise an enabled operator ceiling. Existing running workers outside the ceiling are refused without automatic deletion.
These limits do not establish Docker API authorization, cap aggregate concurrent flow consumption, or cover disk/network usage or separate containers launched through an agent-accessible Docker API. Rootless daemons need the corresponding cgroup controllers delegated.
Performance Impact
No additional Docker requests are made in the disabled or enabled paths: controller support uses the existing info response and reuse validation uses the existing container inspection. Applying or checking limits adds constant-time arithmetic. Enabled ceilings intentionally throttle CPU usage or allow the kernel to terminate a worker on memory exhaustion; operators should size them for their workload. No throughput benchmark was performed.
Documentation Updates
- README.md updates
- API documentation updates
- Configuration documentation updates
- GraphQL schema updates
- Other:
.env.exampleandbackend/docs/docker.md
API and GraphQL contracts are unchanged.
Deployment Notes
No migration is needed with both settings left at their default 0. Operators opting in can set, for example, DOCKER_WORKER_CPUS=2 and DOCKER_WORKER_MEMORY=4294967296 for up to 2 CPUs and 4 GiB RAM per worker, with no worker swap.
When enabling or lowering a ceiling, stop affected flows and recreate their workers before reuse. Existing data is not automatically removed or altered. CPU and memory ceilings can be enabled independently. A RAM ceiling requires both memory and swap-limit support; an unsupported explicitly enabled configuration fails Docker client initialization.
Checklist
Code Quality
- My code follows the project's coding standards
- I have added/updated necessary documentation
- I have added tests to cover my changes
- All new and existing tests pass
- I have run
go fmtandgo vet(for Go code) - I have run
pnpm run lint(for TypeScript/JavaScript code)
The backend suite passes locally and frontend checks pass in the fork workflow. The all-tests checkbox remains open pending full CI/E2E verification; no JavaScript or TypeScript files were changed.
Security
- I have considered security implications
- Changes maintain or improve the security model
- Sensitive information has been properly handled
Compatibility
- Changes are backward compatible
- Breaking changes are clearly marked and documented
- Dependencies are properly updated
No breaking default, dependency change or database migration is introduced.
Documentation
- Documentation is clear and complete
- Comments are added for non-obvious code
- API changes are documented
No API contract changes.
Additional Notes
This deliberately focuses on optional worker resource ceilings. Aggregate budgets, Docker API authorization and network policy are separate concerns.
AI assistance was used for implementation, regression tests and independent review. The changes were reviewed and validated with the checks above before submission.