Deployment
AuditFlow runs the same way locally, on Kubernetes and on managed cloud infrastructure; only the discovery mode and the backing services change.
Docker Compose
Two Compose profiles cover local iteration and observability:
| Command | Stack | When to use |
|---|---|---|
docker compose up --build -d | 3 services + RabbitMQ + Valkey + Cerbos + ClickHouse | Same stack as Kubernetes minus the gateway; Redis-backed dedup, queryable sink |
docker compose -f docker-compose.yml -f docker-compose-observability.yml up --build -d | Stack + OTel Collector + Tempo + Loki + Prometheus + Grafana | Full telemetry, fast iteration |
Build the backend jar first with mvn -B clean package -DskipTests -pl auditflow-be -am.
cp .env.example .env # optional: only needed for custom RabbitMQ credentials
docker compose -f docker-compose.yml -f docker-compose-observability.yml up --build -d
Observability overlay URLs:
| URL | Credentials | Purpose |
|---|---|---|
| http://localhost:3000 | admin / admin | Grafana — pre-provisioned AuditFlow Overview dashboard |
| http://localhost:9090 | — | Prometheus query UI |
| http://localhost:3100 | — | Loki API (use Grafana for UI) |
| http://localhost:3200 | — | Tempo API (use Grafana for UI) |
Kubernetes
AuditFlow supports Kubernetes-native service discovery. Set discovery.mode: kubernetes and the backend resolves transformer and sink service IPs via the fabric8 KubernetesClient at runtime — no hardcoded URLs in Deployment specs.
# application.yml or JAVA_OPTS in your Deployment
transformer:
discovery:
mode: kubernetes
namespace: auditflow
service-name: auditflow-transformer
sink:
discovery:
mode: kubernetes
namespace: auditflow
service-name: auditflow-sink
Health probes are ready to use out of the box:
livenessProbe:
httpGet:
path: /live # Python services; use /actuator/health for the Java backend
port: 8081
readinessProbe:
httpGet:
path: /ready
port: 8081
Custom sinks and transformers can be injected at runtime via a ConfigMap or volume — no image rebuild required:
volumeMounts:
- name: custom-sinks
mountPath: /home/l64user/sinks_bootstrap
volumes:
- name: custom-sinks
configMap:
name: my-sink-plugins
Helm charts are published from
labs64.io-helm-charts:helm repo add labs64io https://labs64.github.io/labs64.io-helm-charts, thenlabs64io/auditflowfor AuditFlow alone orlabs64io/labs64io-ecosystemfor the platform. Itsinstall.shputs the whole stack on a cluster and verifies it; see Deploy to your Kubernetes cluster.
Cloud-managed infrastructure
Swap any self-hosted component for a managed cloud service — pipeline behaviour is unchanged:
| Component | Cloud alternatives |
|---|---|
| Message broker (RabbitMQ) | Amazon MQ for RabbitMQ, CloudAMQP |
| Redis (idempotency) | ElastiCache, Azure Cache for Redis, Memorystore |
| Log storage | Amazon OpenSearch Service, Grafana Cloud Loki |
| Archive storage | Amazon S3, Google Cloud Storage, Azure Blob Storage |
| Metrics / traces | Grafana Cloud, Datadog, Honeycomb |
The cloud sinks (aws_s3_sink, aws_cloudwatch_sink, gcs_sink, azure_blob_sink) read connection details from sink.properties, resolved from environment variables — no credentials in configuration files.
Running without Redis
Redis is used only for idempotency and deduplication, and for multi-replica rate limiting. A single-replica deployment that needs neither can skip running Redis. (The bundled docker-compose.yml stack does run Valkey, so that local behaviour matches Kubernetes.) To run without it via the Helm chart, set:
applicationYaml:
auditflow:
idempotency:
store: memory
tenants:
ratelimit:
backend: in-memory
spring:
autoconfigure:
exclude:
- org.springframework.boot.data.redis.autoconfigure.DataRedisAutoConfiguration
- org.springframework.boot.data.redis.autoconfigure.DataRedisReactiveAutoConfiguration
- org.springframework.boot.data.redis.autoconfigure.health.DataRedisHealthContributorAutoConfiguration
- org.springframework.boot.data.redis.autoconfigure.health.DataRedisReactiveHealthContributorAutoConfiguration
All four exclude entries are required together — Spring Boot still instantiates a Redis connection factory (and its health indicator, which will report DOWN against an unreachable Redis) if only idempotency.store/ratelimit.backend are switched without also excluding the auto-configuration classes.
Running more than one replica without Redis is not supported: rate-limiting falls back to per-process in-memory counters, so each replica enforces its own independent quota instead of a shared one.