Introduction to Kong API Gateway
Kong is an open-source, cloud-native API gateway built on top of OpenResty (Nginx + Lua modules). It acts as middleware between clients and backend services, handling cross-cutting concerns like authentication, rate limiting, logging, and transformations at the gateway layer — before requests ever reach your microservices. Kong supports both traditional REST APIs and modern GraphQL, gRPC, and WebSocket protocols, making it one of the most versatile gateway solutions available today.
What Makes Kong Different
Unlike traditional reverse proxies that require configuration file changes and reloads, Kong is fully programmable via a RESTful Admin API. Every route, service, plugin, and consumer is a dynamic entity you can create, update, or delete without downtime. This declarative, database-backed approach means your entire gateway configuration can be version-controlled, CI/CD-pipelined, and even managed through GitOps workflows using Kong's decK tooling.
Core Concepts and Architecture
Kong's data model revolves around a few key entities:
- Services — Abstract representations of your upstream APIs (the actual backend services)
- Routes — Rules that match incoming requests and map them to a Service (based on paths, headers, methods, or hosts)
- Consumers — Identified API clients or users who consume your APIs
- Plugins — Extensions that intercept requests/responses to add functionality (auth, rate limiting, logging, etc.)
- Upstreams — Groups of target backend servers with load balancing algorithms
Kong can run in two modes: traditional database-backed mode (using PostgreSQL, MySQL, or Cassandra) where configuration is stored in a database and changes are applied via the Admin API, and DB-less mode (declarative configuration loaded from YAML/JSON files) ideal for immutable infrastructure and Kubernetes deployments.
Why Kong Matters in Modern Architectures
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →In microservices and distributed architectures, every service typically needs authentication, rate limiting, request logging, CORS handling, and circuit breaking. Implementing these in every single service leads to code duplication, inconsistency, and operational nightmares. Kong centralizes these cross-cutting concerns at the edge, allowing backend developers to focus purely on business logic. The result is dramatically simpler services, consistent security policies, and a single control plane for all ingress traffic.
Key benefits that make Kong indispensable:
- Zero-downtime configuration — Add routes, plugins, and services on the fly without reloads
- Plugin ecosystem — Over 100 plugins (both official and community-contributed) for authentication, security, transformation, analytics, and serverless functions
- Polyglot protocol support — REST, gRPC, GraphQL, WebSocket, TCP streams all through a single gateway
- Horizontal scalability — Stateless nodes that can scale out behind a load balancer
- Observability — Built-in Prometheus metrics, logging to multiple destinations, and distributed tracing support
- Kubernetes-native — Kong Ingress Controller extends Kubernetes ingress with Kong plugins and policies
Setting Up Kong: Complete Installation Walkthrough
Option 1: Docker Compose (Recommended for Development)
This is the fastest way to get a fully functional Kong environment with PostgreSQL and the Kong Manager UI. Create a docker-compose.yml file:
version: '3.8'
services:
kong-database:
image: postgres:15-alpine
environment:
POSTGRES_USER: kong
POSTGRES_DB: kong
POSTGRES_PASSWORD: kongpass
volumes:
- kong-db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "kong"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
kong-migrations:
image: kong:3.6
depends_on:
kong-database:
condition: service_healthy
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: kong-database
KONG_PG_USER: kong
KONG_PG_PASSWORD: kongpass
KONG_PG_DATABASE: kong
command: kong migrations bootstrap
restart: on-failure
kong:
image: kong:3.6
depends_on:
kong-database:
condition: service_healthy
kong-migrations:
condition: service_completed_successfully
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: kong-database
KONG_PG_USER: kong
KONG_PG_PASSWORD: kongpass
KONG_PG_DATABASE: kong
KONG_ADMIN_LISTEN: 0.0.0.0:8001
KONG_ADMIN_GUI_URL: http://localhost:8002
KONG_PROXY_ACCESS_LOG: /dev/stdout
KONG_ADMIN_ACCESS_LOG: /dev/stdout
KONG_PROXY_ERROR_LOG: /dev/stderr
KONG_ADMIN_ERROR_LOG: /dev/stderr
ports:
- "8000:8000" # Proxy port (incoming API traffic)
- "8001:8001" # Admin API port
- "8002:8002" # Kong Manager GUI
healthcheck:
test: ["CMD", "kong", "health"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
kong-db-data:
Start everything with:
docker-compose up -d
Verify Kong is running by checking the admin endpoint:
curl -s http://localhost:8001/ | jq
Expected response includes Kong's version, timers, and configuration details. The Kong Manager UI is accessible at http://localhost:8002 for visual management.
Option 2: Native Installation on Ubuntu/Debian
For production or bare-metal installations, install Kong directly on the host system:
# Download the Kong package
curl -Lo kong-enterprise-edition-3.6.0.0.deb https://download.konghq.com/gateway-3.x-ubuntu-22.04/pool/kong-3.6.0.0-amd64.deb
# Install the package
sudo dpkg -i kong-enterprise-edition-3.6.0.0.deb
# Verify installation
kong version
Configure Kong by editing /etc/kong/kong.conf:
# /etc/kong/kong.conf
database = postgres
pg_host = 127.0.0.1
pg_port = 5432
pg_user = kong
pg_password = your_secure_password
pg_database = kong
admin_listen = 0.0.0.0:8001
proxy_listen = 0.0.0.0:8000, 0.0.0.0:8443 ssl
# Enable declarative configuration for DB-less mode (alternative)
# database = off
# declarative_config = /etc/kong/kong.yml
Run database migrations and start Kong:
# Bootstrap the database
sudo kong migrations bootstrap -c /etc/kong/kong.conf
# Start Kong
sudo kong start -c /etc/kong/kong.conf
Option 3: DB-less Mode with Declarative Configuration
DB-less mode loads the entire configuration from a YAML or JSON file at startup. Perfect for Kubernetes, immutable infrastructure, and GitOps workflows. Create kong-declarative.yml:
# kong-declarative.yml
_format_version: "3.0"
services:
- name: example-service
url: http://httpbin.org
routes:
- name: example-route
paths:
- /api/v1/external
methods:
- GET
- POST
strip_path: true
plugins:
- name: rate-limiting
config:
minute: 5
policy: local
- name: key-auth
config:
key_names:
- apikey
hide_credentials: true
consumers:
- username: test-user
custom_id: user-123
# Apply key-auth credentials to the consumer
keyauth_credentials:
- consumer: test-user
key: my-secret-api-key-2024
upstreams:
- name: example-upstream
targets:
- target: 10.0.1.100:8080
weight: 100
- target: 10.0.1.101:8080
weight: 50
Start Kong in DB-less mode:
docker run -d --name kong-dbless \
-e KONG_DATABASE=off \
-e KONG_DECLARATIVE_CONFIG=/etc/kong/kong-declarative.yml \
-v $(pwd)/kong-declarative.yml:/etc/kong/kong-declarative.yml \
-p 8000:8000 \
-p 8001:8001 \
kong:3.6
Configuring Services, Routes, and Upstreams
Creating Your First Service and Route via Admin API
The Admin API is the primary interface for dynamic configuration. Here's how to define a service pointing to an upstream API and create a route that exposes it:
# Create a Service (abstract backend)
curl -X POST http://localhost:8001/services \
-H "Content-Type: application/json" \
-d '{
"name": "user-service",
"url": "http://user-api.internal:3000",
"connect_timeout": 60000,
"read_timeout": 60000,
"write_timeout": 60000,
"retries": 3
}'
# Create a Route attached to that service
curl -X POST http://localhost:8001/routes \
-H "Content-Type: application/json" \
-d '{
"name": "user-api-route",
"service": { "name": "user-service" },
"paths": ["/api/users"],
"methods": ["GET", "POST", "PUT", "DELETE"],
"strip_path": true,
"protocols": ["http", "https"]
}'
Now requests to http://localhost:8000/api/users will be proxied to http://user-api.internal:3000/users (note the path stripping). The strip_path parameter removes the matching prefix before forwarding.
Advanced Route Matching
Routes support sophisticated matching criteria beyond paths. You can match on headers, query parameters, hosts, and even combine multiple conditions:
# Route matching on specific host and header
curl -X POST http://localhost:8001/routes \
-H "Content-Type: application/json" \
-d '{
"name": "mobile-gateway-route",
"service": { "name": "user-service" },
"hosts": ["mobile.example.com"],
"headers": {
"X-Client-Version": ["2.0", "2.1"]
},
"methods": ["GET"]
}'
# Route matching with regex path
curl -X POST http://localhost:8001/routes \
-H "Content-Type: application/json" \
-d '{
"name": "versioned-api-route",
"service": { "name": "user-service" },
"paths": ["/api/v[1-3]/users"],
"regex_priority": 10
}'
Configuring Load Balancing with Upstreams
For production deployments with multiple backend instances, use Upstreams and Targets instead of a static URL in the Service definition:
# Create an Upstream with health checks
curl -X POST http://localhost:8001/upstreams \
-H "Content-Type: application/json" \
-d '{
"name": "user-service-upstream",
"algorithm": "least-connections",
"hash_on": "none",
"healthchecks": {
"active": {
"http_path": "/health",
"healthy": {
"interval": 10,
"successes": 3
},
"unhealthy": {
"interval": 5,
"timeouts": 3,
"http_failures": 3
}
},
"passive": {
"healthy": {
"successes": 5
},
"unhealthy": {
"http_failures": 3,
"timeouts": 3
}
}
}
}'
# Add targets (backend instances)
curl -X POST http://localhost:8001/upstreams/user-service-upstream/targets \
-H "Content-Type: application/json" \
-d '{
"target": "192.168.1.10:3000",
"weight": 100
}'
curl -X POST http://localhost:8001/upstreams/user-service-upstream/targets \
-H "Content-Type: application/json" \
-d '{
"target": "192.168.1.11:3000",
"weight": 100
}'
# Update the Service to use the Upstream
curl -X PATCH http://localhost:8001/services/user-service \
-H "Content-Type: application/json" \
-d '{
"host": "user-service-upstream"
}'
Kong supports multiple load balancing algorithms: round-robin, least-connections, consistent-hashing, and latency-based (enterprise edition). Active and passive health checks ensure traffic is only routed to healthy instances.
Working with Plugins: The Power of Kong
Plugins are where Kong truly shines. They can be applied globally, per-service, per-route, or per-consumer. The plugin execution order follows a deterministic priority system. Let's explore the most critical plugins.
Authentication Plugins
Key Authentication (API Keys)
The simplest form of authentication — clients pass an API key in a header or query parameter:
# Enable key-auth plugin on a route
curl -X POST http://localhost:8001/routes/user-api-route/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "key-auth",
"config": {
"key_names": ["apikey", "x-api-key"],
"hide_credentials": true,
"run_on_preflight": true
}
}'
# Create a Consumer (the API client/user)
curl -X POST http://localhost:8001/consumers \
-H "Content-Type: application/json" \
-d '{
"username": "external-partner-org",
"custom_id": "partner-org-001",
"tags": ["partner", "tier-bronze"]
}'
# Issue credentials for that consumer
curl -X POST http://localhost:8001/consumers/external-partner-org/key-auth \
-H "Content-Type: application/json" \
-d '{
"key": "sk-live-8a7b9c3d4e5f6a7b8c9d0e1f2a3b4c5d"
}'
Now any request to the route without a valid API key in the apikey or x-api-key header will receive a 401 Unauthorized response.
JWT Authentication
For more secure, token-based authentication with claims:
# Enable JWT plugin
curl -X POST http://localhost:8001/routes/user-api-route/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "jwt",
"config": {
"claims_to_verify": ["exp", "nbf"],
"maximum_expiration": 0,
"header_names": ["Authorization"],
"cookie_names": ["jwt_token"],
"anonymous": "anonymous-user-id"
}
}'
# Create JWT credentials for a consumer
curl -X POST http://localhost:8001/consumers/external-partner-org/jwt \
-H "Content-Type: application/json" \
-d '{
"algorithm": "HS256",
"secret": "your-256-bit-secret-key-here-minimum-32-characters",
"key": "jwt-key-identifier-001"
}'
OAuth 2.0 Integration
Kong can act as an OAuth 2.0 authorization server or integrate with existing providers:
# OAuth2 plugin configuration for Authorization Code Grant
curl -X POST http://localhost:8001/services/auth-service/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "oauth2",
"config": {
"scopes": ["read", "write", "admin"],
"mandatory_scope": false,
"token_expiration": 3600,
"enable_authorization_code": true,
"enable_client_credentials": true,
"enable_implicit_grant": false,
"enable_password_grant": false,
"accept_http_if_already_terminated": true,
"provision_key": "oauth2_provision_key_2024",
"global_credentials": false
}
}'
Rate Limiting Plugin
Protect your backend services from abuse with flexible rate limiting policies:
# Apply rate limiting at the route level
curl -X POST http://localhost:8001/routes/user-api-route/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "rate-limiting",
"config": {
"minute": 100,
"hour": 5000,
"second": 10,
"policy": "cluster",
"redis": {
"host": "redis-cluster.internal",
"port": 6379,
"database": 0,
"timeout": 2000
},
"fault_tolerant": true,
"hide_client_headers": false,
"limit_by": "consumer"
}
}'
The policy parameter determines the rate limiting strategy:
local— Counters stored in-memory on each Kong node (inaccurate but fast)cluster— Counters synchronized across nodes via Redis or database (accurate, requires central store)redis— Counters stored directly in Redis with atomic operations (most accurate, production recommended)
Request and Response Transformation
Request Transformer
curl -X POST http://localhost:8001/routes/user-api-route/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "request-transformer",
"config": {
"remove": {
"headers": ["x-internal-token", "x-debug-info"],
"querystring": ["internal_id"],
"body": ["sensitive_field"]
},
"rename": {
"headers": [
{"old_name": "x-legacy-header", "new_name": "x-modern-header"}
]
},
"add": {
"headers": ["x-routed-by:kong", "x-gateway-timestamp:$timestamp"],
"querystring": ["gateway=active"],
"body": ["injected_by=gateway"]
},
"append": {
"headers": ["x-proxy-chain:gateway-level"]
}
}
}'
Response Transformer
curl -X POST http://localhost:8001/routes/user-api-route/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "response-transformer",
"config": {
"remove": {
"headers": ["x-powered-by", "server", "x-aspnet-version"],
"json": ["internal_id", "debug_stack_trace"]
},
"add": {
"headers": ["x-response-time:$upstream_response_time", "x-kong-node:$node_name"],
"json": ["gateway_version:3.6.0"]
}
}
}'
CORS Configuration
curl -X POST http://localhost:8001/services/user-service/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "cors",
"config": {
"origins": ["https://app.example.com", "https://admin.example.com"],
"methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
"headers": ["Content-Type", "Authorization", "X-Requested-With"],
"exposed_headers": ["x-custom-header", "x-rate-limit-remaining"],
"credentials": true,
"max_age": 3600,
"preflight_continue": false
}
}'
Logging and Observability Plugins
# Enable comprehensive HTTP logging
curl -X POST http://localhost:8001/services/user-service/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "http-log",
"config": {
"http_endpoint": "http://log-collector.internal:8080/ingest",
"method": "POST",
"timeout": 5000,
"keepalive": 60000,
"retry_count": 3,
"headers": {
"Content-Type": "application/json",
"x-log-source": "kong-gateway"
}
}
}'
# Prometheus metrics endpoint
curl -X POST http://localhost:8001/services/user-service/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "prometheus",
"config": {
"per_consumer": true,
"status_code_metrics": true,
"latency_metrics": true,
"bandwidth_metrics": true,
"upstream_health_metrics": true
}
}'
Bot Detection and IP Restriction
# Bot detection plugin
curl -X POST http://localhost:8001/routes/user-api-route/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "bot-detection",
"config": {
"whitelist": ["GoogleBot", "BingBot"],
"blacklist": ["curl", "python-requests", "nmap"],
"allow": [".*chrome.*", ".*firefox.*"]
}
}'
# IP restriction (allow-list approach)
curl -X POST http://localhost:8001/routes/admin-route/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "ip-restriction",
"config": {
"allow": ["10.0.0.0/8", "172.16.0.0/12", "192.168.1.0/24"],
"deny": null,
"status": 403,
"message": "Your IP address is not authorized to access this resource"
}
}'
Advanced Kong Configuration Patterns
Consumer Groups and Tiered Access
For multi-tier API access, combine consumers with rate limiting and ACL plugins:
# Create consumer group plugin configuration
# Bronze tier - limited access
curl -X POST http://localhost:8001/consumers/external-partner-org/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "rate-limiting-advanced",
"config": {
"identifier": "consumer",
"window": {"size": 60, "type": "sliding"},
"limit": ["minute"],
"namespace": "bronze-tier",
"sync_rate": 10,
"strategy": "redis",
"dictionary_name": "kong_rate_limiting_counters"
}
}'
# Apply tier-specific limits at the service level
curl -X POST http://localhost:8001/services/user-service/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "rate-limiting-advanced",
"config": {
"identifier": "consumer",
"window": {"size": 60, "type": "sliding"},
"limit": [100],
"namespace": "silver-tier",
"strategy": "redis",
"redis": {
"host": "redis-cluster.internal",
"port": 6379
}
}
}'
Chaining Multiple Plugins with Priority Control
Plugins execute in order based on their priority value (higher runs first). The default execution order is: authentication → authorization → transformation → analytics. You can override this:
# Create a custom plugin ordering by applying plugins in sequence
# First, ensure authentication runs (priority 1000+)
# Then authorization (priority 800-999)
# Then transformations (priority 700-799)
# Finally logging (priority 1-699)
# Example: Apply plugins with explicit ordering
curl -X POST http://localhost:8001/routes/user-api-route/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "correlation-id",
"config": {
"header_name": "x-correlation-id",
"generator": "uuid#counter",
"echo_downstream": true
}
}'
Service Mesh Integration with Kong Mesh
Kong can operate as both an ingress gateway and a service mesh sidecar. In Kubernetes environments, the Kong Ingress Controller bridges these worlds:
# Example KongIngress resource for Kubernetes
apiVersion: configuration.konghq.com/v1
kind: KongIngress
metadata:
name: user-service-ingress-config
annotations:
kubernetes.io/ingress.class: kong
route:
methods:
- GET
- POST
strip_path: true
preserve_host: true
protocols:
- http
- https
regex_priority: 10
upstream:
algorithm: least-connections
healthchecks:
active:
http_path: /health
interval: 10
successes: 3
Global Plugins vs. Scoped Plugins
Global plugins apply to every request across all services and routes:
# Global rate limiting across the entire gateway
curl -X POST http://localhost:8001/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "rate-limiting",
"config": {
"minute": 200,
"policy": "local"
}
}'
# Global correlation ID for all requests
curl -X POST http://localhost:8001/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "correlation-id",
"config": {
"header_name": "x-global-correlation-id",
"generator": "uuid",
"echo_downstream": true
}
}'
Managing Kong with deck (Declarative Configuration CLI)
deck is Kong's official CLI tool for managing configuration as code. It allows you to sync, diff, and manage Kong configurations declaratively — essential for CI/CD pipelines.
Installation and Basic Usage
# Install deck
curl -sL https://github.com/kong/deck/releases/download/v1.31.0/deck_1.31.0_linux_amd64.tar.gz | tar xz
sudo mv deck /usr/local/bin/
# Initialize a deck configuration directory
deck init --path ./kong-gateway-config
# Sync configuration from a file to Kong
deck sync --state kong-declarative.yml \
--kong-addr http://localhost:8001
# Diff between local config and running Kong
deck diff --state kong-declarative.yml \
--kong-addr http://localhost:8001
# Dump current Kong configuration to a file
deck dump --kong-addr http://localhost:8001 \
--format yaml \
--output-file kong-current-state.yml
# Validate configuration file
deck validate --state kong-declarative.yml
Example deck Configuration File
# deck-config.yaml
_format_version: "3.0"
_info:
description: Production Gateway Configuration
version: "1.0.0"
services:
- name: orders-service
url: http://orders-api.internal:8080
connect_timeout: 30000
read_timeout: 60000
write_timeout: 60000
retries: 3
routes:
- name: orders-rest-routes
paths:
- /api/v2/orders
methods:
- GET
- POST
- PUT
strip_path: true
protocols: ["http", "https"]
plugins:
- name: jwt
config:
claims_to_verify: ["exp", "iss"]
header_names: ["Authorization"]
- name: rate-limiting
config:
minute: 50
policy: cluster
redis:
host: redis-prod.internal
port: 6379
- name: inventory-service
url: http://inventory-api.internal:8080
routes:
- name: inventory-routes
paths:
- /api/v2/inventory
methods:
- GET
strip_path: true
consumers:
- username: retail-partner
custom_id: partner-retail-001
tags: ["partner", "gold-tier"]
jwt_secrets:
- consumer: retail-partner
key: retail-partner-key-2024
algorithm: HS256
secret: "super-secret-jwt-signing-key-for-retail-partner-min-32-chars"
claims:
- iss: retail-partner-auth-service
upstreams:
- name: orders-upstream
algorithm: round-robin
hash_on: none
targets:
- target: 10.0.2.10:8080
weight: 100
- target: 10.0.2.11:8080
weight: 100
healthchecks:
active:
http_path: /actuator/health
healthy:
interval: 10
successes: 3
unhealthy:
interval: 5
http_failures: 3
timeouts: 3
Sync this configuration to Kong:
deck sync --state deck-config.yaml --kong-addr http://localhost:8001
Best Practices for Production Kong Deployments
1. Use Database-Backed Mode with PostgreSQL
For dynamic, multi-node deployments, always use PostgreSQL as the configuration database. It provides superior consistency, replication, and backup capabilities compared to Cassandra. Reserve DB-less mode for Kubernetes with immutable infrastructure.
2. Implement Comprehensive Health Checks
Configure both active and passive health checks on all upstreams. This prevents routing traffic to unhealthy instances and enables automatic failover:
# Active health check configuration pattern
"healthchecks": {
"active": {
"http_path": "/health",
"timeout": 3,
"concurrency": 10,
"healthy": {
"interval": 10,
"http_statuses": [200, 201],
"successes": 3
},
"unhealthy": {
"interval": 5,
"http_statuses": [500, 503],
"http_failures": 3,
"timeouts": 3
}
},
"passive": {
"healthy": {
"http_statuses": [200, 201, 301, 302],
"successes": 5
},
"unhealthy": {
"http_statuses": [500, 502, 503, 504],
"http_failures": 3,
"timeouts": 3
}
}
}
3. Externalize Plugin Configuration Storage
For rate limiting with cluster or redis policy, always use a dedicated Redis cluster. Don't rely on local policy in multi-node deployments — it leads to inconsistent rate limiting across nodes.
4. Version Control Everything with deck
Treat Kong configuration like application code. Store deck-config.yaml in Git, run deck diff in CI pipelines, and automate deck sync as part of deployment workflows. Never manually edit configuration via the Admin API in production — use deck for all changes.
5. Implement Layered Security
Apply plugins in layers: global plugins for baseline security (bot detection, IP restriction), service-level plugins for authentication and authorization, and route-level plugins for specific transformations. This creates defense-in-depth without duplication.
6. Monitor and Alert on Gateway Metrics
Enable the Prometheus plugin globally and scrape metrics into your observability stack. Key metrics to monitor:
kong_nginx_http_current_connections— Active connections🚀 Need a reliable AI agent for your project?
Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.
Get Started — $23.99/mo