Repositorio complementario: la versión de despliegue con un solo comando de esta guía — despliegue con Docker, generación de claves y renderizado de configuración en un único comando, además de un generador de configuración para el cliente y un ejemplo de Nginx:

kuangyongjie/cliproxyapi-codex-deploy

git clone https://github.com/kuangyongjie/cliproxyapi-codex-deploy.git
cd cliproxyapi-codex-deploy
./scripts/setup.sh

CLIProxyAPI permite colocar Codex detrás de un endpoint propio compatible con OpenAI. El camino práctico es: ejecutar CLIProxyAPI con Docker, completar el OAuth de Codex en el proxy, exponer un endpoint /v1 estable y, por último, apuntar el cliente local de Codex a ese endpoint con la Responses API activada.

Configuración rápida

  1. Despliega CLIProxyAPI con Docker o Docker Compose.
  2. Haz persistente el directorio de autenticación del proxy para que el estado OAuth sobreviva a reinicios.
  3. Ejecuta el flujo de inicio de sesión OAuth de Codex en el proxy.
  4. Expón el proxy mediante HTTPS conservando intacta la ruta /v1/.
  5. Apunta Codex al proxy mediante un base_url personalizado.
  6. Configura wire_api = "responses" y utiliza una clave de proxy para los clientes downstream.

El resto de esta guía explica por qué importa cada paso, cómo encajan entre sí las distintas capas de autenticación y qué fallos suelen romper una instalación que, por lo demás, parece sana.

Si Codex ya forma parte de tu trabajo diario, tarde o temprano aparece una pregunta bastante evidente: ¿sigues usando simplemente el inicio de sesión directo oficial o colocas Codex detrás de una capa proxy propia y lo conviertes en parte de una infraestructura más amplia que controlas tú?

Para una sola máquina y un solo usuario, el acceso directo sigue siendo la opción más sencilla. No tiene sentido fingir lo contrario. Lo instalas, inicias sesión y continúas. Menos piezas móviles, menos servicios y menos lugares donde depurar problemas.

Pero cuando el flujo de trabajo crece, la pregunta cambia. Quizá quieras que varias máquinas apunten al mismo endpoint. Quizá quieras que Codex, Claude Code y Gemini CLI estén detrás de una única capa de routing. O quizá prefieras que el estado OAuth y las políticas de acceso se gestionen en el servidor en lugar de repetir todo en cada cliente. Ahí es donde CLIProxyAPI deja de parecer un proyecto de proxy de nicho y empieza a resultar una pieza de infraestructura útil.

Lo que me hizo tomar este enfoque en serio no fue pensar “quiero una configuración más sofisticada”. Fue darme cuenta de que el login directo funciona muy bien para una máquina, pero empieza a dispersarse cuando Codex tiene que convivir con un VPS, varias herramientas self-hosted y algunos scripts internos. El estado de sesión vive en un cliente, cada aplicación nueva quiere su propia configuración de endpoint y cada consumidor adicional implica otra ronda de puesta a punto. La pregunta real pasó a ser: si quiero Codex detrás de CLIProxyAPI, ejecutándose con Docker en un servidor Linux, autenticado allí mediante OpenAI OAuth y conectado después de nuevo con la aplicación local de Codex, ¿cómo es exactamente esa cadena y dónde suele fallar?

Por qué poner Codex detrás de un proxy

Mi respuesta, en esencia, es esta: si buscas comodidad inmediata, el login directo sigue ganando. Si quieres que Codex forme parte de una capa reutilizable y controlable de herramientas de IA, el enfoque con proxy merece la pena. Su valor no está en parecer más avanzado, sino en que el estado de autenticación, el routing, el control del endpoint y las ampliaciones futuras dejan de estar atados a una única instalación cliente.

