> ## 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.

# Installing Matcher

> Deploy Matcher in development and production using Docker Compose, environment variables, and the supported infrastructure stack.

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.

## Docker compose (development)

***

Docker Compose is the recommended approach for local development and testing.

### 1. Clone the repository

```bash theme={null}
git clone https://github.com/LerianStudio/matcher.git
cd matcher
```

### 2. Configure the environment

The `docker-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](#environment-variables) for details on available settings.

<Note>
  One required variable has no default: `SYSTEMPLANE_SECRET_MASTER_KEY`. See `config/.config-map.example` in the repository for generation instructions.
</Note>

### 3. Start services

Start the required infrastructure services:

```bash theme={null}
docker-compose up -d postgres redis rabbitmq
```

Wait until all services report a healthy status:

```bash theme={null}
docker-compose ps
```

Start the Matcher application:

```bash theme={null}
docker-compose up -d app
```

To start all services at once:

```bash theme={null}
docker-compose up -d
```

### 4. Verify the installation

Confirm that Matcher is running by listing configuration contexts (the call returns an empty array on a fresh install):

```bash theme={null}
curl -H "Authorization: Bearer $TOKEN" http://localhost:4018/v1/contexts
```

If the call succeeds, Matcher's API and its dependencies (PostgreSQL, Redis, RabbitMQ, object storage) are reachable.

### Docker compose services

The default `docker-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:

```bash theme={null}
make dev
```

This starts Matcher with live reload enabled using Air.

## Kubernetes / helm (production)

***

Production deployments should use the official Helm chart.

### Prerequisites

* Kubernetes 1.28+
* Helm 3.12+
* `kubectl` configured for the target cluster

### 1. Create a namespace

```bash theme={null}
kubectl create namespace matcher
```

### 2. Configure values

Create a `values.yaml` file with your deployment configuration:

```yaml theme={null}
replicaCount: 2

image:
 repository: lerianstudio/matcher
 tag: "latest"
 pullPolicy: IfNotPresent

service:
 type: ClusterIP
 port: 4018

ingress:
 enabled: true
 className: nginx
 hosts:
 - host: matcher.example.com
 paths:
 - path: /
 pathType: Prefix
 tls:
 - secretName: matcher-tls
 hosts:
 - matcher.example.com

postgresql:
 external: true
 host: postgres.example.com
 port: 5432
 database: matcher
 username: matcher
 existingSecret: matcher-db-credentials
 existingSecretKey: password

redis:
 external: true
 host: redis.example.com
 port: 6379
 existingSecret: matcher-redis-credentials

rabbitmq:
 external: true
 host: rabbitmq.example.com
 port: 5672
 username: matcher
 existingSecret: matcher-rabbitmq-credentials

auth:
 enabled: true
 serviceAddress: https://auth.example.com

observability:
 enabled: true
 otelExporterEndpoint: http://otel-collector:4317

resources:
 requests:
 cpu: 500m
 memory: 512Mi
 limits:
 cpu: 2000m
 memory: 2Gi
```

### 3. Create secrets

Create Kubernetes secrets for sensitive credentials:

```bash theme={null}
kubectl create secret generic matcher-db-credentials \
 --from-literal=password=your-db-password \
 -n matcher

kubectl create secret generic matcher-redis-credentials \
 --from-literal=password=your-redis-password \
 -n matcher

kubectl create secret generic matcher-rabbitmq-credentials \
 --from-literal=password=your-rabbitmq-password \
 -n matcher
```

### 4. Install the chart

```bash theme={null}
helm install matcher oci://registry-1.docker.io/lerianstudio/matcher-helm \
 --version 2.1.1 \
 --namespace matcher \
 --values values.yaml
```

### 5. Verify the deployment

```bash theme={null}
kubectl get pods -n matcher
kubectl get svc -n matcher
kubectl logs -f deployment/matcher -n matcher
```

### Upgrading

To upgrade an existing deployment:

```bash theme={null}
helm upgrade matcher oci://registry-1.docker.io/lerianstudio/matcher-helm \
 --version 2.1.1 \
 --namespace matcher \
 --values values.yaml
```

## 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)   |

<Note>
  For multi-tenant deployment settings, see [Multi-Tenant Mode](/en/matcher/configuration/matcher-multi-tenant). For runtime configuration management, see [Runtime Configuration (Systemplane)](/en/matcher/configuration/matcher-systemplane).
</Note>

## Verify the installation

***

Validate that Matcher is operating correctly by exercising the API:

```bash theme={null}
curl -X POST http://localhost:4018/v1/contexts \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "name": "Test Context",
   "type": "1:1"
 }'
```

A successful response confirms that the API and its dependencies (database, cache, message broker, object storage) are reachable. Kubernetes liveness and readiness probes are configured at the cluster level for orchestration; you do not need to call them directly.

## Troubleshooting

***

### Common issues

<AccordionGroup>
  <Accordion title="Connection refused to PostgreSQL">
    * **Cause:** PostgreSQL is not running or unreachable.
    * **Resolution:**

    1. Verify PostgreSQL is running: `docker-compose ps postgres`
    2. Check connection values in `.env`
    3. Test connectivity: `nc -zv localhost 5432`
    4. Review logs: `docker-compose logs postgres`
  </Accordion>

  <Accordion title="Redis connection timeout">
    * **Cause:** Redis is not running or credentials are incorrect.
    * **Resolution:**

    1. Verify Redis is running: `docker-compose ps redis`
    2. Confirm `REDIS_PASSWORD`
    3. Test connectivity: `redis-cli -h localhost ping`
  </Accordion>

  <Accordion title="RabbitMQ queues not created">
    * **Cause:** RabbitMQ is still initializing or the virtual host is missing.
    * **Resolution:**

    1. Wait until RabbitMQ is healthy
    2. Access the management UI at [http://localhost:15672](http://localhost:15672)
    3. Verify `RABBITMQ_VHOST`
  </Accordion>

  <Accordion title="Authentication errors">
    * **Cause:** Auth service is unreachable or the token is invalid.
    * **Resolution:**

    1. Verify `PLUGIN_AUTH_ADDRESS`
    2. Disable auth for development: `PLUGIN_AUTH_ENABLED=false`
    3. Review auth service logs
  </Accordion>

  <Accordion title="Migration failed">
    * **Cause:** Database migrations could not be applied.
    * **Resolution:**

    1. Check migration status: `make migrate-status`
    2. Review migration logs
    3. Apply migrations manually: `make migrate-up`
    4. Inspect the `schema_migrations` table if needed
  </Accordion>

  <Accordion title="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.
  </Accordion>
</AccordionGroup>

### Viewing logs

```bash theme={null}
docker-compose logs -f app
kubectl logs -f deployment/matcher -n matcher
```

### Debug mode

Enable debug logging for additional visibility:

```bash theme={null}
LOG_LEVEL=debug docker-compose up app
```

## Next steps

***

<CardGroup cols={2}>
  <Card title="Quick start" icon="rocket" href="/en/matcher/getting-started/matcher-quick-start">
    Run your first reconciliation.
  </Card>

  <Card title="Configuration" icon="gear" href="/en/matcher/configuration/matcher-contexts-and-sources">
    Configure contexts, sources, and match rules.
  </Card>
</CardGroup>
