Skip to main content
This guide covers everything a DevOps or Platform engineer needs to run FinWatch reliably in a production environment — from system requirements and configuration to Docker/Kubernetes deployment, monitoring, backup, and scaling.

System Requirements

Minimum Requirements

Disk Space Estimation

DuckDB stores data in a columnar format which is highly compressed. As a rough guide:
  • 1 million transactions ≈ 50-100 MB on disk
  • 10 million transactions ≈ 500 MB - 1 GB
  • 100 million transactions ≈ 5-10 GB
The instructions.db (compiled rules) is typically under 1 MB regardless of the number of rules.

Configuration Reference

FinWatch is configured entirely through environment variables. Here is the complete reference:

Core Configuration

Git Repository (GitOps)

Data Synchronization

Database Paths

FinWatch creates its databases in the blnk_agent/ directory relative to the working directory:

Memory Management

DuckDB’s performance comes from keeping data in memory. As your transaction volume grows, memory management becomes critical.

How Memory Is Used

DuckDB uses memory for:
  1. Buffer pool — Cached table pages for fast reads.
  2. Query execution — Intermediate results from aggregate queries.
  3. Write-ahead log — Buffered writes before checkpointing to disk.

Configuring the Memory Limit

The FINWATCH_MEMORY_LIMIT environment variable controls DuckDB’s maximum memory usage:
Accepted formats: 512MiB, 1GiB, 2GiB, 4GiB, 8GiB, 16GiB.

DuckDB Pragmas

FinWatch initializes DuckDB with the following settings:
  • threads = 1: Limits DuckDB to a single execution thread. This simplifies the single-writer concurrency model. Go’s runtime handles HTTP concurrency separately.
  • memory_limit: The upper bound on DuckDB’s memory consumption. When exceeded, DuckDB spills intermediate results to the duckdb_temp/ directory.
  • checkpoint_threshold = '64MiB': Controls how frequently in-memory data is flushed to disk. Lower values mean more frequent writes (safer but slower).

Memory Sizing Guidelines

Temp Directory

When DuckDB exceeds its memory limit, it spills data to blnk_agent/duckdb_temp/. Ensure this directory:
  • Has sufficient disk space (at least 2x the memory limit).
  • Is on fast storage (SSD recommended).
  • Is not on a tmpfs or RAM-backed filesystem (defeats the purpose of spilling).

Docker Deployment

Production Docker Run

Key flags:
  • --restart unless-stopped: Auto-restart on crash or server reboot.
  • -v finwatch-data:/app/blnk_agent: Persistent volume for DuckDB data. Without this, data is lost on container restart.
  • --memory 6g: Docker memory limit. Set higher than FINWATCH_MEMORY_LIMIT to leave room for Go runtime overhead.
  • --cpus 2: Limit CPU usage.

Production Docker Compose

Health Check

The Docker health check uses the /instructions endpoint. A 200 OK response confirms:
  • The HTTP server is running.
  • The DuckDB instruction database is accessible.
  • The API can serve requests.

Kubernetes Deployment

Deployment Manifest

Important Kubernetes Notes

  • Replicas: 1. FinWatch uses an embedded DuckDB database with a single-writer model. Running multiple replicas against the same data volume will cause write contention. If you need horizontal scaling, see the Scaling Considerations section.
  • PVC with SSD. DuckDB performance is heavily dependent on disk I/O for spill-to-disk operations. Use SSD-backed persistent volumes.
  • Secrets. Store BLNK_DSN (which contains database credentials) in a Kubernetes Secret, not in plain-text environment variables.
  • Liveness vs. Readiness. The liveness probe checks if FinWatch is alive; the readiness probe checks if it’s ready to accept traffic. The readiness probe has a shorter interval for faster traffic routing.

Monitoring and Observability

Log Format

FinWatch uses zerolog for structured JSON logging. In production, logs are formatted as:

Key Log Events to Monitor

Metrics to Watch

Integrating with Log Aggregators

FinWatch’s JSON logs can be consumed by any standard log aggregator:
  • Datadog: Configure the Docker log driver or use the Datadog agent’s log collection.
  • ELK Stack: Forward container logs via Filebeat or Fluentd.
  • Grafana Loki: Use Promtail to ship container logs.
  • CloudWatch: Use the awslogs Docker log driver.

Backup and Recovery

What to Back Up

DuckDB File Backup

DuckDB database files can be backed up with a simple file copy while FinWatch is running, but for consistency, prefer:

Recovery Scenarios

Scenario: DuckDB data is corrupted or lost.
  1. FinWatch restarts and creates a fresh DuckDB database.
  2. Compiled rules are rebuilt from the .ws files (via Git sync or local directory).
  3. Historical transaction data is rebuilt via the watermark sync from PostgreSQL.
  4. Recovery is fully automatic — no manual intervention required.
Scenario: Git repository is unavailable.
  1. FinWatch continues to operate with the last-synced rules.
  2. Git polling logs warnings but does not crash.
  3. When the repository becomes available again, FinWatch catches up automatically.
Scenario: PostgreSQL (BLNK_DSN) is unavailable.
  1. Watermark sync pauses. Logs warnings.
  2. FinWatch continues to evaluate rules against locally-stored data.
  3. Aggregate functions use the data available in DuckDB — results may be stale.
  4. When PostgreSQL recovers, the watermark sync resumes from where it left off.

Scaling Considerations

Single-Instance Model

FinWatch is designed as a single-instance service. This is a deliberate architectural choice driven by DuckDB’s single-writer concurrency model. The benefits:
  • Simplicity: No distributed coordination, no consensus protocols, no split-brain scenarios.
  • Consistency: All rules see the same data. No eventual consistency issues.
  • Performance: Local DuckDB queries are faster than any network-based alternative.

When to Scale

A single FinWatch instance can comfortably handle:
  • 10,000+ transactions per second for simple rules.
  • 1,000+ transactions per second with complex aggregate rules.
  • 100+ active rules with no performance impact.
If you’re hitting these limits, consider:
  1. Vertical scaling: Increase memory and CPU. DuckDB benefits significantly from more RAM.
  2. Rule optimization: Ensure cheap conditions are evaluated before expensive aggregates (the gate-and-probe pattern).
  3. Time window reduction: Smaller aggregate time windows mean less data to scan.
  4. Data retention: Purge old transaction data that is no longer needed for rule evaluation.

Multi-Instance Patterns

If you truly need horizontal scaling (e.g., processing 100K+ TPS), consider:
  • Sharding by source account: Route transactions to different FinWatch instances based on the source account. This ensures aggregate functions for a given account are always evaluated by the same instance.
  • Read replicas: Run multiple instances in read-only mode for serving API queries, with a single write instance for ingestion.
These patterns add significant operational complexity and should only be considered after exhausting vertical scaling options.

Pre-Production Checklist

  • Environment variables are set — Verify all required vars are configured.
  • Persistent volume is attached — DuckDB data survives container restarts.
  • Memory limits are appropriate — Docker/K8s limit > DuckDB memory_limit + 1-2 GB headroom.
  • Git repository is accessible — FinWatch can clone and pull the rules repo.
  • PostgreSQL is reachable — If using BLNK_DSN, verify connectivity.
  • Health checks are configured — Liveness and readiness probes are active.
  • Logging is aggregated — Logs are shipped to your monitoring platform.
  • Backup strategy is in place — Rules are in Git; data can be rebuilt.
  • Alerts are configured — Monitor for compilation errors, high latency, and sync failures.
  • Test transactions have been validated — Run a full test suite before going live.

Next Steps