Una vez existe esa capa proxy, la utilidad suele ir mucho más allá de Codex. Lo que realmente obtienes es un punto de entrada reutilizable compatible con OpenAI que pueden compartir otras herramientas self-hosted. Si un proyecto admite un base_url personalizado, es muy probable que pueda sentarse detrás del mismo gateway. Codex puede ser lo primero que conectes, pero rara vez será lo único. Un frontend autoalojado, un panel de agentes, un servicio interno de scripts u otra aplicación compatible con OpenAI pueden reutilizar el mismo endpoint en vez de mantener cada uno su propio estado de autenticación upstream.

Es fácil pasar por alto qué es exactamente lo que se comparte. No se trata de copiar el mismo secreto upstream cada vez en más sitios. Es justo lo contrario. El estado OAuth y los tokens permanecen centralizados dentro de la capa proxy, y las herramientas downstream solo necesitan la URL del proxy y una clave emitida por él. Puede parecer un cambio de configuración pequeño, pero operativamente tiene una forma muy distinta de repetir credenciales y login en cada aplicación o contenedor. La próxima vez que añadas una herramienta compatible con OpenAI, la pregunta ya no será “¿cómo inicio sesión también aquí?”, sino “¿qué clave del proxy debe usar y debe compartir el mismo endpoint?”

Para un desarrollador independiente esto importa porque cambia la forma de gestionar secretos. En vez de que cada máquina y cada aplicación tengan que conocer las credenciales upstream, el estado OAuth sensible queda centralizado en el proxy. Los clientes downstream y los proyectos self-hosted solo necesitan una clave del proxy y un endpoint estable. Añadir un nuevo proyecto pasa a significar “emitir una nueva clave downstream y apuntarlo al gateway”, no “repetir de nuevo toda la historia de autenticación upstream”.

También existe un punto intermedio muy práctico. Si hoy solo tienes unas pocas herramientas internas, al principio puedes dejar que varias compartan una misma clave API del proxy para poner todo en marcha rápidamente. Más adelante, cuando quieras logs más limpios, límites por proyecto o poder revocar a un consumidor sin afectar al resto, separas esas claves. En otras palabras, la capa proxy es útil tanto cuando optimizas por velocidad hoy como cuando te preparas para una arquitectura más disciplinada mañana.

Divide la cadena en tres partes

Lo que más me ayudó fue separar todo en tres piezas. Primero está el servicio CLIProxyAPI que se ejecuta en el servidor. Segundo está el estado OAuth de Codex, que debe almacenarse realmente en el directorio de autenticación montado que utiliza ese servicio. Tercero está la configuración local del cliente Codex, principalmente ~/.codex/config.toml y ~/.codex/auth.json. Si mantienes esas tres capas separadas mentalmente, la instalación es manejable. Cuando se mezclan, todo parece bastante más misterioso de lo que realmente es.

La documentación del proyecto ya cubre las piezas principales: Codex mediante OAuth, despliegue con Docker y Docker Compose y el patrón de configuración del cliente local. La clave está en comprender qué archivo pertenece a qué capa. config.yaml pertenece al servicio proxy. El directorio auth/ montado contiene el estado de inicio de sesión almacenado por el proxy. Los archivos dentro de ~/.codex/ pertenecen a la aplicación local de Codex.

Lado servidor: despliegue con Docker e inicio de sesión OAuth

Si hoy montara esto desde cero, mantendría el directorio del servidor extremadamente pequeño:

/opt/cliproxyapi/
├── docker-compose.yml
├── config.yaml
└── auth/

Ese directorio auth/ es más importante de lo que parece. CLIProxyAPI usa por defecto ~/.cli-proxy-api como directorio de autenticación y, con Docker, la idea es montar esa ruta fuera del contenedor para que el estado OAuth sobreviva a reinicios y reconstrucciones. Muchos casos de “ayer funcionaba y tras reiniciar dejó de hacerlo” no son más que falta de persistencia en este punto.

En una primera pasada también dejaría config.yaml lo más pequeño posible. El archivo oficial config.example.yaml es grande, pero para un proxy de Codex al principio interesan pocas piezas:

host: ""
port: 8317
auth-dir: "~/.cli-proxy-api"

api-keys:
  - "<proxy-api-key>"

remote-management:
  allow-remote: false
  secret-key: ""

