Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.finwatch.finance/llms.txt

Use this file to discover all available pages before exploring further.

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

ResourceMinimumRecommendedNotes
RAM2 GB4-8 GBDuckDB is memory-intensive for aggregate queries
CPU1 core2-4 coresSingle-threaded DuckDB, but Go runtime uses additional cores
Disk1 GB10-50 GBDepends on transaction volume and retention
NetworkOutbound HTTPSRequired for Git sync and WebSocket tunnel
OSLinux (amd64)Also supports macOS and Windows for development

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

VariableDefaultDescription
FINWATCH_PORT8081HTTP server port
WATCH_SCRIPT_DIRwatch_scriptsDirectory where .ws rule files are stored
FINWATCH_MEMORY_LIMIT2GiBMaximum memory DuckDB is allowed to use

Git Repository (GitOps)

VariableDefaultDescription
WATCH_SCRIPT_GIT_REPO(empty)Git repository URL for rule syncing. Enables GitOps mode when set.
WATCH_SCRIPT_GIT_BRANCHmainBranch to track for rule updates

Data Synchronization

VariableDefaultDescription
BLNK_DSN(empty)PostgreSQL connection string for Blnk database. Enables watermark sync when set.

Database Paths

FinWatch creates its databases in the blnk_agent/ directory relative to the working directory:
PathPurpose
blnk_agent/blnk.dbTransaction data (DuckDB)
blnk_agent/instructions.dbCompiled rules (DuckDB)
blnk_agent/duckdb_temp/Temporary directory for DuckDB spill-to-disk

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:
# Conservative (low-traffic systems)
export FINWATCH_MEMORY_LIMIT="1GiB"

# Standard (medium-traffic systems)
export FINWATCH_MEMORY_LIMIT="2GiB"

# High-performance (high-traffic systems)
export FINWATCH_MEMORY_LIMIT="8GiB"
Accepted formats: 512MiB, 1GiB, 2GiB, 4GiB, 8GiB, 16GiB.

DuckDB Pragmas

FinWatch initializes DuckDB with the following settings:
SET access_mode = 'READ_WRITE';
SET threads = 1;
SET memory_limit = '2GiB';
SET checkpoint_threshold = '64MiB';
  • 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

Transaction VolumeRecommended MemoryNotes
< 10K/day1GiBMinimal footprint
10K - 100K/day2GiBDefault is sufficient
100K - 1M/day4GiBAggregate queries benefit from more memory
1M - 10M/day8GiBLarge time windows need significant buffer pool
> 10M/day16GiB+Consider data retention policies

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

docker run -d \
  --name finwatch \
  --restart unless-stopped \
  -p 8081:8081 \
  -e WATCH_SCRIPT_GIT_REPO="https://github.com/your-org/finwatch-rules.git" \
  -e WATCH_SCRIPT_GIT_BRANCH="main" \
  -e WATCH_SCRIPT_DIR="/app/watch_scripts" \
  -e BLNK_DSN="postgres://user:password@db-host:5432/blnk?sslmode=require" \
  -e FINWATCH_MEMORY_LIMIT="4GiB" \
  -v finwatch-data:/app/blnk_agent \
  --memory 6g \
  --cpus 2 \
  finwatch/finwatch:latest
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

version: '3.8'

services:
  finwatch:
    image: finwatch/finwatch:latest
    container_name: finwatch
    restart: unless-stopped
    ports:
      - "8081:8081"
    environment:
      - WATCH_SCRIPT_GIT_REPO=https://github.com/your-org/finwatch-rules.git
      - WATCH_SCRIPT_GIT_BRANCH=main
      - WATCH_SCRIPT_DIR=/app/watch_scripts
      - BLNK_DSN=postgres://user:password@db-host:5432/blnk?sslmode=require
      - FINWATCH_MEMORY_LIMIT=4GiB
    volumes:
      - finwatch-data:/app/blnk_agent
    deploy:
      resources:
        limits:
          memory: 6G
          cpus: '2'
        reservations:
          memory: 2G
          cpus: '1'
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8081/instructions"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 15s
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "5"

