selfhost.directory

The index of self-hostable projects — ranked, categorized and served with live updates.
Own your data and stop giving it to big tech.

GPT-Load logo

Self-host GPT-Load

v2.0.0-rc.57

AI gateway for API keys and subscription accounts

BetaOpen SourceMIT
Docker ComposeCompose file & guide
docker compose up -d
Updated8 hours ago
LanguageGo
Setup difficultyEasy
Stars
7 176
Ports
3001 (HTTP service (API gateway and embedded management UI)), 1455 (OAuth callback port for subscription channels), 54545 (OAuth callback port for subscription channels), 51121 (OAuth callback port for subscription channels)
Docker
ghcr.io/tbphp/gpt-load
Groups list showing channels, connection types, model counts and credential healthMonitor health page with available, cooling and blacklisted credentialsManagement console login with the generated management key

What is GPT-Load?

Should you self-host GPT-Load?

GPT-Load scores 64/100 on selfhost.directory (how scoring works) and ranks #7 of 39 in API Gateway. It has 7 176 GitHub stars.

It shipped 2 stable releases in the past year. The latest, v1.4.14, came out on 27 October 2026. New versions land roughly every 5 days.

Setup is easy: most people get it running in one sitting. An official Docker image is available. It is MIT-licensed and written mostly in Go.

Its closest alternative on selfhost.directory is Kong Gateway (82/100, 44 314 stars). It scores higher overall, so compare the two before you pick. See GPT-Load vs Kong Gateway side by side.

Features

  • AccessKey management
  • Automatic failover and retries
  • Codex live voice relay
  • Credential blacklisting and recoverySubscription credentials need reauthorization or manual handling instead of probing.
  • Credential encryption at restLosing encryption.key makes stored credentials unrecoverable; no master-key rotation.
  • Embedded management web UI
  • Management API
  • Model aliases
  • Multi-credential load balancing
  • Multi-protocol AI gateway
  • Outbound proxy support
  • Request logging
  • Route check diagnostics
  • Subscription account channels
  • Usage and cost analyticsCosts are estimates derived from upstream usage, not provider invoices.

GPT-Load README

How to install GPT-Load

Install GPT-Load with Docker Compose

Docker and Docker Compose
Environment variables
HOST, PORT, AUTH_KEY, ENCRYPTION_KEY, DATA_DIR, DATABASE_DSN, BIND_ADDRESS, OAUTH_CALLBACK_BIND_ADDRESS
git clone --depth 1 https://github.com/tbphp/gpt-load.git
cd gpt-load

cp .env.example .env
docker compose up -d

Official Compose file uses ghcr.io/tbphp/gpt-load:2; data lives in the gpt-load-data volume. Recommended method.

