Audience: platform engineers, SREs, security teams, and architects who deploy, configure, secure, and operate Hyperswitch in production. This guide covers the system architecture, the complete configuration surface, the security model, day-2 operations, and the internals you need to run a payments platform reliably. Everything here is derived from the source at the referenced commit.
1. Executive Summary
Hyperswitch is an open-source (Apache 2.0) payment orchestration platform built in Rust by Juspay. The application server in this repository accepts payment API requests, applies routing and decisioning, talks to 100+ payment processors through a unified connector layer, and persists state to PostgreSQL with a Redis-backed write-through cache. It is operated alongside a PCI card vault, an encryption/key-management service, a merchant dashboard (Control Center), and client SDKs.
What you deploy is up to three Rust binaries plus their data dependencies:
| Binary | Role |
|---|---|
router |
The HTTP API server (actix-web). Handles all payment, refund, customer, routing, admin, and webhook traffic. |
scheduler (producer + consumer) |
Executes deferred and scheduled tasks via a process tracker (retries, sync, cleanups, mail). |
drainer |
Reads buffered SQL operations from Redis streams and applies them to PostgreSQL (the key-value write-back path). |
Core data dependencies: PostgreSQL (primary + optional read replica) and Redis. Optional services: card vault, encryption/key-manager service, an analytics pipeline (Kafka + ClickHouse + OpenSearch), a monitoring stack (Grafana/Loki/Tempo/Prometheus/OpenTelemetry), email (AWS SES or SMTP), and the Superposition dynamic-config service.
Important
Hyperswitch is compile-time configurable. Major capabilities are Cargo feature flags (
v1/v2,olap/oltp,payouts,frm,kv,dynamic_routing, …). The official Docker images are built with a standard feature set; if you build your own binaries, the feature flags you choose determine which endpoints and subsystems exist. This is the single most important fact to internalize before operating Hyperswitch.
2. Architecture
2.1 Request and data flow
┌──────────────────────────────────────────┐
Merchant server ───► │ router (actix-web) │
Checkout SDK ───► │ /payments /refunds /customers /routing │
Control Center ───► │ /admin /webhooks ... (olap + oltp) │
└───────────────┬──────────────────────────┘
│
┌────────────────────────────┼───────────────────────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌───────────────────┐ ┌────────────────────┐
│ PostgreSQL │◄──────►│ Redis │ │ Payment │
│ (master+replica)│ │ cache + streams │ │ connectors │
└────────▲────────┘ └─────────┬──────────┘ │ (Stripe/Adyen/...) │
│ │ └────────────────────┘
│ drains SQL ops │ enqueues tasks
┌────────┴────────┐ ┌────────┴─────────┐
│ drainer │ │ scheduler │
│ Redis stream→DB │ │ producer+consumer│
└─────────────────┘ └──────────────────┘
Side services: card vault (PCI), encryption/key-manager (KMS),
analytics (Kafka→ClickHouse→OpenSearch), monitoring (Grafana/OTel),
email (SES/SMTP), Superposition (dynamic config).
2.2 The KV (key-value) performance path
Hyperswitch can operate database writes in two modes per merchant:
- Direct SQL — the router writes straight to PostgreSQL.
- KV (write-through) — the router writes to a Redis stream (
DRAINER_STREAM, partitionednum_partitions = 64) and serves reads from the Redis cache. The drainer asynchronously reads those streams and applies the SQL to PostgreSQL. This decouples request latency from database write latency at high throughput.
The KV mode is gated by the kv/full_kv configuration and the per-merchant use_kv flag. When you run KV, the drainer must be running or buffered writes will not reach the database.
Warning
In KV mode the drainer is on the critical durability path. If the drainer stops, Redis stream entries accumulate and database state falls behind. Monitor drainer liveness and Redis stream depth.
2.3 The scheduler / process tracker
Deferred work (delayed payment sync, retries, webhook delivery retries, mandate/scheduler jobs, recovery workflows) is enqueued into a process tracker and executed by the scheduler:
- The producer scans for due tasks and pushes batches (
batch_size = 200) into a Redis queue under a producer lock. - The consumer (
consumer_group = "SCHEDULER_GROUP") pulls and executes them.
Both share a loop_interval (default 5000 ms) and a graceful shutdown interval. The scheduler also exposes a small health server (scheduler.server, default port 3000).
2.4 The connector abstraction
Every payment processor is implemented against a common set of traits (in hyperswitch_interfaces) and lives in hyperswitch_connectors / the connector modules. The router builds a connector-agnostic domain request, the connector implementation transforms it to and from the processor's wire format, and routing selects which connector handles a given transaction. This is what lets one API span 100+ processors.
2.5 The routing / decision engine (Euclid)
Routing rules are expressed in an internal rule engine (the euclid crate and its constraint-graph helpers hyperswitch_constraint_graph / kgraph_utils). It evaluates static rules (priority, volume split) and, with the dynamic_routing feature, success-rate and elimination strategies. Decision-manager configuration (3DS rules, surcharge) is managed through the /routing/decision endpoints.
2.6 Multitenancy
The server is multi-tenant. Each tenant has its own storage, accounts store, and (for analytics) its own pool, resolved per request from the multitenancy configuration. A global store holds cross-tenant tables (users, tenants). Tenant context flows through SessionState/AppState and into downstream service and gRPC headers.
2.7 Crate map
The workspace contains ~40 crates. Document and reason about them by layer, not individually:
| Layer | Crates | Responsibility |
|---|---|---|
| API server | router, router_env, router_derive |
HTTP server, route tree, request handling, logging/env, macros. |
| Async workers | scheduler, drainer |
Deferred task execution; Redis-stream → SQL drain. |
| Domain & types | hyperswitch_domain_models, api_models, common_enums, common_types, common_utils |
Business/domain models, request/response models, shared enums/types/utilities. |
| Connectors | hyperswitch_connectors, hyperswitch_interfaces |
Processor integrations and the trait contracts they implement. |
| Storage | diesel_models, storage_impl, redis_interface |
Diesel ORM models, storage backends, Redis client. |
| Routing/decisioning | euclid, euclid_macros, euclid_wasm, hyperswitch_constraint_graph, kgraph_utils |
Rule DSL, constraint graph, WASM rule evaluation. |
| Security/PII | masking, cards, pm_auth |
PII masking, card masking/validation, payment-method (open-banking) auth. |
| Platform services | external_services, payment_methods, payment_link, currency_conversion, subscriptions, injector, analytics |
KMS/email/file storage, payment-method logic, hosted links, forex, subscriptions, analytics. |
| Tooling / spec | openapi, smithy, smithy-core, smithy-generator, config_importer, hsdev, test_utils, connector_configs |
OpenAPI generation, Smithy models, config import, dev tooling, test harness. |
Note
The clap-based binaries you may notice (
config_importer,hsdev, the drainer/scheduler settings parsers,test_utils/newman_runner) are operations and development tools, not a user-facing CLI. Hyperswitch is operated through its config files and HTTP API, not a command-line surface.
3. System Requirements
| Component | Requirement |
|---|---|
| OS | Linux (production); macOS/WSL2 for local dev. Distributed as Docker images. |
| Language/runtime | Rust (built binaries; the official images are prebuilt — no toolchain needed to run). Nightly toolchain only required to build/format from source. |
| Database | PostgreSQL (a primary; an optional read replica). Schema managed by Diesel migrations. |
| Cache/streams | Redis 7+ (single or clustered). Required for caching, the scheduler queue, and (in KV mode) the drainer streams. |
| Card vault | hyperswitch-card-vault for production PCI card storage. A local Postgres-backed mock exists for development only. |
| Encryption | hyperswitch-encryption-service (key manager) for production key management. |
| Optional analytics | Kafka, ClickHouse, OpenSearch for the analytics/OLAP features. |
| Optional monitoring | Grafana, Loki, Tempo, Prometheus, OpenTelemetry collector. |
| Email (optional) | AWS SES or an SMTP server (only with the email feature). |
4. Configuration Mastery
4.1 How configuration is loaded
Hyperswitch is configured with TOML files. The reference file is config/config.example.toml, which enumerates every available option with inline comments and defaults — treat it as the canonical configuration dictionary. Deployments use environment-specific files:
| File | Used for |
|---|---|
config/config.example.toml |
Reference only — copy from it. |
config/development.toml |
Local development defaults. |
config/docker_compose.toml |
The Docker Compose deployment (this is where you set admin_api_key, connector URLs, etc.). |
config/dashboard.toml |
Control Center feature toggles. |
config/deployments/* |
Per-environment deployment configs. |
Important
The drainer and scheduler validate their configuration on startup and panic on invalid config (
conf.validate()), failing fast rather than running misconfigured. Validate config changes in a non-production environment first.
4.2 Core sections
The following are the operationally critical sections. All defaults below are from config.example.toml.
[server] — HTTP server
| Key | Default | Purpose |
|---|---|---|
port |
8080 |
Listen port. |
host |
127.0.0.1 |
Bind address (set to 0.0.0.0 in containers). |
workers |
10 |
Worker threads (defaults to physical CPU count). |
request_body_limit |
32768 |
Max request body in bytes (32 kB). |
shutdown_timeout |
30 |
Graceful shutdown grace period (seconds). |
keep_alive |
5 |
Keep-alive timeout (seconds). |
client_request_timeout |
5000 |
Client request timeout (ms). |
[server.tls] enables HTTPS directly (port = 8081, private_key, certificate). In most deployments TLS is terminated at a load balancer instead.
[master_database] / [replica_database] — PostgreSQL
| Key | Default | Purpose |
|---|---|---|
username / password |
— | Credentials (use a base64 KMS-encrypted value when KMS is enabled). |
host / port |
localhost / 5432 |
Connection target. |
dbname |
hyperswitch_db |
Database name. |
pool_size |
5 |
Open connections to keep. |
connection_timeout |
10 |
Connection timeout (seconds). |
queue_strategy |
Fifo |
bb8 pool queue strategy. |
The replica is used for read traffic; point it at a read replica in production.
[redis] — cache and streams
Key options include host/port (127.0.0.1/6379), pool_size (5), default_ttl (300 s), default_hash_ttl (900 s), reconnect_max_attempts (5), default_command_timeout (30 s), max_in_flight_commands (5000), and max_failure_threshold_seconds (5 — the maximum time Redis may be unreachable before the server shuts down). Set use_legacy_version = true only for Redis < 6 / RESPv2. For clustered Redis use the clustered_redis deployment profile.
[secrets] — encryption and auth secrets
| Key | Purpose |
|---|---|
master_enc_key |
Master encryption key used to encrypt each merchant's encryption key. Must be 32 bytes. |
admin_api_key |
The admin API key for system/admin operations. |
jwt_secret |
Secret for signing/verifying user JWTs. |
Caution
The example values (
sample_key,test_admin,secret) are placeholders. Never run any non-local environment with example secrets. In production, source these through KMS rather than plaintext config.
[locker] — card vault
| Key | Default | Purpose |
|---|---|---|
host |
"" |
Card vault (locker) host. |
mock_locker |
true |
Emulate a locker in Postgres. Development only. |
locker_enabled |
true |
Whether cards are saved to the locker. |
ttl_for_storage_in_secs |
220752000 |
Stored-card TTL. |
Warning
mock_locker = truestores card data in your application Postgres and is not PCI-compliant. For any real card data, run the dedicatedhyperswitch-card-vaultservice and setmock_locker = falsewith a realhost.
[key_manager] — encryption service
url (default http://localhost:5000) points at the encryption/key-management service. use_legacy_key_store_decryption controls upgrade compatibility — set false for new deployments, true only when upgrading from older versions. mTLS to the key manager is available with the keymanager_mtls build feature (cert, ca).
[scheduler] and [drainer]
The scheduler config defines the stream names, loop_interval (5000 ms), producer batch_size (200), consumer group, and the scheduler health server ([scheduler.server], port 3000). The drainer config defines stream_name (DRAINER_STREAM), num_partitions (64), max_read_count (100), and loop/shutdown intervals.
[api_keys], [eph_key], [webhooks], [refund], [cors], [user]
| Section | Notable keys |
|---|---|
[api_keys] |
hash_key — 32-byte hex (64 hex chars) used to hash merchant API keys before storage. |
[eph_key] |
validity — ephemeral key lifetime in hours (default 1). |
[webhooks] |
outgoing_enabled (true), redis_lock_expiry_seconds (180). |
[refund] |
max_attempts (10), max_age (365 days). |
[cors] |
origins, allowed_methods, max_age (30 s), wildcard_origin (false). |
[user] |
password_validity_in_days (90), 2FA expiry, force_two_factor_auth (false), totp_issuer_name. |
[merchant_id_auth] |
merchant_id_auth_enabled (false) — requires x-merchant-id on payment endpoints when on. |
[connectors] and [pm_filters.*]
The [connectors] section sets the base URL for every processor (e.g. adyen.base_url, stripe.base_url, aci.base_url). These are not optional — an empty/invalid base URL makes requests to that connector fail. [pm_filters.<connector>] restricts which payment methods a connector offers by currency and country:
[pm_filters.stripe]
paypal = { currency = "USD,INR", country = "US" }
[log] and [log.telemetry]
Logging goes to file ([log.file], default off) and/or console ([log.console], default on). Levels: TRACE/DEBUG/INFO/WARN/ERROR/OFF. filtering_directive sets per-crate levels (e.g. WARN,router=INFO,reqwest=INFO). [log.telemetry] configures OpenTelemetry traces/metrics export (otel_exporter_otlp_endpoint default http://localhost:4317, sampling_rate 0.1, optional AWS X-Ray generator).
[email]
Only active with the email feature. Selects an active client (SES or SMTP), the sender address, and SES role configuration. The local Docker setup uses Mailhog for testing.
Analytics, forex, Superposition
[analytics] selects the analytics source (clickhouse) and [events] the event sink (kafka); enable them in development.toml/docker_compose.toml for the OLAP profile. [forex_api] holds the currency-conversion API keys and cache TTLs. Superposition provides dynamic, hot-reloadable configuration via its client.
5. Security
Payments software is a high-value target. Hyperswitch's security posture rests on layered key management, PII masking, a dedicated card vault, and explicit authentication tiers.
5.1 Authentication & authorization tiers
| Tier | Mechanism | Scope |
|---|---|---|
| Admin API key | Static config value (secrets.admin_api_key) |
System operations: create merchant/connector accounts. Highly confidential. |
| Merchant secret (API) key | api-key header; stored hashed via api_keys.hash_key |
Server-to-server merchant operations. |
| Publishable key | pk_{env}_{uuid} |
Client-side calls; safe to expose. |
| Ephemeral key | Short-lived, scoped to one customer | Client access to a specific customer object. |
| JWT bearer | Authorization: Bearer …, signed with jwt_secret |
Control Center / user sessions; optional 2FA (TOTP). |
Optionally, merchant_id_auth_enabled requires an x-merchant-id header on payment endpoints, and the partial-auth build feature adds checksum-based key authentication.
5.2 Key management and encryption
- A master encryption key (
secrets.master_enc_key, 32 bytes) encrypts each merchant's data-encryption key (envelope encryption). - In production, secrets and keys are resolved through the encryption/key-manager service (
[key_manager]), not plaintext config. Database passwords can be supplied as base64 KMS-encrypted values. - mTLS to the key manager is available (
keymanager_mtls).
5.3 Cardholder data and PCI scope
- Card data belongs in the card vault (
hyperswitch-card-vault), a separate PCI-scoped service. Setlocker.mock_locker = falseand a reallocker.hostin production. - The
maskingcrate wraps PII/secret values so they are redacted in logs and debug output; thecardscrate handles card masking and validation. - The recommended integration keeps raw card data off your servers entirely by collecting it through the checkout SDK (client-side tokenization).
Caution
The biggest self-hosting security mistakes are: (1) running with
mock_locker = trueand real cards, (2) shipping examplesecrets, and (3) exposing the admin API key. Eliminate all three before going live.
5.4 Transport, CORS, and network
- Enable TLS at the edge (load balancer) or directly via
[server.tls]. - Restrict
[cors].originsto your known front ends; avoidwildcard_origin = true. - Use the
[proxy]settings to route outbound connector traffic through an egress proxy, withbypass_proxy_hostsfor internal hosts. - Verify outgoing-webhook signatures on your receiver, and verify the HMAC
signatureon payment redirect URLs using the profile'spayment_response_hash_key.
5.5 Reporting vulnerabilities
Per the project's security policy, report vulnerabilities privately via GitHub Security Advisories on the repository or by email to [email removed] — never via public issues.
6. Operations
6.1 Running the services
Production runs the three binaries as separate workloads (the official images are hyperswitch-router, hyperswitch-producer, hyperswitch-consumer, hyperswitch-drainer, published under docker.juspay.io/juspaydotin/…):
| Workload | Notes |
|---|---|
router |
Stateless; scale horizontally behind a load balancer. Each replica needs DB + Redis access. |
producer + consumer |
Run the scheduler when you use deferred tasks (retries, delayed sync, mail). |
drainer |
Required when KV mode is enabled. Scale with stream partitions. |
For Kubernetes, use hyperswitch-helm; for a full provisioned stack, hyperswitch-suite (Terraform).
6.2 Database migrations
The schema is managed by Diesel migrations in migrations/ (and v2_migrations/ / v2_compatible_migrations/ for the v2 schema). In Docker Compose a migration-runner service applies them before the server starts. Apply migrations as a deploy step and ensure they complete before routing traffic to a new version.
Important
Database-schema, API-contract, and configuration changes are tracked explicitly in the project (the
M-database-changes,M-api-contract-changes,M-configuration-changeslabels). Treat any release carrying these as requiring a coordinated migration + config rollout.
6.3 Health checks
| Endpoint | Use |
|---|---|
GET /health |
Liveness — the process is up. |
GET /health/ready |
Readiness/deep health — verifies dependencies (DB, Redis, etc.). |
Wire /health to liveness probes and /health/ready to readiness probes so traffic only reaches replicas with healthy dependencies. The scheduler exposes its own health server on scheduler.server.port (3000).
6.4 Observability
- Logs: console (JSON via
log_format = "json") and/or file, with per-crate filtering directives. - Metrics & traces: OpenTelemetry export (
[log.telemetry]) to an OTLP endpoint; optional AWS X-Ray. ThemonitoringDocker profile bundles Grafana, Loki, Tempo, Prometheus, and an OTel collector. - Background metrics: collected on an interval (
bg_metrics_collection_interval_in_secs, 15 s). - Analytics: with the OLAP profile, events flow Kafka → ClickHouse, queryable via OpenSearch dashboards; Hyperswitch's own dashboards surface payment analytics.
6.5 Capacity and tuning levers
| Lever | Where | Effect |
|---|---|---|
server.workers |
[server] |
Request concurrency per replica. |
master_database.pool_size |
[master_database] |
DB connection capacity. |
redis.pool_size, max_in_flight_commands |
[redis] |
Redis throughput/backpressure. |
KV mode + drainer num_partitions |
[drainer] |
Write throughput decoupled from DB latency. |
scheduler.producer.batch_size, loop_interval |
[scheduler] |
Deferred-task throughput vs. latency. |
6.6 Backup, DR, and rollback
- State of record is PostgreSQL — back it up (and the card vault's storage) with point-in-time recovery. Redis is a cache/queue; in KV mode, drain it fully before any maintenance that could lose stream data.
- Rollback a release by deploying the previous image; do not run a newer schema against an older binary. Because migrations are forward-oriented, plan rollbacks at the application layer and keep migrations backward-compatible across one release where possible.
7. Troubleshooting (Operational)
Symptom: server exits shortly after start with a config error
Cause: Invalid configuration — the drainer/scheduler validate() panics on bad config; the router fails to construct required clients (encryption, store).
Resolution: Check the startup logs for the specific section. Validate the active TOML against config.example.toml; confirm DB/Redis/key-manager reachability.
Symptom: Redis streams growing, DB state lagging
Cause: KV mode is on but the drainer is down or under-provisioned. Resolution: Restart/scale the drainer; verify it can reach Redis and PostgreSQL. Monitor stream depth as a first-class alert.
Symptom: Failed to fetch currency exchange rate / HE_00 / Could not acquire the lock for cache entry
Cause: Stale forex cache lock in Redis after enabling currency conversion.
Resolution: redis-cli del "{forex_cache}_lock" and restart the app. Ensure valid [forex_api] keys.
Symptom: connector calls fail immediately
Cause: Missing/invalid [connectors].<connector>.base_url or bad credentials.
Resolution: Set the correct base URL (sandbox vs. live) and re-enter credentials in the Control Center / via the connector-account API.
Symptom: requires_customer_action payments never complete
Cause: Customers cannot reach the authentication redirect, or return_url is unreachable.
Resolution: Ensure the redirect URL from confirm is presented and return_url is publicly reachable; verify the redirect HMAC.
Symptom: Redis unreachable causes the server to shut down
Cause: Redis was down longer than redis.max_failure_threshold_seconds (min 5 s).
Resolution: Restore Redis; the server self-terminates by design to avoid serving in an inconsistent state. Run Redis with HA (clustered profile) to avoid single-node outages.
8. Quick Reference
8.1 Default ports
| Port | Service |
|---|---|
8080 |
router API |
8081 |
router HTTPS ([server.tls]) |
3000 |
scheduler health server / Grafana |
9000 |
Control Center |
9050 |
web SDK · 9060 demo app |
5432 |
PostgreSQL · 6379 Redis |
8123 |
ClickHouse · 9200 OpenSearch · 8090 Kafka UI |
5000 |
key-manager / encryption service |
8.2 Docker Compose profiles
| Profile | Adds |
|---|---|
| (default) | router, Postgres, Redis, Control Center, web SDK, migration runner |
scheduler |
producer + consumer |
monitoring |
Grafana, Loki, Tempo, Prometheus, OTel, RedisInsight |
olap |
Kafka, ClickHouse, OpenSearch, Kafka UI |
full_setup |
Mailhog and extras |
clustered_redis / full_kv |
Redis cluster · KV-mode services |
8.3 Feature flags that change behavior
| Flag | Effect |
|---|---|
v1 / v2 |
Which API version's endpoints are compiled. |
olap |
Dashboard/admin/analytics/routing/user endpoints. |
oltp |
Payment/refund/customer transaction endpoints. |
payouts |
Payout endpoints and routing. |
frm |
Fraud-management hooks. |
email |
Email client and email flows. |
kv / full_kv |
Key-value write-through mode (requires drainer). |
dynamic_routing |
Success-rate / elimination routing. |
revenue_recovery |
Revenue-recovery workflows (v2). |
keymanager_mtls, partial-auth |
mTLS to key manager; checksum auth. |
8.4 Config files
| File | Role |
|---|---|
config/config.example.toml |
Canonical reference (every option). |
config/development.toml |
Local dev. |
config/docker_compose.toml |
Docker Compose deployment (set admin key, connectors). |
config/dashboard.toml |
Control Center feature toggles. |
config/deployments/* |
Per-environment configs. |
8.5 Key config sections at a glance
[server] · [server.tls] · [proxy] · [key_manager] · [master_database] · [replica_database] · [redis] · [secrets] · [locker] · [api_keys] · [eph_key] · [webhooks] · [refund] · [cors] · [user] · [merchant_id_auth] · [connectors] · [pm_filters.*] · [scheduler] · [drainer] · [log.*] · [email] · [forex_api] · [analytics] / [events].