Aquí hay dos cosas fáciles de interpretar mal. La sección api-keys no contiene tu credencial upstream de OpenAI. Son las claves que utilizan los clientes downstream cuando llaman al proxy. Esa separación es, de hecho, una de las mejores partes del diseño, porque puedes emitir distintas claves para diferentes clientes o proyectos. Codex puede usar una, otra aplicación self-hosted otra, y si necesitas rotar o revocar una de ellas lo haces en la capa proxy sin volver a tocar la autenticación upstream. Y salvo que exista un motivo real para exponer la capa de administración de forma remota, dejaría desactivado remote-management.allow-remote. El valor predeterminado ya es conservador, y eso es exactamente lo apropiado aquí.

Para Docker Compose, mantendría la configuración sencilla y explícita:

services:
  cli-proxy-api:
    image: eceasy/cli-proxy-api:latest
    container_name: cli-proxy-api
    restart: unless-stopped
    ports:
      - "127.0.0.1:8317:8317"
      - "127.0.0.1:1455:1455"
    volumes:
      - ./config.yaml:/CLIProxyAPI/config.yaml
      - ./auth:/root/.cli-proxy-api

Ambos puertos están vinculados a loopback a propósito. El puerto de la API solo necesita ser accesible para el Nginx del mismo host — la sección del lado cliente más abajo ya hace proxy a http://127.0.0.1:8317 — y el listener del callback OAuth tiene un historial documentado de enlazar todas las interfaces cuando se publica abiertamente (issue #3463 del upstream). Publicar estos dos puertos en Internet por defecto convertiría una superficie de login temporal en una permanente; no trates la publicación pública como el valor seguro por defecto. Si tu topología de red necesita realmente un acceso más amplio, defínelo como una excepción explícita con sus propias restricciones, no como punto de partida.

Una nota más sobre el tag de la imagen: :latest es un tag móvil. El mapa de campos v8 de esta guía se verificó el 2026-10-14 contra la rama main del repositorio upstream — no ejecutando esta imagen. Antes de desplegar, confirma que el tag que descargues siga correspondiendo a ese layout, o fija un digest que hayas verificado tú.

Ese puerto adicional 1455 está ahí por una razón. La documentación oficial del proveedor indica que Codex OAuth utiliza un callback local en el puerto 1455. Es frecuente concentrarse solo en el puerto principal de la API, 8317, pero la parte OAuth del flujo tiene sus propias expectativas. El servicio puede estar levantado y sano mientras el login sigue fallando porque nunca se tuvo en cuenta ese callback.

Con el contenedor en marcha, el comando documentado para iniciar sesión en Codex es:

docker compose exec cli-proxy-api /CLIProxyAPI/CLIProxyAPI -no-browser --codex-login

El flag -no-browser es especialmente útil en un servidor Linux remoto porque imprime la URL en vez de intentar abrir un navegador dentro del contenedor. También es el momento de ser muy preciso mentalmente: en esta fase no estás iniciando sesión en el cliente local de Codex. Estás autenticando Codex para el servicio proxy, de modo que el proxy pueda responder después a solicitudes compatibles con Codex.

Hay algo de ese callback que es fácil entender mal en una máquina remota. El navegador se ejecuta en tu portátil local, así que http://localhost:1455/auth/callback se resuelve a tu portátil — no al VPS — sin importar cómo estén mapeados los puertos del contenedor. Para una configuración remota Linux/VPS, el orden que realmente cierra el bucle es:

  1. En tu máquina local (no en el servidor), abre primero el túnel:
    ssh -N -L 1455:127.0.0.1:1455 <user>@<vps>
  2. Ejecuta el comando de login de esta guía. La CLI imprime la URL de OAuth — con -no-browser, su propio código incluso imprime estas instrucciones del túnel SSH.
  3. Abre esa URL en tu navegador local y completa el inicio de sesión. El callback a localhost:1455 ahora viaja por el túnel de vuelta al contenedor.

