This guide walks through tuning AgileSec to handle large data volumes and/or large numbers of sensors.
Test Profiles
For full tuning coverage and validation, tune and validate against both test profiles. Each test profile stresses different parts of the AgileSec data pipeline.
|
Test Profile |
Ingestion Shape |
Details |
Pipeline Stresses |
|
|---|---|---|---|---|
|
1 |
EDR, CrowdStrike |
|
|
Stresses connection counts, file descriptors, HAProxy |
|
2 |
Tanium |
|
|
Stresses buffer sizing, flush threads, OpenSearch bulk/write queue depth, Kafka partition throughput per partition. |
Tuning Prioritization Strategy
OS and HAProxy tuning should be applied as a baseline, regardless of symptoms. Other tuning may be prioritized and applied based on platform symptoms as follows.
|
Symptoms |
Tuning Priorities |
|---|---|
|
HAProxy 503s / backend marked down under load |
|
|
OpenSearch 429s ( |
|
|
Many sensors, connection-count symptoms (EDR profile) |
|
|
Few sensors, large-payload/throughput symptoms (Tanium profile) |
|
Important: In general, a 503 or rejection at any one tier is often caused by backpressure one or two tiers downstream. Do not stop tuning at the tier where the error surfaces: trace it back using the metrics available (buffer stats, thread pool stats, HAProxy logs) before concluding tuning is complete.
Baseline Tuning: OS and HAProxy
Apply OS-Level Kernel and HAProxy tuning preventatively as a baseline, even before problems turn up. Every node should have headroom for file descriptors and connections preventatively.
Node topology
|
Node |
Relevant Services |
OS Tuning Considerations |
|---|---|---|
|
Frontends |
HAProxy |
HAProxy terminates sensor connections, routes to backend nodes. Frontend Nodes only need headroom for HAProxy’s connection count. |
|
Backends |
Fluentd, OpenSearch |
Fluentd and OpenSearch compete for the same CPU, memory, and file descriptor budget on backend nodes. Each backend node needs enough combined headroom for Fluentd’s workers/buffers and OpenSearch's thread pools/heap running side by side. |
1. OS-level kernel tuning
The following OS tuning steps apply to all frontend nodes (HAProxy) and backend nodes (Fluentd and OpenSearch).
Take the following into account for OS-level tuning:
-
vm.max_map_countmatters most for OpenSearch (Lucene mmap) -
The file descriptor and backlog settings matter for HAProxy and Fluentd.
-
On the backend nodes, size
nofileandfs.file-maxagainst Fluentd and OpenSearch running together, not either service alone – see Node topology above.
-
Add the following to
/etc/sysctl.conf:vm.max_map_count = 262144 fs.file-max = 2097152 net.core.somaxconn = 16384 net.ipv4.tcp_max_syn_backlog = 16384
-
Run
sysctl -pto apply without a reboot. -
Add the following to
/etc/security/limits.conf:<os user> - nofile 262144 <os user> - nproc 262144 <os user> - memlock unlimited -
limits.confonly applies to new login sessions, not already-running processes. systemd services do not always inherit PAM limits fromlimits.conf. For systemd-managed services (HAProxy, Fluentd, OpenSearch), also set the limit under[Service]in the unit's override file:[Service] LimitNOFILE=262144
2. HAProxy
Update services/haproxy/haproxy.cfg on all frontend and backend nodes.
-
Raise
maxconn(both files,globalanddefaults)global maxconn <sized to target connection count – see note below> ulimit-n <2x maxconn> defaults maxconn <below global, with headroom for other backends>-
Note:
maxconnby default is 4000/3000, process-wide across every frontend and backend in that instance – the first ceiling hit under load. Noulimit-nis set by default.
-
Sizing note: maxconn is not 1:1 with sensor count. maxconn also covers UI/API/dashboard traffic, health checks, and (on backend-2) the internal manager/ingestion/sm/opensearch hop and churn.
Rule of thumb (frontend config): maxconn = sensor count × 1.5 to cover base connections plus reconnect churn and UI/internal overhead in one number. Increase if you see frontend-level connection denials.
-
Add L7 health check + timeout overrides to
fluentd_backend.Add these lines to thebackend fluentd_backendblock:timeout connect 10s timeout check 30s timeout server 2m timeout client 2m option httpchk GET / http-check expect status 200 default-server inter 5s fall 3 rise 2 on-marked-down shutdown-sessions check port 6444-
Note: This replaces the existing
default-serverline already present in the block. Without timeouts, 10s timeouts are inherited fromdefaults, which are too short and can cause 503s on slower VMs or under heavy load.
-
Warning: If 503s persist after 2 minutes 30 seconds, increase incrementally (3–4 minutes). Do not jump to OpenSearch's 10m to avoid masking a truly stuck node.
-
Raise timeouts on
opensearch_backend/be_opensearchtimeout connect 10s timeout server 10m timeout client 10m
-
-
Note: OpenSearch bulk indexing/search/forcemerge/snapshot ops can run past the 1 minute default under heavy load.
-
3. Fluentd
Known Bug: Fix Needed
In config_envs/fluentd, FLUENTD_HEALTH_CHECK_POPT=6444 is misspelled. To fix, update to FLUENTD_HEALTH_CHECK_PORT=6444.
As written, the env var does not match td-agent.conf's ENV['FLUENTD_HEALTH_CHECK_PORT'] reference, so the health check silently falls back to the 24220 default instead of 6444.
Fluentd Tuning Strategy: Increasing Workers vs. Tuning Buffer Settings
Use signals from monitor_agent (port 24230) to decide whether add workers or tune buffer settings.
|
Fluentd Tuning Strategy |
Details |
|
|
|---|---|---|---|
|
1 |
Increase Workers |
CPU is the constraint:
|
|
|
2 |
Tune Buffer Settings ( |
Buffer is the constraint:
Note: Adding workers will not help if the constraint is Kafka/OpenSearch write throughput. |
|
Before tuning, check both signals from monitor_agent (port 24230) on each backend node.
-
From backend node terminals, check the buffer queue / retry stats:
curl http://localhost:<FLUENTD_MONITOR_PORT>/api/plugins.json -
Look at the output plugin entry for:
-
buffer_queue_length– chunks currently queued, waiting to flush. Climbing steadily = buffer backing up. -
buffer_total_queued_size– total bytes queued across those chunks. -
retry_count– failed flush attempts on the current chunk. Climbing alongside queue length = downstream (Kafka/OpenSearch) rejecting writes, not just a slow flush.
-
-
To filter to a single plugin instead of the full list:
curl "http://localhost:<FLUENTD_MONITOR_PORT>/api/plugins.json?type=kafka2" -
To extract only OpenSearch output plugins actually buffering and writing data:
curl -s http://localhost:<FLUENTD_MONITOR_PORT>/api/plugins.json \ | jq '.plugins[] | select(.plugin_id | test("opensearch")) | {plugin_id, buffer_queue_length, buffer_total_queued_size, retry_count}'-
This excludes filter/tag-routing plugins and stdout debug outputs.
-
Increase Workers
Add workers when a single process cannot accept/process connections fast enough. One worker means one accept loop handling all concurrent connections.
On backend nodes:
-
Uncomment and set
workersin<system>inservices/td-agent/etc/td-agent.conf:<system> ... workers "#{ENV['FLUENTD_WORKERS'] || 4}" </system> -
Move
healthcheckandmonitor_agentinto a<worker 0>block. Pinning sources toworker 0is required, not optional, before raising worker count:<worker 0> <source> @type http_healthcheck @id healthcheck bind 0.0.0.0 port "#{ENV['FLUENTD_HEALTH_CHECK_PORT'] || 24220}" </source> <source> @type monitor_agent bind 0.0.0.0 port "#{ENV['FLUENTD_MONITOR_PORT'] || 24230}" tag debug.monitor emit_interval "#{ENV['FLUENTD_MONITOR_INTERVAL_SECONDS'] || 1800}" </source> </worker>-
By default, sources sit at top level. If sources are not pinned to
worker 0, whenworkersis uncommented, every worker will try to bind the same ports, causing failure to start or other misbehavior.
-
Tune Buffer Settings
Update buffer settings in buf.conf / buf_new.conf when monitor_agent shows buffer queue length climbing, or when you see any of these downstream signals:
-
OpenSearch 429s / circuit breaker trips: check
GET _nodes/stats/thread_poolorGET _cat/thread_pool?vfor write queue rejections. -
retry_countclimbing alongsidebuffer_queue_lengthin themonitor_agent/jqoutput: distinguishes "flush is just slow" (queue grows, retries stay 0) from "downstream is actively rejecting" (both climb). -
td-agent logs showing
buffer flush took long timeorfailed to flush the bufferwarnings, which often surface the actual downstream exception.
On backend nodes:
-
Update
buf.confwhen sending data directly to Fluentd:@type "file" flush_at_shutdown true overflow_action block chunk_limit_size "#{ENV['FLUENTD_BUFFER_CHUNK_LIMIT_SIZE'] || '16M'}" total_limit_size "#{ENV['FLUENTD_BUFFER_TOTAL_LIMIT_SIZE'] || '5GB'}" flush_thread_count "#{ENV['FLUENTD_BUFFER_FLUSH_THREAD_COUNT'] || 2}" flush_mode "#{ENV['FLUENTD_BUFFER_FLUSH_MODE'] || 'interval'}" flush_interval "#{ENV['FLUENTD_BUFFER_FLUSH_INTERVAL'] || '5s'}" queued_chunks_limit_size "#{ENV['FLUENTD_QUEUED_CHUNKS_LIMIT_SIZE'] || 128}" retry_type exponential_backoff retry_wait "#{ENV['FLUENTD_BUFFER_RETRY_WAIT'] || '10s'}" retry_max_interval "#{ENV['FLUENTD_RETRY_MAX_INTERVAL'] || '3m'}" retry_forever "#{ENV['FLUENTD_RETRY_FOREVER'] || true}" -
Update
buf_new.confwhen sending data through ingestion service:flush_thread_count "#{ENV['FLUENTD_BUFFER_FLUSH_THREAD_COUNT'] || 2}"
Important: Align output_opensearch.conf If Needed
Update output_opensearch.conf as needed to stay in sync with buffer tuning.
-
bulk_message_request_threshold(default16M) inoutput_opensearch.confshould matchchunk_limit_sizeinbuf.conf/buf_new.conf.-
Raising
chunk_limit_sizewithout raisingbulk_message_request_thresholdcan split a single flush into multiple bulk requests unnecessarily.
-
-
request_timeout(default600s/10m) inoutput_opensearch.confshould matchopensearch_backend/be_opensearch's HAProxytimeout server/timeout client.
-
-
Otherwise, HAProxy will kill the connection before Fluentd's OpenSearch client gives up on its own.
-
4. OpenSearch
Updating OpenSearch’s refresh interval to 30 seconds (30s) delays finding visibility but does not affect policy execution, which forces an explicit refresh.
From AgileSec’s Advanced Dashboards → Dev Tools, update the refresh interval:
PUT kf-agilesec_com-isg-event-*/_settings
{
"index": { "refresh_interval": "30s" }
}