How to configure GPT-Load

  • AUTH_KEYEmpty, reads or generates ${DATA_DIR}/auth.keyBearer key for the management UI and `/api` management API, not a data-plane AccessKey.

    Bearer key for the management UI and `/api` management API, not a data-plane AccessKey.

  • BIND_ADDRESSEmpty, inherits HOSTCompose only; overrides the host publishing address for the main service port without changing OAuth callback ports.

    Compose only; overrides the host publishing address for the main service port without changing OAuth callback ports.

  • CLIENT_IP_HEADEREmpty, uses the connection IPClient IP header, such as `X-Forwarded-For` or `CF-Connecting-IP`; missing or invalid values fall back to the connection IP. Shared by logs, AccessKey IP restrictions, and other IP consumers; supports IPv4/IPv6. Restart after changes.

    Client IP header, such as `X-Forwarded-For` or `CF-Connecting-IP`; missing or invalid values fall back to the connection IP. Shared by logs, AccessKey IP restrictions, and other IP consumers; supports IPv4/IPv6. Restart after changes.

  • CODEX_LIVE_ICE_SERVERSOptional JSON array of ICE/STUN/TURN servers when the network requires one.

    Optional JSON array of ICE/STUN/TURN servers when the network requires one.

  • CODEX_LIVE_MAX_SESSIONS
  • CODEX_LIVE_PUBLIC_IPCodex voice defaults to direct media. Choose Off / Direct / Relay in system settings; Codex groups can override this default. Existing media-relay deployments must select Relay. Only relay mode uses these process-wide media settings (shared by all relay groups). For Docker, use docker-compose.voice.yml and set a reachable media IP below. UDP defaults to 50000-50127. Publish the same range and allo

    Codex voice defaults to direct media. Choose Off / Direct / Relay in system settings; Codex groups can override this default. Existing media-relay deployments must select Relay. Only relay mode uses these process-wide media settings (shared by all relay groups). For Docker, use docker-compose.voice.yml and set a reachable media IP below. UDP defaults to 50000-50127. Publish the same range and allo

  • CODEX_LIVE_UDP_PORT_MAX
  • CODEX_LIVE_UDP_PORT_MIN
  • CONTAINER_STOP_GRACE_PERIOD15sDocker duration to wait before Compose force-stops the container; should be longer than `GRACEFUL_SHUTDOWN_TIMEOUT`.

    Docker duration to wait before Compose force-stops the container; should be longer than `GRACEFUL_SHUTDOWN_TIMEOUT`.

  • DATABASE_DSNEmpty, uses ${DATA_DIR}/gpt-load.dbEmpty uses application-managed SQLite; non-empty values support SQLite paths or URLs, MySQL URLs, and PostgreSQL URLs, and are treated as operator-managed external databases. Container file paths must be inside a mounted directory.

    Empty uses application-managed SQLite; non-empty values support SQLite paths or URLs, MySQL URLs, and PostgreSQL URLs, and are treated as operator-managed external databases. Container file paths must be inside a mounted directory.

  • DATABASE_MAX_IDLE_CONNECTIONS5Maximum idle connections for MySQL and PostgreSQL, positive integer and no greater than `DATABASE_MAX_OPEN_CONNECTIONS`. SQLite always uses one connection.

    Maximum idle connections for MySQL and PostgreSQL, positive integer and no greater than `DATABASE_MAX_OPEN_CONNECTIONS`. SQLite always uses one connection.

  • DATABASE_MAX_OPEN_CONNECTIONS10Maximum open connections for MySQL and PostgreSQL, positive integer. SQLite always uses one connection.

    Maximum open connections for MySQL and PostgreSQL, positive integer. SQLite always uses one connection.

  • DATA_DIR./dataDirectory for the managed database, `auth.key`, `encryption.key`, and runtime state; official Compose uses `/app/data`, while the Windows Setup service uses `%ProgramData%\GPT-Load\data`.

    Directory for the managed database, `auth.key`, `encryption.key`, and runtime state; official Compose uses `/app/data`, while the Windows Setup service uses `%ProgramData%\GPT-Load\data`.

  • ENCRYPTION_KEYEmpty, reads or generates ${DATA_DIR}/encryption.keyEncrypts channel credentials; changing or losing it makes existing credentials undecryptable, so back it up with the database.

    Encrypts channel credentials; changing or losing it makes existing credentials undecryptable, so back it up with the database.

  • GRACEFUL_SHUTDOWN_TIMEOUT10Maximum time to wait for requests after a stop signal, positive integer in seconds.

    Maximum time to wait for requests after a stop signal, positive integer in seconds.

  • HOST127.0.0.1Native listening address, and the default host address for Compose's main port and OAuth callback ports; Compose always listens on `0.0.0.0` inside the container.

    Native listening address, and the default host address for Compose's main port and OAuth callback ports; Compose always listens on `0.0.0.0` inside the container.

  • HTTP_PROXYEmptyEnvironment proxy for HTTP upstream requests.

    Environment proxy for HTTP upstream requests.

  • HTTPS_PROXYEmptyEnvironment proxy for HTTPS upstream requests.

    Environment proxy for HTTPS upstream requests.

  • IDLE_TIMEOUT120HTTP keep-alive idle connection timeout, positive integer in seconds.

    HTTP keep-alive idle connection timeout, positive integer in seconds.

  • LOG_FORMATtextSupports `text` and `json`; any other value fails startup.

    Supports `text` and `json`; any other value fails startup.

  • LOG_LEVELinfoSupports `panic`, `fatal`, `error`, `warn`, `warning`, `info`, `debug`, and `trace`; invalid values warn and fall back to `info`.

    Supports `panic`, `fatal`, `error`, `warn`, `warning`, `info`, `debug`, and `trace`; invalid values warn and fall back to `info`.

  • MODELS_DEV_AUTO_SYNC_ENABLEDUnset, initial default trueWhen unset, uses the persisted management UI setting; when set, forces Models.dev auto-sync on or off and makes the same UI option read-only.

    When unset, uses the persisted management UI setting; when set, forces Models.dev auto-sync on or off and makes the same UI option read-only.

  • NO_PROXYEmptyComma-separated hosts, domains, or IPs that bypass the environment proxy.

    Comma-separated hosts, domains, or IPs that bypass the environment proxy.

  • OAUTH_CALLBACK_BIND_ADDRESSEmpty, inherits HOSTCompose only; overrides the host publishing address for the fixed OAuth callback ports `1455`, `54545`, and `51121`.

    Compose only; overrides the host publishing address for the fixed OAuth callback ports `1455`, `54545`, and `51121`.

  • PORT3001HTTP service port, must be `1–65535`; Compose also uses it for the container port, host publishing, and health check.

    HTTP service port, must be `1–65535`; Compose also uses it for the container port, host publishing, and health check.

  • READ_TIMEOUT60HTTP request read timeout, positive integer in seconds.

    HTTP request read timeout, positive integer in seconds.

  • TRUSTED_PROXIESEmptyOptional comma-separated proxy IPs or CIDRs; applies only with `CLIENT_IP_HEADER`. Empty means trusting the selected header directly, with trust enforced by your deployment. When set, only matching connection peers may supply the header; others use their connection IP. For `X-Forwarded-For`, scan right to left for the first untrusted IP (leftmost if all are trusted); without a list, use the leftmo

    Optional comma-separated proxy IPs or CIDRs; applies only with `CLIENT_IP_HEADER`. Empty means trusting the selected header directly, with trust enforced by your deployment. When set, only matching connection peers may supply the header; others use their connection IP. For `X-Forwarded-For`, scan right to left for the first untrusted IP (leftmost if all are trusted); without a list, use the leftmo

GPT-Load releases & version history

FAQ

Why did the account switch while quota was still available?

Displayed quota does not drive rotation order — upstream rate-limit responses put an account into cooldown immediately, though refreshing quota can release eligible cooldowns early.

Why does a subscription account ask for reauthorization?

Start with the Refresh credentials action on the account card, and reconnect or re-import the account if that still fails; refreshing quota does not bring authorization back.

Why can't a remote deployment complete OAuth authorization?

The browser resolves localhost to your own machine, so the redirect never reaches the server; paste the full callback URL back into the console or forward the port over SSH.

Why does streaming output disconnect midway?

Raise the stream idle timeout, and make sure a fronting reverse proxy does not buffer responses — Nginx for example needs proxy_buffering off.

Why do inference models always time out?

Reasoning-heavy models can think for a long time before producing output, so raise the first-byte timeout rather than the overall request timeout.

What happens when a credential gets blacklisted?

Blacklisting kicks in after a configurable run of consecutive failures; API-key groups are probed periodically and restored on a successful probe, while subscription credentials must be reauthorized or handled manually.

Why do occasional failures recover on retry?

That pattern is normally upstream rate limiting: only the throttled model cools down on that credential while its other models stay available, and adding credentials is the simplest mitigation.

How can a client use a different model name?

Define a model alias so the name the client asks for is forwarded as a different upstream model, letting you change providers without touching application code.

Why does the gateway say a model does not exist?

Use route inspection to find out whether the AccessKey, the Group or the credential is responsible, adjust the configuration, and run the check again.

Can I access the gateway remotely?

Loopback-only binding is deliberate; reach the console over SSH forwarding or a hardened reverse proxy instead of switching to 0.0.0.0, because an exposed UI leaks every stored credential.

Why do I get an unauthorized / 401 response?

Requests must carry a gateway AccessKey; the management key and upstream provider keys serve different purposes.

What should I check when a client cannot connect?

Probe the service with a /health request first; if that succeeds the problem is client-side, usually an extra or missing /v1 suffix on the URL.

Why does startup report a permission error?

The managed data directory must be owner-only; when ownership or link type cannot be verified the program refuses to start rather than loosening the checks, so inspect the DATA_DIR owner.

Why doesn't changing HOST take effect inside the container?

Inside the container HOST and DATA_DIR are pinned, and published host reachability is decided by Compose port mapping, where BIND_ADDRESS wins over HOST.

What if the port is already occupied?

Port 3001 is the default and can be moved with PORT in .env, but the subscription callback ports 1455, 54545 and 51121 are fixed upstream, so only one default Compose instance per host can do local callback authorization.

More in API Gateway

Rate this project

Click to rate — no account needed.

0 comments

Posting as guest
Loading comments…