Si omites el túnel, la CLI se quedará en «Waiting for Codex authentication callback…» y, tras unos 15 segundos, ofrecerá Paste the Codex callback URL (or press Enter to keep waiting):. Puedes pegar la URL completa del callback directamente desde la barra de direcciones del navegador — ese es el camino manual propio de la CLI, no un truco.

Si este paso no termina de verdad, nada de lo que viene después importa demasiado. La primera vez vigilaría dos cosas: la salida de docker compose logs -f y el directorio auth/ montado. Si el flujo OAuth parece haber finalizado, pero no aparece nada útil en auth/, todavía no tienes un estado de proxy funcional.

Referencia de campos de config.yaml (v8)

El config.yaml mínimo de más arriba en esta guía usa las grafías antiguas de nivel superior (host, port, auth-dir, api-keys, remote-management). Siguen cargando sin problema, pero el proyecto upstream ha pasado a un layout v8 (config-version: 8) donde cada ajuste vive bajo una sección con nombre. Si copias ejemplos del config.example.yaml actual del repositorio upstream, verás el nuevo layout; aquí tienes un mapa campo por campo de los ajustes que realmente toca una configuración de proxy Codex. Cada nombre de campo está verificado contra el config.example.yaml del upstream en la rama main (revisado el 2026-10-14). Los forks «plus» pueden divergir; confirma su imagen/versión fijada y su config.example.yaml antes de asumir que este layout les aplica.

Campo (v8)Grafía antiguaQué hace
config-version: 8—Declara el layout v8. Si coexisten ambas grafías de un campo, gana el valor v8.
server.hosthostInterfaz de escucha. Una cadena vacía enlaza todas las interfaces.
server.portportEl puerto de la API que llaman los clientes. 8317 en esta guía.
oauth.auth-dirauth-dirDónde se guarda el estado OAuth de Codex. Debe coincidir con el montaje de auth/, o los logins se pierden al reiniciar.
access.api-keysapi-keysClaves downstream para tus clientes. No son credenciales upstream de OpenAI.
management.allow-remoteremote-management.allow-remoteDéjalo en false salvo que tengas un motivo real para exponer la API de administración.
management.secret-keyremote-management.secret-keyClave de la API de administración. Vacía desactiva por completo la API de administración.
client.codex.optimize-multi-agent-v2—Opcional. Optimiza las peticiones de Codex Desktop y codex-tui para configuraciones multiagente. Por defecto false.

Una configuración mínima estilo v8 para el setup de esta guía quedaría así:

config-version: 8

server:
  host: ""
  port: 8317

oauth:
  auth-dir: "~/.cli-proxy-api"

access:
  api-keys:
    - "<proxy-api-key>"

management:
  allow-remote: false
  secret-key: ""

Dos detalles merecen mención aparte. Primero: access.api-keys es la misma lista que el antiguo api-keys: claves que entregas a clientes downstream, nunca secretos upstream. Segundo: oauth.auth-dir dentro del contenedor se resuelve por defecto a /root/.cli-proxy-api, y por eso el archivo compose monta ./auth justo ahí. Cambias un lado sin el otro y vuelve el fallo de «ayer funcionaba y tras reiniciar se perdió el login» de la sección anterior.

Docker Compose: la vía independiente

El repositorio complementario enlazado al inicio de esta guía automatiza todo lo siguiente en un script. Si prefieres ser dueño de cada archivo y ver exactamente qué se ejecuta, aquí tienes la misma configuración como proyecto compose independiente, sin scripts auxiliares.

Mantén el mismo directorio pequeño:

/opt/cliproxyapi/
├── docker-compose.yml
├── config.yaml
└── auth/

El archivo compose es el mismo de la sección del lado servidor de más arriba — los mismos puertos ligados a 127.0.0.1, los mismos montajes, ensamblado a partir del propio docker-compose.yml del repositorio upstream y recortado a lo que necesita esta guía. No lo repito aquí porque debe existir exactamente un archivo que mantener correcto; cópialo de arriba. El montaje de ./auth tiene que seguir aterrizando donde apunta oauth.auth-dir, o el estado OAuth se pierde con cada reconstrucción del contenedor.

Levanta y observa el arranque:

docker compose config
docker compose up -d
docker compose logs -f

El primer comando merece convertirse en hábito: docker compose config renderiza el archivo efectivo sin iniciar nada, una comprobación estática gratuita que caza errores de indentación y rutas de montaje equivocadas antes de que se conviertan en misterios en tiempo de ejecución.

Luego ejecuta el login de Codex exactamente como en la sección anterior:

docker compose exec cli-proxy-api /CLIProxyAPI/CLIProxyAPI -no-browser --codex-login

Una salvedad honesta: cada campo del archivo compose de arriba está verificado contra el docker-compose.yml del repositorio upstream y la configuración ya mostrada en esta guía, pero no ejecuté este archivo exacto en una máquina real para esta actualización. Si tu entorno difiere — Docker rootless, un data root no estándar — las rutas de volúmenes son lo primero que debes revisar.

Lado cliente: configuración de Codex y Nginx

Aquí llega la parte que al principio resulta un poco contraintuitiva. Incluso después de que el proxy haya completado correctamente el OAuth de Codex, la aplicación local de Codex sigue necesitando una nueva configuración. Según la página oficial de CLIProxyAPI para el cliente Codex, esto significa editar ~/.codex/config.toml y ~/.codex/auth.json.

Ayuda pensar en dos capas de claves separadas. En upstream, el proxy conserva el estado OAuth real y los tokens que le permiten actuar en nombre de tu acceso a Codex. En downstream, cada cliente o aplicación solo necesita permiso para usar tu proxy. Esa separación es precisamente lo que vuelve útil esta arquitectura cuando interviene más de una herramienta: haces la autenticación upstream una vez y luego distribuyes el acceso downstream de forma controlada.

En un servidor remoto detrás de tu propio dominio, trataría config.toml como el lugar donde le dices a Codex que deje de pensar “OpenAI directo” y pase a “usar mi proveedor proxy”:

model_provider = "cliproxyapi"
model = "gpt-5-codex"
model_reasoning_effort = "high"

[model_providers.cliproxyapi]
name = "cliproxyapi"
base_url = "https://codex-proxy.example.com/v1"
wire_api = "responses"

Hay dos errores que aparecen con frecuencia. El primero es dejar base_url apuntando a un ejemplo de localhost aunque el proxy esté realmente en un servidor remoto. El segundo es olvidar wire_api = "responses". La documentación de CLIProxyAPI utiliza explícitamente el formato responses para Codex, así que omitir esa línea es una de las formas más fáciles de crear un comportamiento extraño del cliente que parece más difícil de depurar de lo que en realidad es.

Después está auth.json, que la documentación oficial mantiene deliberadamente mínimo:

{
  "OPENAI_API_KEY": "<proxy-api-key>"
}

El valor <proxy-api-key> suele hacer dudar un momento, pero la duda debe ser sobre qué clave colocar ahí, no sobre si importa. Tiene que ser una de las claves de la lista api-keys de tu config.yaml: la clave downstream del proxy del paso 6 de la configuración rápida. CLIProxyAPI trata esas claves como credenciales de autenticación del cliente, de modo que esto es lo que presenta el Codex local cuando habla con el proxy. Sigue sin ser una clave upstream real de OpenAI; la lógica de routing vive en config.toml, especialmente en el bloque del proveedor y en el base_url del proxy.

Si además quieres colocar Nginx delante del servicio y añadir HTTPS, el proxying en sí es directo. Donde no intentaría ser ingenioso es con la ruta /v1/. Consérvala y evita reescrituras innecesarias:

server {
  listen 443 ssl http2;
  server_name codex-proxy.example.com;

  location /v1/ {
    proxy_pass http://127.0.0.1:8317/v1/;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

Cuando ese endpoint es estable, su utilidad supera con mucho a la aplicación de escritorio de Codex. Muchas herramientas self-hosted solo necesitan una base URL compatible con OpenAI y una credencial similar a una API key. En ese punto, la configuración deja de ser “un proxy especial para Codex” y empieza a convertirse en “mi gateway unificado de IA”. Las variables de entorno que suelen esperar esos proyectos tienen este aspecto:

OPENAI_BASE_URL=https://codex-proxy.example.com/v1
OPENAI_API_KEY=<proxy-api-key-for-this-project>

Ahí es donde la arquitectura empieza a compensar de verdad. La parte pesada —OAuth upstream, estado de las cuentas, routing del proveedor y posibles políticas multi-cuenta— permanece dentro de CLIProxyAPI. Cada proyecto downstream solo necesita un endpoint estable y su propia clave de proxy. Si más adelante cambias el upstream, añades cuentas, modificas la estrategia de routing o mueves tráfico entre herramientas, haces el cambio en un único sitio en lugar de tocar todos los clientes y todas las aplicaciones.

Esto resulta todavía más útil cuando ya ejecutas varios tipos de herramientas. Puede que Codex gestione tu trabajo de programación, un frontend de chat self-hosted cubra el prompting del día a día y un pequeño servicio interno de scripts o agentes lleve la automatización. Sin un gateway compartido, cada uno tiende a desarrollar su propia historia de secretos. Con un proxy en medio, el estado upstream de larga duración vive en un solo lugar mientras la parte downstream se simplifica mucho: qué proyecto recibe qué clave y a qué endpoint debe llamar. Ese es el punto en el que la instalación deja de parecer “un truco especial de Codex” y empieza a parecer infraestructura normal.

Los fallos más comunes

En uso real, los fallos más habituales normalmente no están en Docker. Suelen ser cosas como:

  • el contenedor del proxy está levantado, pero el directorio auth/ montado nunca llegó a recibir el estado OAuth de Codex
  • se editó la configuración del cliente local, pero base_url todavía apunta a localhost
  • existe el proxy por dominio, pero falta /v1/ o se ha modificado
  • se omitió wire_api = "responses"
  • alguien da por hecho que auth.json necesita un secreto upstream real de OpenAI y vuelve a introducir credenciales que no hacen falta

También es la diferencia más clara frente al login directo. El acceso directo es más sencillo porque la autenticación y el comportamiento del cliente permanecen en un mismo sitio. CLIProxyAPI divide el sistema entre runtime del servicio, estado OAuth almacenado, reverse proxy y configuración del cliente. Eso exige más trabajo al principio, pero te da una forma mucho más reutilizable cuando quieres que Codex viva dentro de un entorno mayor en vez de ser una aplicación local aislada.

¿Merece la pena? Para mí, la respuesta más limpia es esta: el login directo es mejor cuando optimizas para “quiero usar Codex ahora mismo en esta máquina”. CLIProxyAPI es mejor cuando optimizas para “quiero que Codex forme parte de mi propia capa controlada de herramientas de IA”. Una opción reduce la fricción. La otra deja más espacio para estandarizar, reutilizar y ampliar después. Y ese intercambio empieza a ser mucho más atractivo en cuanto tienes más de un consumidor para la misma capacidad upstream. Cuando varias herramientas self-hosted comparten el mismo backend de IA, centralizar la autenticación y las claves downstream suele ser mucho más limpio que repartir secretos y estados de sesión por cada proyecto.

Si eliges este segundo camino, conviene tratar config.yaml y auth/ como estado real, no como simples archivos de instalación desechables. Lo que más duele durante una recuperación rara vez es volver a descargar una imagen. Lo problemático es reconstruir esas pequeñas piezas de autenticación y configuración que asumiste que serían fáciles de recrear.

Si ya estás diseñando un flujo de trabajo más amplio con Codex, Qué cambió en el nuevo Codex y por qué se parece más a un entorno de desarrollo es una lectura complementaria útil. Y si este proxy va a quedarse en un VPS durante tiempo, Copias de seguridad de volúmenes Docker en GitHub: recuperación de bajo coste para 1Panel encaja de forma natural, porque el estado de autenticación y configuración deja de parecer teórico en cuanto necesitas recuperarlo deprisa.