volumes:
  finwatch-data:
    driver: local

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

apiVersion: apps/v1
kind: Deployment
metadata:
  name: finwatch
  labels:
    app: finwatch
spec:
  replicas: 1
  selector:
    matchLabels:
      app: finwatch
  template:
    metadata:
      labels:
        app: finwatch
    spec:
      containers:
        - name: finwatch
          image: finwatch/finwatch:latest
          ports:
            - containerPort: 8081
              name: http
          env:
            - name: WATCH_SCRIPT_GIT_REPO
              value: "https://github.com/your-org/finwatch-rules.git"
            - name: WATCH_SCRIPT_GIT_BRANCH
              value: "main"
            - name: WATCH_SCRIPT_DIR
              value: "/app/watch_scripts"
            - name: FINWATCH_MEMORY_LIMIT
              value: "4GiB"
            - name: BLNK_DSN
              valueFrom:
                secretKeyRef:
                  name: finwatch-secrets
                  key: blnk-dsn
          resources:
            requests:
              memory: "2Gi"
              cpu: "500m"
            limits:
              memory: "6Gi"
              cpu: "2000m"
          livenessProbe:
            httpGet:
              path: /instructions
              port: 8081
            initialDelaySeconds: 15
            periodSeconds: 30
            timeoutSeconds: 5
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /instructions
              port: 8081
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 3
            failureThreshold: 2
          volumeMounts:
            - name: data
              mountPath: /app/blnk_agent
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: finwatch-data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: finwatch-data
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 50Gi
  storageClassName: ssd
---
apiVersion: v1
kind: Service
metadata:
  name: finwatch
spec:
  selector:
    app: finwatch
  ports:
    - port: 8081
      targetPort: 8081
      name: http
  type: ClusterIP

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:
{
  "level": "info",
  "time": "2026-04-18T14:30:00Z",
  "message": "Received inject request",
  "transaction_id": "txn_001"
}

Key Log Events to Monitor

Log MessageLevelMeaning
Received inject requestinfoTransaction received via API
Compiled watch scriptinfoA rule was successfully compiled
Failed to compile watch scripterrorA rule has a syntax error
Error processing transactionerrorTransaction injection failed
WebSocket tunnel not connectederrorAnomaly reporting is offline
Failed to clone or update Git repositoryfatalGitOps sync is broken

Metrics to Watch

MetricSourceWarning ThresholdAction
API response timeHTTP access logs> 100ms P95Investigate DuckDB memory pressure
Rule compilation errorsApplication logsAnyFix the malformed .ws file
Transaction ingestion rateApplication logsSudden dropCheck API connectivity
DuckDB file sizeDisk monitoring> 80% of diskImplement data retention policy
Memory usageContainer metrics> 80% of limitIncrease FINWATCH_MEMORY_LIMIT
WebSocket disconnectionsApplication logs> 1/hourCheck network connectivity to Blnk Cloud
Git sync failuresApplication logsAnyCheck Git credentials and network

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

DataLocationBackup StrategyRecovery Priority
Rules (.ws files)Git repositoryGit itself is the backupCritical — rules are the core logic
Transaction datablnk_agent/blnk.dbFile-level backup or sync from PostgreSQLMedium — can be rebuilt from PostgreSQL
Compiled instructionsblnk_agent/instructions.dbFile-level backupLow — rebuilt automatically from .ws files
Variable definitionsGit repository or config storeSame as rulesHigh — needed for rules to function

DuckDB File Backup

DuckDB database files can be backed up with a simple file copy while FinWatch is running, but for consistency, prefer:
# Stop FinWatch, copy the file, restart
docker stop finwatch
cp blnk_agent/blnk.db blnk_agent/blnk.db.backup
docker start finwatch

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