From 11064eb2d2cc82009f99e3b2b56a596f77e77092 Mon Sep 17 00:00:00 2001 From: upmcplanetracker <219436948+upmcplanetracker@users.noreply.github.com> Date: Mon, 7 Sep 2026 14:29:36 -0400 Subject: [PATCH 1/2] Update __init__.py enabled DS support --- backend/open_webui/__init__.py | 38 +++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/backend/open_webui/__init__.py b/backend/open_webui/__init__.py index e803cea466..d50937500f 100644 --- a/backend/open_webui/__init__.py +++ b/backend/open_webui/__init__.py @@ -1,6 +1,7 @@ import base64 import os import random +import socket import sys from pathlib import Path from typing import Annotated @@ -14,6 +15,29 @@ KEY_FILE = Path.cwd() / '.webui_secret_key' DEFAULT_SECRET_KEY_LENGTH = 24 +def get_dual_stack_host(requested_host: str, port: int) -> str: + """ + Attempts to use requested_host (default '::' for dual-stack binding). + If '::' is used but IPv6 binding fails or is unavailable, falls back to '0.0.0.0'. + """ + if requested_host == '::': + if not socket.has_ipv6: + typer.echo('IPv6 not supported by system. Falling back to IPv4 (0.0.0.0).') + return '0.0.0.0' + + try: + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind(('::', port)) + sock.close() + return '::' + except OSError: + typer.echo(f'IPv6 binding failed on port {port}. Falling back to IPv4 (0.0.0.0).') + return '0.0.0.0' + + return requested_host + + def version_callback(value: bool) -> None: if value: from open_webui.env import VERSION @@ -34,7 +58,7 @@ def main( @app.command() def serve( - host: str = '0.0.0.0', + host: str = '::', port: int = 8080, ): os.environ['FROM_INIT_PY'] = 'true' @@ -77,13 +101,15 @@ def serve( from open_webui.env import UVICORN_WORKERS, UVICORN_WS_PER_MESSAGE_DEFLATE # On Windows, uvicorn's default loop factory hardcodes ProactorEventLoop, - # which is incompatible with psycopg v3 async. Setting loop='none' lets + # which is incompatible with psycopg v3 async. Setting loop='none' lets # asyncio.run() respect the WindowsSelectorEventLoopPolicy set in db.py. loop = 'none' if sys.platform == 'win32' else 'auto' + bind_host = get_dual_stack_host(host, port) + uvicorn.run( 'open_webui.main:app', - host=host, + host=bind_host, port=port, forwarded_allow_ips='*', workers=UVICORN_WORKERS, @@ -94,15 +120,17 @@ def serve( @app.command() def dev( - host: str = '0.0.0.0', + host: str = '::', port: int = 8080, reload: bool = True, ): from open_webui.env import UVICORN_WS_PER_MESSAGE_DEFLATE + bind_host = get_dual_stack_host(host, port) + uvicorn.run( 'open_webui.main:app', - host=host, + host=bind_host, port=port, reload=reload, forwarded_allow_ips='*', From 4f279fff0fb8472880963e3a4fb171bda4f842b0 Mon Sep 17 00:00:00 2001 From: upmcplanetracker <219436948+upmcplanetracker@users.noreply.github.com> Date: Mon, 7 Sep 2026 14:30:12 -0400 Subject: [PATCH 2/2] Update start.sh enabled ds supprot --- backend/start.sh | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/backend/start.sh b/backend/start.sh index ef7cb5af10..f86a957bfb 100755 --- a/backend/start.sh +++ b/backend/start.sh @@ -4,34 +4,46 @@ set -euo pipefail # --------------------------------------------------------------------------- # Container entry point for Open WebUI. # Handles secret key generation, optional Ollama/CUDA/Playwright setup, -# HuggingFace Space deployment, and launches the uvicorn server. +# HuggingFace Space deployment, dual-stack host binding, and launches the uvicorn server. # --------------------------------------------------------------------------- # Default optional env vars that we test below with bash's `,,` lowercase # expansion. The two can't be combined inline (`${VAR:-default,,}` makes # the default literal `,,`), so we normalise once up front and the simple # `${VAR,,}` form stays safe under `set -u` everywhere else. -: "${USE_SLIM_DOCKER:=}" "${WEB_LOADER_ENGINE:=}" "${USE_OLLAMA_DOCKER:=}" "${USE_CUDA_DOCKER:=}" +: "${WEB_LOADER_ENGINE:=}" "${USE_OLLAMA_DOCKER:=}" "${USE_CUDA_DOCKER:=}" SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) cd "$SCRIPT_DIR" || exit 1 # ── Playwright browser installation (if configured) ────────────────────────── -if [[ "${USE_SLIM_DOCKER,,}" != "true" && "${WEB_LOADER_ENGINE,,}" == "playwright" ]]; then +if [[ "${WEB_LOADER_ENGINE,,}" == "playwright" ]]; then if [[ -z "${PLAYWRIGHT_WS_URL:-}" ]]; then echo "Installing Playwright Chromium browser..." playwright install chromium playwright install-deps chromium fi + python -c "import nltk; nltk.download('punkt_tab')" fi -# ── Secret key setup ───────────────────────────────────────────────────────── +# ── Secret key & network setup ─────────────────────────────────────────────── KEY_FILE="${WEBUI_SECRET_KEY_FILE:-.webui_secret_key}" WEBUI_SECRET_KEY_LENGTH="${WEBUI_SECRET_KEY_LENGTH:-24}" PORT="${PORT:-8080}" -HOST="${HOST:-0.0.0.0}" +# Default to dual-stack IPv6/IPv4 binding '::' unless explicitly overridden +HOST="${HOST:-::}" + +PYTHON_CMD=$(command -v python3 || command -v python) + +# Probe IPv6 socket availability if HOST is set to '::' +if [[ "$HOST" == "::" ]]; then + if ! "$PYTHON_CMD" -c "import socket; s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM); s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1); s.bind(('::', ${PORT})); s.close()" 2>/dev/null; then + echo "WARNING: IPv6 binding failed or unavailable on port ${PORT}. Falling back to IPv4 (0.0.0.0)." + HOST="0.0.0.0" + fi +fi if [[ -z "${WEBUI_SECRET_KEY:-}" && -z "${WEBUI_JWT_SECRET_KEY:-}" ]]; then echo "No WEBUI_SECRET_KEY environment variable set, loading from file." @@ -71,11 +83,11 @@ if [[ -n "${SPACE_ID:-}" ]]; then if [[ -n "${ADMIN_USER_EMAIL:-}" && -n "${ADMIN_USER_PASSWORD:-}" ]]; then echo "Creating admin user for Space..." WEBUI_SECRET_KEY="${WEBUI_SECRET_KEY:-}" \ - uvicorn open_webui.main:app --host "$HOST" --port "$PORT" --forwarded-allow-ips "${FORWARDED_ALLOW_IPS:-*}" --ws-per-message-deflate "${UVICORN_WS_PER_MESSAGE_DEFLATE:-true}" & + "$PYTHON_CMD" -m uvicorn open_webui.main:app --host "$HOST" --port "$PORT" --forwarded-allow-ips "${FORWARDED_ALLOW_IPS:-*}" --ws-per-message-deflate "${UVICORN_WS_PER_MESSAGE_DEFLATE:-true}" & webui_pid=$! echo "Waiting for server to become healthy..." - until curl -sf "http://localhost:${PORT}/health" > /dev/null 2>&1; do + until curl -sf "http://localhost:${PORT}/health" > /dev/null 2>&1 || curl -sf "http://[::1]:${PORT}/health" > /dev/null 2>&1; do sleep 1 done @@ -95,7 +107,6 @@ fi # ── Launch uvicorn ─────────────────────────────────────────────────────────── -PYTHON_CMD=$(command -v python3 || command -v python) UVICORN_WORKERS="${UVICORN_WORKERS:-1}" if [[ "$#" -gt 0 ]]; then