--- title: "Cómo instalar Kong API Gateway con Docker en Ubuntu 24.04" description: "Despliega Kong Gateway con PostgreSQL y Docker Compose en Ubuntu 24.04, publica un servicio con rutas y protégelo con claves de API y límite de peticiones." category: "Proxy y Equilibrio de Carga" language: es source: https://cubepath.com/es/docs/proxy-y-equilibrio-de-carga/instalacion-de-la-puerta-de-api-kong updated: 2026-09-25 --- # Cómo instalar Kong API Gateway con Docker en Ubuntu 24.04 Kong Gateway es una puerta de enlace de API de código abierto construida sobre Nginx y OpenResty. Se coloca delante de tus servicios y centraliza el enrutamiento, la autenticación, el límite de peticiones y los logs mediante plugins que se configuran en caliente a través de su Admin API. En este tutorial desplegarás Kong con PostgreSQL usando Docker Compose en Ubuntu 24.04, publicarás un servicio de prueba, lo protegerás con claves de API y un límite de peticiones y añadirás un certificado TLS. ## Requisitos previos - Un servidor con Ubuntu 24.04 LTS y al menos 2 GB de RAM, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Docker Engine y el plugin de Docker Compose instalados desde el repositorio oficial de Docker, con tu usuario en el grupo `docker`. - `curl` y `jq` para trabajar con la Admin API: `sudo apt install curl jq`. - Opcional, para el paso 7: un dominio apuntando al servidor y un certificado para él (por ejemplo, emitido con Certbot). ## Cómo se organiza Kong Antes de empezar conviene conocer los cuatro objetos con los que vas a trabajar: | Objeto | Qué representa | |---|---| | Service | Una API o aplicación de destino (su URL interna). | | Route | La regla que decide qué peticiones entrantes van a un Service (ruta, host, método). | | Consumer | Un cliente de la API, al que se asocian credenciales. | | Plugin | Funcionalidad que se aplica de forma global, a un Service, a una Route o a un Consumer. | Kong escucha en dos puertos principales: el proxy (8000 para HTTP y 8443 para HTTPS dentro del contenedor), que recibe el tráfico de los clientes, y la Admin API (8001), que usas para configurarlo. La Admin API da control total sobre el gateway y nunca debe quedar expuesta a Internet. ## Paso 1: Preparar el proyecto Crea un directorio para el despliegue: ```bash mkdir -p ~/kong && cd ~/kong ``` Genera una contraseña aleatoria para PostgreSQL y guárdala en un archivo `.env`, que Docker Compose lee automáticamente: ```bash echo "KONG_PG_PASSWORD=$(openssl rand -hex 24)" > .env chmod 600 .env ``` ## Paso 2: Definir los contenedores Crea el archivo de Compose: ```bash nano ~/kong/compose.yaml ``` ```yaml services: kong-db: image: postgres:16 restart: unless-stopped environment: POSTGRES_USER: kong POSTGRES_DB: kong POSTGRES_PASSWORD: ${KONG_PG_PASSWORD} volumes: - kong-db:/var/lib/postgresql/data healthcheck: test: ["CMD", "pg_isready", "-U", "kong", "-d", "kong"] interval: 5s timeout: 5s retries: 10 kong-migrations: image: kong:3.9 command: kong migrations bootstrap depends_on: kong-db: condition: service_healthy environment: KONG_DATABASE: postgres KONG_PG_HOST: kong-db KONG_PG_USER: kong KONG_PG_PASSWORD: ${KONG_PG_PASSWORD} restart: on-failure kong: image: kong:3.9 restart: unless-stopped depends_on: kong-migrations: condition: service_completed_successfully environment: KONG_DATABASE: postgres KONG_PG_HOST: kong-db KONG_PG_USER: kong KONG_PG_PASSWORD: ${KONG_PG_PASSWORD} KONG_PROXY_LISTEN: "0.0.0.0:8000, 0.0.0.0:8443 ssl http2" KONG_ADMIN_LISTEN: "0.0.0.0:8001" KONG_PROXY_ACCESS_LOG: /dev/stdout KONG_PROXY_ERROR_LOG: /dev/stderr KONG_ADMIN_ACCESS_LOG: /dev/stdout KONG_ADMIN_ERROR_LOG: /dev/stderr ports: - "80:8000" - "443:8443" - "127.0.0.1:8001:8001" healthcheck: test: ["CMD", "kong", "health"] interval: 10s timeout: 5s retries: 10 httpbin: image: mccutchen/go-httpbin restart: unless-stopped volumes: kong-db: ``` Puntos importantes: - `kong-migrations` crea el esquema en PostgreSQL y termina. Kong solo arranca cuando ha terminado correctamente. - El proxy se publica en los puertos 80 y 443 del servidor. La Admin API se publica únicamente en `127.0.0.1:8001`, así que solo es accesible desde el propio servidor. - `httpbin` es una aplicación de prueba que responde con los datos de la petición. No publica puertos: solo Kong puede llegar a ella por la red interna de Compose. En producción la sustituirás por tus servicios reales. > Importante: los puertos que publica Docker no pasan por las reglas de UFW. Por eso la Admin API se enlaza explícitamente a `127.0.0.1` en lugar de confiar en el cortafuegos. ## Paso 3: Arrancar Kong Levanta la pila: ```bash docker compose up -d ``` Comprueba el estado de los contenedores. `kong-migrations` debe aparecer como terminado con código 0 y `kong` como `healthy` pasados unos segundos: ```bash docker compose ps -a ``` ```output NAME IMAGE STATUS kong-httpbin-1 mccutchen/go-httpbin Up 40 seconds kong-kong-1 kong:3.9 Up 25 seconds (healthy) kong-kong-db-1 postgres:16 Up 40 seconds (healthy) kong-kong-migrations-1 kong:3.9 Exited (0) 27 seconds ago ``` Consulta la versión y el estado de la base de datos a través de la Admin API: ```bash curl -s http://127.0.0.1:8001 | jq -r .version curl -s http://127.0.0.1:8001/status | jq .database ``` ```output 3.9.1 { "reachable": true } ``` Una petición al proxy devuelve 404 porque todavía no hay rutas: ```bash curl -i http://localhost/ ``` ```output HTTP/1.1 404 Not Found ... { "message":"no Route matched with those values", "request_id":"..." } ``` ## Paso 4: Crear un servicio y una ruta Registra `httpbin` como servicio. Kong resolverá el nombre `httpbin` dentro de la red de Compose: ```bash curl -s -X POST http://127.0.0.1:8001/services \ --data name=httpbin \ --data url=http://httpbin:8080 | jq '{id, name, host, port}' ``` ```output { "id": "5a1c7f2e-...", "name": "httpbin", "host": "httpbin", "port": 8080 } ``` Crea una ruta que envíe al servicio todo lo que empiece por `/demo`. Con `strip_path=true` (el valor por defecto) Kong elimina el prefijo, así que `/demo/get` llega al servicio como `/get`: ```bash curl -s -X POST http://127.0.0.1:8001/services/httpbin/routes \ --data name=demo \ --data 'paths[]=/demo' | jq '{name, paths, strip_path}' ``` Prueba la ruta a través del proxy: ```bash curl -s http://localhost/demo/get | jq '.headers["X-Forwarded-Prefix"]' ``` ```output [ "/demo" ] ``` La respuesta es la de `httpbin`: Kong ha recibido `/demo/get`, ha quitado el prefijo y ha añadido la cabecera `X-Forwarded-Prefix` con la parte eliminada. Si quieres enrutar por dominio en lugar de por ruta, añade `--data 'hosts[]=api.your_domain'` al crear la ruta. ## Paso 5: Exigir una clave de API Activa el plugin `key-auth` en el servicio. A partir de ese momento cada petición debe incluir una clave válida en la cabecera `apikey`: ```bash curl -s -X POST http://127.0.0.1:8001/services/httpbin/plugins \ --data name=key-auth \ --data 'config.key_names[]=apikey' | jq '{name, enabled}' ``` Comprueba que una petición sin clave es rechazada: ```bash curl -i http://localhost/demo/get ``` ```output HTTP/1.1 401 Unauthorized ... { "message":"No API key found in request", "request_id":"..." } ``` Crea un consumidor y deja que Kong genere una clave aleatoria para él: ```bash curl -s -X POST http://127.0.0.1:8001/consumers --data username=app-client | jq '{username}' API_KEY=$(curl -s -X POST http://127.0.0.1:8001/consumers/app-client/key-auth | jq -r .key) echo "$API_KEY" ``` Repite la petición con la clave: ```bash curl -s -o /dev/null -w '%{http_code}\n' -H "apikey: $API_KEY" http://localhost/demo/get ``` ```output 200 ``` Kong añade las cabeceras `X-Consumer-Username` y `X-Consumer-ID` a la petición que llega al backend, de modo que tu aplicación sabe qué cliente está llamando. ## Paso 6: Limitar las peticiones El plugin `rate-limiting` cuenta las peticiones por consumidor (o por IP si no hay consumidor autenticado). Actívalo en el servicio con un límite de 5 peticiones por minuto para poder probarlo fácilmente: ```bash curl -s -X POST http://127.0.0.1:8001/services/httpbin/plugins \ --data name=rate-limiting \ --data config.minute=5 \ --data config.policy=local | jq '{name, config: {minute: .config.minute, policy: .config.policy}}' ``` La política `local` guarda los contadores en memoria de cada nodo de Kong. Si ejecutas varios nodos detrás de un balanceador, usa `redis` para que compartan el contador. Lanza siete peticiones seguidas: ```bash for i in $(seq 1 7); do curl -s -o /dev/null -w '%{http_code}\n' -H "apikey: $API_KEY" http://localhost/demo/get done ``` ```output 200 200 200 200 200 429 429 ``` Las respuestas incluyen cabeceras que indican al cliente cuánto margen le queda: ```bash curl -s -D - -o /dev/null -H "apikey: $API_KEY" http://localhost/demo/get | grep -i ratelimit ``` ```output X-RateLimit-Limit-Minute: 5 X-RateLimit-Remaining-Minute: 0 RateLimit-Limit: 5 RateLimit-Remaining: 0 RateLimit-Reset: 38 ``` Para dar a un cliente concreto un límite distinto, crea el mismo plugin sobre el consumidor (`POST /consumers/app-client/plugins`). La configuración más específica tiene prioridad sobre la del servicio. ## Paso 7: Añadir un certificado TLS Kong sirve HTTPS en el puerto 443 con un certificado autofirmado hasta que le das uno válido. Sube tu certificado y asócialo al dominio (SNI). La clave privada solo es legible por root, por eso se lee con `sudo`: ```bash sudo curl -s -X POST http://127.0.0.1:8001/certificates \ -F "cert=@/etc/letsencrypt/live/your_domain/fullchain.pem" \ -F "key=@/etc/letsencrypt/live/your_domain/privkey.pem" \ -F "snis[]=your_domain" | jq '{id, snis}' ``` Comprueba desde tu equipo que Kong presenta el certificado correcto: ```bash curl -s -o /dev/null -w '%{http_code}\n' -H "apikey: your_api_key" https://your_domain/demo/get ``` ```output 200 ``` Como `curl` valida el certificado por defecto, un `200` confirma que la cadena es correcta. Sustituye `your_api_key` por la clave generada en el paso 5. Cuando renueves el certificado, actualízalo con `PATCH /certificates/` usando los mismos parámetros `cert` y `key`. ## Paso 8: Exportar la configuración Toda la configuración está en PostgreSQL. Además de hacer copias de la base de datos, puedes exportarla a un archivo declarativo legible, útil para revisarla o versionarla en Git: ```bash docker compose exec kong kong config db_export /tmp/kong.yaml docker compose cp kong:/tmp/kong.yaml ./kong-export.yaml grep -A3 '^services:' kong-export.yaml ``` ```output services: - connect_timeout: 60000 enabled: true host: httpbin ``` > Advertencia: el archivo exportado contiene las credenciales de los consumidores, como las claves de API. Guárdalo con permisos restrictivos y no lo subas a un repositorio público. ## Solución de problemas - **`kong` no arranca y `kong-migrations` sale con error**: revisa `docker compose logs kong-migrations`. Suele ser una contraseña de PostgreSQL distinta de la que se usó al crear el volumen; si es una instalación nueva, borra el volumen con `docker compose down -v` y vuelve a empezar. - **`no Route matched with those values`**: la ruta no coincide. Lista las rutas con `curl -s http://127.0.0.1:8001/routes | jq '.data[] | {name, paths, hosts}'`. - **`502 Bad Gateway` o `An invalid response was received from the upstream server`**: Kong no llega al servicio. Comprueba la URL con `curl -s http://127.0.0.1:8001/services/httpbin | jq '{host, port, path}'` y que el backend esté en la misma red o sea accesible desde el contenedor. - **Actualizar Kong**: cambia la etiqueta de la imagen, ejecuta `docker compose run --rm kong kong migrations up` y después `docker compose up -d`. Lee antes las notas de la versión. ## Conclusión Tienes Kong Gateway funcionando con PostgreSQL, un servicio publicado a través de una ruta, autenticación por clave de API, límite de peticiones por consumidor y un certificado TLS, con la Admin API accesible solo desde el propio servidor. Como siguientes pasos, puedes añadir plugins como `cors`, `ip-restriction` o `prometheus`, pasar a la política `redis` del límite de peticiones si escalas a varios nodos, y gestionar la configuración de forma declarativa con decK en tu pipeline de CI.