Back to Openviking

Public Access & Reverse Proxy

docs/en/guides/12-public-access.md

0.3.194.4 KB
Original Source

Public Access & Reverse Proxy

OpenViking serves REST API, MCP, OAuth, .well-known/*, and Web Studio (/studio) on port 1933 by default. This guide shows how to put it behind a public HTTPS domain.

Why HTTPS: OAuth 2.1 / the MCP SDK require HTTPS for any non-localhost issuer — Claude.ai, Claude Desktop, ChatGPT, Cursor and other OAuth MCP clients refuse to connect over plain HTTP and report "Issuer URL must be HTTPS". API-key-only clients (including Claude Code with --header) work over HTTP, but TLS is still strongly recommended for production.

Prerequisites: a public domain, ports 80 + 443 reachable, DNS pointing at your host.

docker compose up already brings up a Caddy reverse-proxy container. Add a domain block to it and you get HTTPS on 443 with auto-renewal.

1. Create .env

dotenv
OPENVIKING_PUBLIC_BASE_URL=https://ov.your-domain.com
[email protected]   # optional; recommended for Let's Encrypt

OPENVIKING_PUBLIC_BASE_URL is read by both the OpenViking container (used as the issuer in OAuth metadata and WWW-Authenticate headers) and Caddy (as the HTTPS site address).

2. Add a domain block to Caddyfile

caddyfile
{$OPENVIKING_PUBLIC_BASE_URL} {
    reverse_proxy openviking:1933
    # Pin ACME registration email (optional):
    # tls {$OV_ACME_EMAIL}
}

3. Uncomment HTTPS lines in docker-compose.yml

Three places:

yaml
# In caddy.ports — uncomment:
- "80:80"
- "443:443"

# In caddy.volumes — uncomment:
- caddy_data:/data
- caddy_config:/config

# At the bottom — uncomment:
volumes:
  caddy_data:
  caddy_config:

4. Launch

bash
docker compose up -d

The first HTTPS request triggers ACME certificate issuance. Subsequent requests use the cached cert. Caddy handles renewal automatically.

5. Verify

bash
curl https://ov.your-domain.com/health
# {"status": "ok"}

# OAuth metadata (if oauth.enabled = true):
curl https://ov.your-domain.com/.well-known/oauth-authorization-server

# Open Studio in the browser:
open https://ov.your-domain.com/studio

Option B: bring your own reverse proxy

If you already run nginx / Traefik / Envoy / Cloudflare for TLS termination, point the upstream straight at OV's 1933.

nginx

nginx
server {
    listen 443 ssl http2;
    server_name ov.your-domain.com;

    ssl_certificate     /etc/letsencrypt/live/ov.your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ov.your-domain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:1933;
        proxy_set_header Host              $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host  $host;
    }
}

server {
    listen 80;
    server_name ov.your-domain.com;
    return 301 https://$host$request_uri;
}

Caddy (host install, no compose)

caddyfile
ov.your-domain.com {
    reverse_proxy 127.0.0.1:1933
}

Cloudflare / CDN

Point the CDN origin at http://your-server-ip:1933. Set OPENVIKING_PUBLIC_BASE_URL=https://ov.your-domain.com so the server knows its public address. Make sure the CDN forwards Host, X-Forwarded-Proto, and X-Forwarded-Host.

Telling the server its public URL

OAuth metadata, WWW-Authenticate headers, and resource URLs need to embed the public origin. Resolution order (highest to lowest):

  1. OPENVIKING_PUBLIC_BASE_URL environment variable
  2. oauth.issuer in ov.conf
  3. X-Forwarded-Proto + X-Forwarded-Host request headers
  4. The request's Host header

Behind any reverse proxy, set option 1 explicitly:

bash
export OPENVIKING_PUBLIC_BASE_URL="https://ov.your-domain.com"

or in ov.conf:

jsonc
{
  "oauth": {
    "enabled": true,
    "issuer": "https://ov.your-domain.com"
  }
}

Compatibility note: the :1934 single-upstream proxy

docker compose up also ships a Caddy reverse proxy on port 1934, simply reverse_proxy openviking:1933kept only for compatibility with deployments that already bookmarked 1934. New deployments can connect to 1933 directly; there is no routing value here. Remove the caddy service and the 1934 port mapping in docker-compose.yml if you don't need it.