Matcher automates financial reconciliation across multiple data sources, eliminating manual matching work and providing a full audit trail for every transaction. Setting up Matcher means putting in place the foundation for exception management, compliance reporting, and operational visibility. This guide walks you through deploying Matcher in development and production environments.Documentation Index
Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
Use this file to discover all available pages before exploring further.
Docker compose (development)
Docker Compose is the recommended approach for local development and testing.
1. Clone the repository
2. Configure the environment
Thedocker-compose.yml file includes sensible defaults for local development. You can override any value by setting environment variables in your shell or by creating a .env file in the project root.
Refer to Environment variables for details on available settings.
One required variable has no default:
SYSTEMPLANE_SECRET_MASTER_KEY. See config/.config-map.example in the repository for generation instructions.3. Start services
Start the required infrastructure services:4. Verify the installation
Confirm that Matcher is running:Docker compose services
The defaultdocker-compose.yml includes:
| Service | Port | Purpose |
|---|---|---|
postgres | 5432 | PostgreSQL primary database |
postgres-replica | 5433 | PostgreSQL read replica |
redis | 6379 | Valkey (Redis-compatible) cache |
rabbitmq | 5672, 15672 | RabbitMQ (AMQP and management UI) |
seaweedfs | 8333, 9333 | S3-compatible object storage |
app | 4018 | Matcher API |
Development with hot reload
For active development, use:Kubernetes / helm (production)
Production deployments should use the official Helm chart.
Prerequisites
- Kubernetes 1.28+
- Helm 3.12+
kubectlconfigured for the target cluster
1. Create a namespace
2. Configure values
Create avalues.yaml file with your deployment configuration:
3. Create secrets
Create Kubernetes secrets for sensitive credentials:4. Install the chart
5. Verify the deployment
Upgrading
To upgrade an existing deployment:Environment variables
Matcher is configured entirely through environment variables.
Application
| Variable | Default | Description |
|---|---|---|
ENV_NAME | development | Runtime environment name |
LOG_LEVEL | info | Log verbosity |
DEPLOYMENT_MODE | local | Deployment mode (local, byoc, saas) |
SERVER_ADDRESS | :4018 | HTTP server bind address |
HTTP_BODY_LIMIT_BYTES | 33554432 | Maximum request body size (bytes) |
CORS
| Variable | Default | Description |
|---|---|---|
CORS_ALLOWED_ORIGINS | http://localhost:3000 | Allowed origins |
CORS_ALLOWED_METHODS | GET,POST,PUT,PATCH,DELETE,OPTIONS | Allowed HTTP methods |
CORS_ALLOWED_HEADERS | Origin,Content-Type,Accept,Authorization,X-Request-ID | Allowed request headers |
Database (PostgreSQL)
| Variable | Default | Description |
|---|---|---|
POSTGRES_HOST | localhost | Primary database host |
POSTGRES_PORT | 5432 | Primary database port |
POSTGRES_USER | matcher | Username |
POSTGRES_PASSWORD | matcher_dev_password | Password |
POSTGRES_DB | matcher | Database name |
POSTGRES_SSLMODE | disable | SSL mode |
POSTGRES_TLS_REQUIRED | false | Enforce TLS at bootstrap |
POSTGRES_MAX_OPEN_CONNS | 25 | Max open connections |
POSTGRES_MAX_IDLE_CONNS | 5 | Max idle connections |
POSTGRES_CONN_MAX_LIFETIME_MINS | 30 | Connection max lifetime (minutes) |
POSTGRES_CONN_MAX_IDLE_TIME_MINS | 5 | Connection max idle time (minutes) |
POSTGRES_CONNECT_TIMEOUT_SEC | 10 | Connection timeout (seconds) |
POSTGRES_QUERY_TIMEOUT_SEC | 30 | Query timeout (seconds) |
MIGRATIONS_PATH | migrations | Path to migration files |
Database replica (PostgreSQL)
| Variable | Default | Description |
|---|---|---|
POSTGRES_REPLICA_HOST | — | Replica host |
POSTGRES_REPLICA_PORT | — | Replica port |
POSTGRES_REPLICA_USER | — | Replica username |
POSTGRES_REPLICA_PASSWORD | — | Replica password |
POSTGRES_REPLICA_DB | — | Replica database name |
POSTGRES_REPLICA_SSLMODE | — | Replica SSL mode |
POSTGRES_REPLICA_TLS_REQUIRED | false | Enforce TLS for replica |
Cache (Redis)
| Variable | Default | Description |
|---|---|---|
REDIS_HOST | localhost:6379 | Redis address (host:port) |
REDIS_MASTER_NAME | — | Sentinel master name |
REDIS_PASSWORD | — | Password |
REDIS_DB | 0 | Database index |
REDIS_PROTOCOL | 3 | RESP protocol version |
REDIS_TLS | false | Enable TLS |
REDIS_TLS_REQUIRED | false | Enforce TLS at bootstrap |
REDIS_CA_CERT | — | CA certificate path |
REDIS_POOL_SIZE | 10 | Connection pool size |
REDIS_MIN_IDLE_CONNS | 2 | Minimum idle connections |
REDIS_READ_TIMEOUT_MS | 3000 | Read timeout (milliseconds) |
REDIS_WRITE_TIMEOUT_MS | 3000 | Write timeout (milliseconds) |
REDIS_DIAL_TIMEOUT_MS | 5000 | Dial timeout (milliseconds) |
Messaging (RabbitMQ)
| Variable | Default | Description |
|---|---|---|
RABBITMQ_URI | amqp | URI scheme (amqp or amqps) |
RABBITMQ_HOST | localhost | Broker host |
RABBITMQ_PORT | 5672 | Broker port |
RABBITMQ_USER | matcher_admin | Username |
RABBITMQ_PASSWORD | matcher_dev_password | Password |
RABBITMQ_VHOST | / | Virtual host |
RABBITMQ_HEALTH_URL | http://localhost:15672 | Management API URL for health checks |
RABBITMQ_ALLOW_INSECURE_HEALTH_CHECK | false | Allow HTTP (non-TLS) health check |
RABBITMQ_TLS_REQUIRED | false | Enforce TLS at bootstrap |
Authentication
| Variable | Default | Description |
|---|---|---|
PLUGIN_AUTH_ENABLED | false | Enable authentication |
PLUGIN_AUTH_ADDRESS | — | Auth service URL |
AUTH_JWT_SECRET | — | JWT signing secret |
Object storage (S3-compatible)
| Variable | Default | Description |
|---|---|---|
OBJECT_STORAGE_ENDPOINT | http://localhost:8333 | S3 endpoint URL |
OBJECT_STORAGE_REGION | us-east-1 | S3 region |
OBJECT_STORAGE_BUCKET | matcher-exports | Bucket for exports |
OBJECT_STORAGE_ACCESS_KEY_ID | — | Access key ID |
OBJECT_STORAGE_SECRET_ACCESS_KEY | — | Secret access key |
OBJECT_STORAGE_USE_PATH_STYLE | true | Use path-style addressing |
OBJECT_STORAGE_ALLOW_INSECURE_ENDPOINT | false | Allow HTTP (non-TLS) endpoint |
OBJECT_STORAGE_TLS_REQUIRED | false | Enforce TLS at bootstrap |
Observability
| Variable | Default | Description |
|---|---|---|
ENABLE_TELEMETRY | false | Enable OpenTelemetry |
OTEL_RESOURCE_SERVICE_NAME | matcher | Service name for traces/metrics |
OTEL_LIBRARY_NAME | github.com/LerianStudio/matcher | Instrumentation library name |
OTEL_RESOURCE_SERVICE_VERSION | 1.1.0 | Service version |
OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT | development | Deployment environment label |
OTEL_EXPORTER_OTLP_ENDPOINT | localhost:4317 | OTLP collector endpoint |
DB_METRICS_INTERVAL_SEC | 15 | DB metrics collection interval |
TLS
| Variable | Default | Description |
|---|---|---|
SERVER_TLS_CERT_FILE | — | TLS certificate path |
SERVER_TLS_KEY_FILE | — | TLS private key path |
TLS_TERMINATED_UPSTREAM | false | Trust upstream TLS termination (e.g., load balancer) |
TRUSTED_PROXIES | — | Trusted proxy CIDR ranges |
Rate limiting
| Variable | Default | Description |
|---|---|---|
RATE_LIMIT_ENABLED | true | Enable global rate limiting |
RATE_LIMIT_MAX | 100 | Max requests per window |
RATE_LIMIT_EXPIRY_SEC | 60 | Rate limit window (seconds) |
EXPORT_RATE_LIMIT_MAX | 10 | Max export requests per window |
EXPORT_RATE_LIMIT_EXPIRY_SEC | 60 | Export rate limit window (seconds) |
DISPATCH_RATE_LIMIT_MAX | 50 | Max dispatch requests per window |
DISPATCH_RATE_LIMIT_EXPIRY_SEC | 60 | Dispatch rate limit window (seconds) |
ADMIN_RATE_LIMIT_MAX | 30 | Max admin requests per window |
ADMIN_RATE_LIMIT_EXPIRY_SEC | 60 | Admin rate limit window (seconds) |
Swagger
| Variable | Default | Description |
|---|---|---|
SWAGGER_ENABLED | false | Enable Swagger UI |
SWAGGER_HOST | — | Override Swagger spec host |
SWAGGER_SCHEMES | https | Swagger spec schemes (comma-separated) |
Idempotency
| Variable | Default | Description |
|---|---|---|
IDEMPOTENCY_RETRY_WINDOW_SEC | 300 | Window for retrying failed idempotent requests (seconds) |
IDEMPOTENCY_SUCCESS_TTL_HOURS | 168 | How long completed idempotency keys are cached (hours) |
IDEMPOTENCY_HMAC_SECRET | — | HMAC secret for signing idempotency keys (min 32 bytes) |
Deduplication
| Variable | Default | Description |
|---|---|---|
DEDUPE_TTL_SEC | 3600 | TTL for deduplication keys (seconds) |
Outbox
| Variable | Default | Description |
|---|---|---|
OUTBOX_RETRY_WINDOW_SEC | 300 | Cooldown before retrying failed events (seconds) |
OUTBOX_DISPATCH_INTERVAL_SEC | 2 | Dispatcher poll interval (seconds) |
Workers
| Variable | Default | Description |
|---|---|---|
EXPORT_WORKER_ENABLED | true | Enable export worker |
EXPORT_WORKER_POLL_INTERVAL_SEC | 5 | Export worker poll interval (seconds) |
EXPORT_WORKER_PAGE_SIZE | 1000 | Rows per export page |
EXPORT_PRESIGN_EXPIRY_SEC | 3600 | Pre-signed URL expiry for exports (seconds) |
CLEANUP_WORKER_ENABLED | true | Enable cleanup worker |
CLEANUP_WORKER_INTERVAL_SEC | 3600 | Cleanup worker interval (seconds) |
CLEANUP_WORKER_BATCH_SIZE | 100 | Cleanup batch size |
CLEANUP_WORKER_GRACE_PERIOD_SEC | 3600 | Grace period before cleanup (seconds) |
WEBHOOK_TIMEOUT_SEC | 30 | Webhook dispatch timeout (seconds) |
CALLBACK_RATE_LIMIT_PER_MIN | 60 | Max callbacks per external system per minute |
Scheduler
| Variable | Default | Description |
|---|---|---|
SCHEDULER_INTERVAL_SEC | 60 | Cron-based scheduler poll interval (seconds) |
Archival
| Variable | Default | Description |
|---|---|---|
ARCHIVAL_WORKER_ENABLED | false | Enable audit log archival worker |
ARCHIVAL_WORKER_INTERVAL_HOURS | 24 | Archival run interval (hours) |
ARCHIVAL_HOT_RETENTION_DAYS | 90 | Days to keep data in hot storage |
ARCHIVAL_WARM_RETENTION_MONTHS | 24 | Months to keep data in warm storage |
ARCHIVAL_COLD_RETENTION_MONTHS | 84 | Months to keep data in cold storage |
ARCHIVAL_BATCH_SIZE | 5000 | Rows per archival batch |
ARCHIVAL_STORAGE_BUCKET | matcher-archives | S3 bucket for archives |
ARCHIVAL_STORAGE_PREFIX | archives/audit-logs | S3 key prefix for archives |
ARCHIVAL_STORAGE_CLASS | GLACIER | S3 storage class for archives |
ARCHIVAL_PARTITION_LOOKAHEAD | 3 | Partition lookahead count |
ARCHIVAL_PRESIGN_EXPIRY_SEC | 3600 | Pre-signed URL expiry for archives (seconds) |
Fetcher / Discovery
| Variable | Default | Description |
|---|---|---|
FETCHER_ENABLED | false | Enable Fetcher-backed discovery |
FETCHER_URL | http://localhost:4006 | Fetcher service URL |
FETCHER_ALLOW_PRIVATE_IPS | false | Allow private IP ranges |
FETCHER_HEALTH_TIMEOUT_SEC | 5 | Fetcher health check timeout (seconds) |
FETCHER_REQUEST_TIMEOUT_SEC | 30 | Fetcher request timeout (seconds) |
FETCHER_DISCOVERY_INTERVAL_SEC | 60 | Discovery poll interval (seconds) |
FETCHER_SCHEMA_CACHE_TTL_SEC | 300 | Schema cache TTL (seconds) |
FETCHER_EXTRACTION_POLL_INTERVAL_SEC | 5 | Extraction poll interval (seconds) |
FETCHER_EXTRACTION_TIMEOUT_SEC | 600 | Extraction timeout (seconds) |
FETCHER_MAX_EXTRACTION_BYTES | 2147483648 | Max extraction payload size (bytes, default 2 GiB) |
FETCHER_BRIDGE_INTERVAL_SEC | 30 | Bridge worker poll interval (seconds) |
FETCHER_BRIDGE_BATCH_SIZE | 50 | Extractions per tenant per bridge cycle |
FETCHER_BRIDGE_TENANT_CONCURRENCY | 4 | Concurrent tenants in bridge cycle |
FETCHER_BRIDGE_STALE_THRESHOLD_SEC | 3600 | Stale extraction threshold (seconds) |
FETCHER_BRIDGE_RETRY_MAX_ATTEMPTS | 5 | Max retry attempts per extraction |
FETCHER_CUSTODY_RETENTION_SWEEP_INTERVAL_SEC | 900 | Custody sweep interval (seconds) |
FETCHER_CUSTODY_RETENTION_GRACE_PERIOD_SEC | 3600 | Grace period before custody deletion (seconds) |
APP_ENC_KEY | — | Base64-encoded master key shared with Fetcher |
M2M credentials
| Variable | Default | Description |
|---|---|---|
M2M_TARGET_SERVICE | fetcher | Target service name for credential lookup |
M2M_CREDENTIAL_CACHE_TTL_SEC | 300 | Redis cache TTL for M2M credentials (seconds) |
AWS_REGION | — | AWS region for Secrets Manager |
Infrastructure
| Variable | Default | Description |
|---|---|---|
INFRA_CONNECT_TIMEOUT_SEC | 30 | Infrastructure startup connection timeout (seconds) |
HEALTH_CHECK_TIMEOUT_SEC | 5 | Legacy per-check probe timeout (seconds) |
HEALTH_CHECK_TIMEOUT_MS | 800 | Per-check probe timeout (milliseconds, preferred) |
For multi-tenant deployment settings, see Multi-Tenant Mode. For runtime configuration management, see Runtime Configuration (Systemplane).
Verify the installation
Validate that Matcher is operating correctly.
Liveness check
Readiness check
API verification
Troubleshooting
Common issues
Connection refused to PostgreSQL
Connection refused to PostgreSQL
- Cause: PostgreSQL is not running or unreachable.
- Resolution:
- Verify PostgreSQL is running:
docker-compose ps postgres - Check connection values in
.env - Test connectivity:
nc -zv localhost 5432 - Review logs:
docker-compose logs postgres
Redis connection timeout
Redis connection timeout
- Cause: Redis is not running or credentials are incorrect.
- Resolution:
- Verify Redis is running:
docker-compose ps redis - Confirm
REDIS_PASSWORD - Test connectivity:
redis-cli -h localhost ping
RabbitMQ queues not created
RabbitMQ queues not created
- Cause: RabbitMQ is still initializing or the virtual host is missing.
- Resolution:
- Wait until RabbitMQ is healthy
- Access the management UI at http://localhost:15672
- Verify
RABBITMQ_VHOST
Authentication errors
Authentication errors
- Cause: Auth service is unreachable or the token is invalid.
- Resolution:
- Verify
PLUGIN_AUTH_ADDRESS - Disable auth for development:
PLUGIN_AUTH_ENABLED=false - Review auth service logs
Migration failed
Migration failed
- Cause: Database migrations could not be applied.
- Resolution:
- Check migration status:
make migrate-status - Review migration logs
- Apply migrations manually:
make migrate-up - Inspect the
schema_migrationstable if needed
Database migration errors after upgrading from main
Database migration errors after upgrading from main
If upgrading from the main branch, migrations 000020 and 000021 run automatically. Migration 000020 renames systemplane configuration keys for cross-product standardization. Migration 000021 converts the
external_system column from an enum type to VARCHAR(255), allowing arbitrary external system identifiers. If migrations fail, check the schema_migrations table and ensure no conflicting manual changes exist.Viewing logs
Debug mode
Enable debug logging for additional visibility:Next steps
Quick start
Run your first reconciliation.
Configuration
Configure contexts, sources, and match rules.

