← Back to DevBytes

Kong API Gateway: Complete Setup and Configuration Guide

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:

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:

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:

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: