LobeHub moves self-hosted chats to a server gateway
LobeHub v2.2.18 changes the self-hosted Compose setup with gateway URLs, service tokens, keys, and reverse-proxy checks.
Moving execution away from the browser
LobeHub v2.2.18 makes a meaningful change to Docker Compose deployments: chats use server-side Gateway Mode by default. This is not just another toggle in the interface. The browser keeps a WebSocket connection to the gateway while the server runs agent operations and sends their events through that service.
That is a better fit for a homelab where I want the moving parts to be explicit. LobeHub no longer relies on the browser to carry the main chat execution loop. The gateway becomes another service in the stack, with its own network path, service token, and HTTP/WebSocket endpoint that I can place behind Caddy.
What needs to change in an existing Compose stack
Pulling the new image is not enough for an older deployment. The setup now needs a deployment-specific JWKS_KEY, a public-only JWKS_PUBLIC_KEY, a GATEWAY_SERVICE_TOKEN, and a browser-reachable AGENT_GATEWAY_URL. It can also use AGENT_GATEWAY_INTERNAL_URL so the main application container reaches the gateway over the internal Compose network rather than through the public route.
The important security boundary is keeping the private signing material out of the gateway container. LobeHub keeps JWKS_KEY, which signs internal JWTs, while the gateway receives JWKS_PUBLIC_KEY to validate them. The service token protects internal calls to gateway operation endpoints, so it should be an independent secret rather than a value borrowed from another component.
This is a reduced Compose example based on the documented configuration. Real values should stay outside version control, whether that means a local .env file or a secrets manager, and the public URLs must match what the browser actually uses.
services:
lobe:
environment:
ENABLE_AGENT_GATEWAY: "1"
AGENT_GATEWAY_URL: https://gateway.example.com
AGENT_GATEWAY_INTERNAL_URL: http://gateway:8787
GATEWAY_SERVICE_TOKEN: ${GATEWAY_SERVICE_TOKEN}
JWKS_KEY: ${JWKS_KEY}
gateway:
image: ghcr.io/lobehub/lobehub-gateway:0.3.2
environment:
SERVICE_TOKEN: ${GATEWAY_SERVICE_TOKEN}
JWKS_PUBLIC_KEY: ${JWKS_PUBLIC_KEY}
LOBE_API_BASE_URL: https://lobe.example.comOne public address and one internal route
The public and internal gateway addresses solve a common Docker problem. AGENT_GATEWAY_URL must be reachable from the browser because it is also the WebSocket origin. AGENT_GATEWAY_INTERNAL_URL, on the other hand, can target the Compose service directly, such as http://gateway:8787, without sending container traffic back through the reverse proxy.
With a dedicated subdomain, Caddy can publish the gateway with a small configuration. Its normal reverse_proxy behavior supports WebSocket upgrades, but short proxy timeouts are a bad idea here because an agent can keep a connection open for a long-running operation.
gateway.example.com {
reverse_proxy gateway:8787
}A path prefix is possible too, but it needs extra care. The proxy must strip that prefix before forwarding traffic because the gateway expects /ws and /api/operations/* at its root. Forwarding /agent-gateway/ws unchanged may leave a healthy container behind an endpoint that never connects.
The fine print matters
This does not turn the gateway into a distributed execution platform. The documented self-hosted Go implementation keeps operations, buffered events, and other runtime state in memory, so a restart loses that state. It also requires backend calls and the browser WebSocket for the same operation to reach the same gateway instance.
Gateway Mode should not be confused with complete agent isolation either. The gateway coordinates browser and server traffic, but model credentials, enabled tools, and permission boundaries still need separate review. Splitting services into containers clarifies responsibility; it does not replace secrets management or access control.
Before upgrading, I would back up the database and save the current docker-compose.yml and .env. The release notes explicitly stop short of claiming a fully verified migration and rollback path for every production setup. If gateway settings are missing, LobeHub emits a startup warning and falls back to browser execution, which prevents a total outage but can also conceal an unfinished migration.
Source: LobeHub v2.2.18 Official documentation: LobeHub Agent Gateway in Go