This guide shows you how to capture every HTTP request and response from Qtap and route it to storage backends using Fluent Bit. This pattern is ideal for comprehensive observability, troubleshooting, audit trails, and compliance requirements where you need complete traffic capture.
Why Fluent Bit?
While Qtap can write directly to S3-compatible object stores, Fluent Bit provides critical advantages at scale:
Performance: Batches and buffers writes to reduce API calls
Reliability: Built-in retry logic, buffering, and backpressure handling
Flexibility: Route traffic to multiple destinations (S3, CloudWatch, Elasticsearch, etc.)
Filtering: Apply additional filtering, enrichment, or transformation
Cost optimization: Batch uploads reduce S3 API costs significantly
Qtap captures HTTP traffic using eBPF (TLS inspection, no proxies)
Qtap writes HTTP transaction objects as structured logs
Docker forwards logs to Fluent Bit using the fluentd log driver
Fluent Bit parses JSON, filters, and tags HTTP transactions
Fluent Bit batches, buffers, and routes to storage backends
Set TTL policies on storage (recommended: 90 days)
Docker Deployment
This setup uses Docker's fluentd log driver to forward logs directly to Fluent Bit over the network. This approach is simpler and more reliable than tailing log files.
Step 1: Create Qtap Configuration
Create qtap-config.yaml:
Step 2: Create Fluent Bit Configuration
Create fluent-bit.conf:
How the filters work:
The grep filter runs before parsing so it can match the raw log field while it still exists
The parser filter then expands the JSON payload and removes the original log key
This sequencing ensures only HTTP transaction data reaches downstream outputs
Create parsers.conf:
Step 3: Create Docker Compose
Create docker-compose.yaml:
Step 4: Start and Validate
Expected output (one line per HTTP transaction):
Response bodies are base64 encoded in the body field.
Production Outputs
AWS S3
Replace the stdout output in fluent-bit.conf with:
Add environment variables to the fluent-bit service in docker-compose:
Set lifecycle policy:
MinIO (S3-Compatible)
AWS CloudWatch Logs
Set retention policy:
Multiple Destinations
You can send data to multiple outputs simultaneously:
Filtering and Optimization
Capture Only Errors (4xx/5xx)
To reduce volume, capture only failed requests using Qtap's Rulekit:
Filter by Domain
Capture only specific domains:
Exclude Noisy Processes
Filter in Fluent Bit
You can also filter within Fluent Bit using the grep filter:
When you aggregate logs with Docker's forward log driver, Fluent Bit surfaces the channel as source instead of stream, so update the regex to Regex source stdout in that deployment model.
Monitoring and Troubleshooting
Verify Qtap is Capturing Traffic
What to look for:
"exe": "/usr/bin/curl" - Process identified correctly
If you see records without "metadata", those are operational logs that should be filtered out
Make sure the grep filter runs before the parser filter or leave Preserve_Key On; otherwise the log field will disappear before the match runs
High memory usage:
Reduce Fluent Bit Flush interval (more frequent uploads)
Adjust total_file_size for S3 batching (smaller = more frequent uploads)
Add filtering to reduce captured volume
Enable compression for outputs
S3 upload failures:
Verify AWS credentials are correct
Check IAM permissions (s3:PutObject required)
Ensure bucket exists and is in the correct region
Check network connectivity to S3 endpoint
Alternative: File Tailing Approach
If you have existing logging infrastructure or cannot use the forward protocol, you can tail Docker's log files directly:
fluent-bit.conf:
docker-compose.yaml changes:
Ensure your parsers.conf file includes both the built-in docker parser and the generic_json_parser definition so the tail input can decode Docker log metadata before emitting HTTP transactions.
This approach requires more complex path management and may have permission issues. The forward protocol approach is recommended.
Best Practices
Start Small: Begin with error-only capture or specific domains, then expand
Set TTLs: Always configure lifecycle policies (90 days recommended)
Monitor Volume: Track storage growth and adjust filtering as needed
Use IAM Roles: Never hardcode S3 credentials in production
Compress: Enable gzip compression for S3 uploads
Batch Uploads: Use appropriate total_file_size (50M default)
Test Filtering: Validate filters match expected objects
Health Checks: Monitor Fluent Bit metrics and error logs
Backup Config: Version control all configuration files
Security: Limit access to logs - they contain sensitive data
Summary
This guide demonstrated a validated deployment pattern for capturing all HTTP traffic with Fluent Bit using Docker:
[SERVICE]
Flush 5
Daemon Off
Log_Level info
Parsers_File parsers.conf
# Input: Receive logs via forward protocol
[INPUT]
Name forward
Listen 0.0.0.0
Port 24224
# Filter: Only keep HTTP transactions (lines with metadata field)
[FILTER]
Name grep
Match docker.qtap # Matches the logging tag configured in docker-compose
Regex log .*"metadata":.*
# Filter: Parse JSON logs from Qtap (runs after grep so the log field still exists)
[FILTER]
Name parser
Match docker.qtap
Key_Name log
Parser generic_json_parser
Reserve_Data On
Preserve_Key Off
# Output: Write to stdout for testing
[OUTPUT]
Name stdout
Match docker.qtap
Format json_lines
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep On
[PARSER]
Name generic_json_parser
Format json
[OUTPUT]
Name s3
Match docker.qtap
endpoint http://minio:9000
bucket qtap-http-traffic
region us-east-1
total_file_size 50M
compression gzip
s3_key_format /qtap/year=%Y/month=%m/day=%d/$UUID.gz
use_put_object On
[OUTPUT]
Name cloudwatch_logs
Match docker.qtap
region us-east-1
log_group_name /qpoint/http-traffic
log_stream_prefix qtap-
auto_create_group On
# Send to S3 for long-term storage
[OUTPUT]
Name s3
Match docker.qtap
bucket your-bucket-name
region us-east-1
total_file_size 50M
upload_timeout 10m
compression gzip
s3_key_format /qtap/year=%Y/month=%m/day=%d/hour=%H/$UUID.gz
store_dir /tmp/fluent-bit/s3
use_put_object On
# Also send to stdout for debugging
[OUTPUT]
Name stdout
Match docker.qtap
Format json_lines
# Only capture requests to specific domain
[FILTER]
Name grep
Match docker.qtap
Regex request.url .*api\.important\.com.*
# Only keep stdout log lines (tail input exposes `stream`, the forward driver exposes `source`)
[FILTER]
Name grep
Match docker.*
Regex stream stdout
# Exclude health checks
[FILTER]
Name grep
Match docker.qtap
Exclude request.url .*/health$
# Check Fluent Bit logs for errors
docker logs fluent-bit | grep -i "error\|warn"
# Count processed HTTP objects (check for the metadata field)
docker logs fluent-bit | grep -c '"metadata"'
# Check S3 uploads (if using S3 output)
docker logs fluent-bit | grep "s3"
[INPUT]
Name tail
Path /var/lib/docker/containers/*/*.log
Parser docker
Tag docker.*
Refresh_Interval 5
Read_from_Head true
# Keep only stdout log lines (tail input exposes the field as `stream`)
[FILTER]
Name grep
Match docker.*
Regex stream stdout
[FILTER]
Name grep
Match docker.*
Regex log .*"metadata":.*
# Parse the JSON payload after filtering
[FILTER]
Name parser
Match docker.*
Key_Name log
Parser generic_json_parser
Reserve_Data On
Preserve_Key Off