AIAI Coding AgentsLLMDockerSelf-Hosting

Open WebUI + Ollama with Docker Compose: Full 2026 Setup (Linux, Windows, Mac)

Before you start

  • 18 min read

Boxes marked Run in terminal are what you type; plain text explains why. Check what you need first

What you get in one minute#

Open WebUI is a self-hosted chat page for AI models, and Ollama is the program that runs the models - together they give you a private ChatGPT-style assistant on your own computer.

Your chats, documents and users stay on your machine. You can upload files and ask questions about them, search the web, and give family or colleagues their own logins. This guide is the 2026 version: pinned image tags, a compose file without the obsolete version: line, and all data in one folder you can back up.

At a glance

  • You get: a working chat page at http://localhost:3000 with a local model, plus backups and an update routine.
  • Time needed: about 15 minutes, plus the model download.
  • Difficulty: easy. You copy a few commands and one file.
  • You need: Docker, a terminal, about 10 GB of free disk and 8 GB of RAM or more.
  • Optional: an NVIDIA GPU for speed; a domain name only for the HTTPS section.
WhatValue (read on 2026-10-26)
Open WebUIv0.12.4, released 2026-10-25
Ollamav0.40.11, released 2026-10-24
Images usedghcr.io/open-webui/open-webui:v0.12.4 and ollama/ollama:0.40.11 (both official)
Ports3000 on your computer (to Open WebUI), 11434 for Ollama (inside Docker only)
Docsdocs.openwebui.com and docs.ollama.com

Which version, and why: we checked the issue tracker, discussions and Reddit. We found no repeated reports of install failures or data loss for the 0.12 series, so this guide uses the latest release. It is only one day old, so the evidence is thin. If your first start shows a database migration error, restore your backup and pin v0.12.3 instead.

How it fits together#

You talk to Open WebUI in your browser, and Open WebUI asks Ollama to run the model.

How the stack fits together: you open Open WebUI in the browser on port 3000; Open WebUI asks the Ollama container on port 11434 to run a model; Open WebUI saves users, chats and documents in one folder and Ollama loads downloaded models from another folder; a GPU is optional

Not sure which compose variant to use? Follow this picture.

Which setup is yours: on a Mac, use the native Ollama app and run only Open WebUI in Docker; on Linux with an NVIDIA GPU, add the NVIDIA block to the compose file; everyone else uses the plain compose file

Before you start#

You need Docker with Compose v2 and a terminal.

LinuxWindowsMac
DockerDocker Engine with Compose v2Docker Desktop (WSL2 backend)Docker Desktop
Commanddocker composedocker composedocker compose
NVIDIA GPUNVIDIA Container ToolkitCheck Docker Desktop's GPU docsNot available in containers
AMD GPUollama/ollama:rocm imageNot covered hereNot available
Apple GPU--Use the native Ollama app

Check your tools:

Run in terminalbash
docker --version
docker compose version

Checklist:

  • [ ] Docker is running.
  • [ ] Port 3000 is free on your computer.
  • [ ] You have about 10 GB free disk for the images and one small model.
  • [ ] You can restart your machine's Docker if you add GPU support.

Tested on 2026-10-26: not run on a test machine. Every command and tag was checked against the official docs and the public registries (the image tags exist), as described in Sources.

Windows note: the Open WebUI docs advise running Docker commands from the WSL terminal when you use Docker with WSL. Docker Desktop also works from PowerShell, and the commands below use it. If something odd happens, try the same commands in a WSL terminal.

Step 1: Create the folders and a secret key#

Make one folder for everything and put a random secret key in a private .env file.

The key keeps people logged in. If you leave it out, everyone is logged out whenever the container is recreated.

Linux#

Run in terminalbash
mkdir -p ~/docker_volumes/open-webui/{ollama,open-webui}
cd ~/docker_volumes/open-webui
cat > .env <<EOF
DATA_ROOT=$HOME/docker_volumes/open-webui
WEBUI_SECRET_KEY=$(openssl rand -hex 32)
EOF
chmod 600 .env

Windows (PowerShell)#

Codepowershell
$d = "$HOME\docker_volumes\open-webui"
New-Item -ItemType Directory -Force "$d\ollama","$d\open-webui" | Out-Null
Set-Location $d
$b = New-Object byte[] 32
[Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b)
$key = -join ($b | ForEach-Object { '{0:x2}' -f $_ })
Set-Content .env "DATA_ROOT=$($d -replace '\\','/')`nWEBUI_SECRET_KEY=$key"

Mac (Terminal)#

Same as Linux:

Run in terminalbash
mkdir -p ~/docker_volumes/open-webui/{ollama,open-webui}
cd ~/docker_volumes/open-webui
cat > .env <<EOF
DATA_ROOT=$HOME/docker_volumes/open-webui
WEBUI_SECRET_KEY=$(openssl rand -hex 32)
EOF
chmod 600 .env

Check it worked: ls -a (Windows: Get-ChildItem -Force) shows .env, ollama and open-webui. Never put .env in Git.

Step 2: Save the compose file#

Create docker-compose.yml in that folder. The file content is the same on all three systems, except the Mac note below. Save it with your editor, or with the commands here.

Linux#

Open docker-compose.yml in your editor (nano docker-compose.yml) and paste this:

File contentyaml
services:
  ollama:
    image: ollama/ollama:0.40.11
    container_name: ollama
    restart: unless-stopped
    volumes:
      - ${DATA_ROOT}/ollama:/root/.ollama
    # NVIDIA GPU (Linux): remove the # signs in front of the next 7 lines
    # deploy:
    #   resources:
    #     reservations:
    #       devices:
    #         - driver: nvidia
    #           count: all
    #           capabilities: [gpu]

  open-webui:
    image: ghcr.io/open-webui/open-webui:v0.12.4
    container_name: open-webui
    restart: unless-stopped
    depends_on:
      - ollama
    ports:
      - "127.0.0.1:3000:8080"
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
      - WEBUI_SECRET_KEY=${WEBUI_SECRET_KEY}
    volumes:
      - ${DATA_ROOT}/open-webui:/app/backend/data

Windows (PowerShell)#

Run notepad docker-compose.yml, paste the same content, and save it in $HOME\docker_volumes\open-webui. Make sure Notepad does not add .txt to the name.

Mac (Terminal)#

Same as Linux: nano docker-compose.yml, paste the same content, save.

What the important lines do:

LineMeaning
ghcr.io/open-webui/open-webui:v0.12.4The official image, pinned. :main and :latest are rolling builds, not stable releases.
127.0.0.1:3000:8080Only this computer can open the page. Remove 127.0.0.1: to allow your home network.
OLLAMA_BASE_URLTells Open WebUI where Ollama is, by its service name.
${DATA_ROOT}/...Your data lives in ~/docker_volumes/open-webui, easy to back up.

Mac: Docker on a Mac cannot use the Apple GPU, so Ollama in a container is CPU only. For real speed, install the native Ollama app, delete the whole ollama: service and the depends_on block, and set OLLAMA_BASE_URL=http://host.docker.internal:11434. This also works on Linux and Windows when Ollama runs on the host.

AMD GPU (Linux): use the image ollama/ollama:0.40.11-rocm (the tag exists) and pass the devices /dev/kfd and /dev/dri to the container, as the Ollama docs describe.

NVIDIA GPU (Linux): install the NVIDIA Container Toolkit, then run sudo nvidia-ctk runtime configure --runtime=docker and restart Docker. After that, uncomment the deploy: block above.

Step 3: Start it#

Start both containers and watch the log until Open WebUI is ready.

Linux#

Run in terminalbash
docker compose up -d
docker compose ps
docker compose logs -f open-webui

Windows (PowerShell)#

Run in terminalpowershell
docker compose up -d
docker compose ps
docker compose logs -f open-webui

Mac (Terminal)#

Same commands as Linux:

Run in terminalbash
docker compose up -d
docker compose ps
docker compose logs -f open-webui

Check it worked: docker compose ps lists ollama and open-webui as running. Press Ctrl+C to leave the log. The first start can take a minute while the database is prepared.

Potential issue: if port 3000 is taken, change the left number in 127.0.0.1:3000:8080, for example to 3001.

Step 4: Download a model#

Pull one small model so you can chat right away.

Linux#

Run in terminalbash
docker exec -it ollama ollama pull llama3.2
docker exec ollama ollama list

Windows (PowerShell)#

Run in terminalpowershell
docker exec -it ollama ollama pull llama3.2
docker exec ollama ollama list

Mac (Terminal)#

Same commands as Linux, but if you use the native Ollama app, run ollama pull llama3.2 directly.

Run in terminalbash
docker exec -it ollama ollama pull llama3.2
docker exec ollama ollama list

Check it worked: ollama list shows llama3.2. You can also pull models later from inside Open WebUI.

Step 5: First-run setup#

Open the page, create the admin account and pick your model.

Same on all three systems, in your browser:

Linux#

  1. Open http://localhost:3000.
  2. Click Get started and sign up. The first account becomes the administrator. Use a strong password and store it safely: if you lose it, a new account needs admin activation, so there is no easy way back (the docs have a password-reset guide).
  3. Open Admin Settings > Connections. The Ollama address should read http://ollama:11434.
  4. Choose llama3.2 in the model picker and send a message.

Windows (PowerShell)#

Same steps. Open http://localhost:3000 in your browser. Check the log with docker compose logs open-webui --tail 20.

Mac (Terminal)#

Same steps. If you use the native Ollama app, the connection address is http://host.docker.internal:11434.

Check it worked: the model answers, and docker compose logs open-webui --tail 20 shows no errors.

Put it behind HTTPS (with or without Traefik)#

With Traefik#

This section is only for a server with a domain name; if you use Open WebUI at home, skip it.

You need a domain pointing at your server, ports 80 and 443 open, and Traefik already running with a websecure entrypoint and a certificate resolver (here called letsencrypt). Create the shared network once if it is missing:

Run in terminalbash
docker network create proxy

Change the open-webui service: remove ports: and add the lines below. The label names come from Traefik's Docker provider; the Open WebUI settings are from its docs. Adjust the entrypoint and resolver names to match your Traefik setup.

File contentyaml
  open-webui:
    # ...image, volumes and the rest stay as before; delete the ports: block
    networks: [proxy]
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
      - WEBUI_SECRET_KEY=${WEBUI_SECRET_KEY}
      - WEBUI_URL=https://ai.<your-domain>
      - CORS_ALLOW_ORIGIN=https://ai.<your-domain>
      - WEBUI_SESSION_COOKIE_SECURE=true
      - WEBUI_AUTH_COOKIE_SECURE=true
    labels:
      - traefik.enable=true
      - traefik.docker.network=proxy
      - traefik.http.routers.owui.rule=Host(`ai.<your-domain>`)
      - traefik.http.routers.owui.entrypoints=websecure
      - traefik.http.routers.owui.tls=true
      - traefik.http.routers.owui.tls.certresolver=letsencrypt
      - traefik.http.services.owui.loadbalancer.server.port=8080

Also add the network at the bottom of the file, and keep ollama on the default network so it is never reachable from outside:

File contentyaml
networks:
  proxy:
    external: true

Because open-webui now sits on proxy only, it can no longer reach ollama. Put it on both: networks: [proxy, default].

Open WebUI needs WebSocket support (Traefik does this by default). If streamed answers look garbled, check Traefik's compression and buffering middleware.

Without Traefik: Caddy#

Choose this if you have no Traefik yet and run one small server. Caddy is one small program that gets and renews certificates by itself.

Your caseWhat Caddy doesWhat you do
Public domainAutomatic Let's Encrypt certificatePoint a DNS A record at the server; open ports 80 and 443
Home only, no domaintls internal: Caddy's own local certificateTrust Caddy's root certificate on each device (below)

Caddyfile for Open WebUI (it listens on 127.0.0.1:3000 in this guide):

File contenttext
ai.example.com {
    reverse_proxy localhost:3000
}

Home-only variant (point the name ai.home.arpa at the server in your router, Pi-hole or hosts file):

File contenttext
ai.home.arpa {
    tls internal
    reverse_proxy localhost:3000
}

Run Caddy (Caddyfile in /etc/caddy/Caddyfile on Linux):

  • Linux: install from the official apt repository (see caddyserver.com/docs/install), save the Caddyfile, then sudo systemctl reload caddy.
  • Windows (PowerShell): winget install CaddyServer.Caddy, then caddy run --config Caddyfile in the folder with your Caddyfile.
  • Mac: brew install caddy, then caddy run --config Caddyfile.
  • Docker: add this service to the same compose file and put the Caddyfile in ~/docker_volumes/caddy/conf/ (create it first: Linux/Mac mkdir -p ~/docker_volumes/caddy/conf; Windows PowerShell New-Item -ItemType Directory -Force "$HOME\docker_volumes\caddy\conf"). Inside Docker, use reverse_proxy open-webui:8080 in the Caddyfile instead of localhost:3000.
File contentyaml
  caddy:
    image: caddy:2.11.7
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ~/docker_volumes/caddy/conf:/etc/caddy:ro
      - ~/docker_volumes/caddy/data:/data

Home-only: trust Caddy's root certificate. It is pki/authorities/local/root.crt in Caddy's data folder.

  • Linux: on the Caddy machine run sudo caddy trust. On other Linux devices copy root.crt to /usr/local/share/ca-certificates/ and run sudo update-ca-certificates.
  • Windows: caddy trust in an administrator PowerShell on the Caddy machine. On other devices double-click root.crt and install it into "Trusted Root Certification Authorities".
  • Mac: caddy trust on the Caddy machine. On other Macs add root.crt in Keychain Access and set it to "Always Trust".

Open WebUI streams answers over WebSockets; Caddy handles that by default. Set WEBUI_URL and CORS_ALLOW_ORIGIN in the compose file to the https address (for example https://ai.example.com), as in the Traefik example above.

Check it worked: curl -I https://ai.example.com answers HTTP/2 200.

Security and testing#

Keep it off the internet unless you have a reason, and protect the secret key.

Do thisWhy
Keep 127.0.0.1: in the port mappingOnly your computer can open the page
chmod 600 .env and keep it out of GitIt holds the secret key
For remote access, use a VPN such as WireGuard or TailscaleSafer than opening port 3000
Never set WEBUI_AUTH=False on a shared machineSingle-user mode removes the login, and you cannot switch back to multi-user
Turn on two-factor sign-in if your version offers itAdded in v0.12.0 as ENABLE_MFA

Tests:

Run in terminalbash
docker compose ps
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:3000/
curl -s http://localhost:3000/api/config

You should get status 200 and a JSON answer. With Traefik: curl -I https://ai.<your-domain>. On Windows use curl.exe instead of curl.

Upload a document in a chat, and Open WebUI finds the relevant parts and gives them to the model.

The standard image bundles what RAG needs, so it works out of the box. The :slim image (about 197 MB against 1.62 GB for the standard one, per the docs on 2026-10-16) leaves embeddings, PDF reading and speech to external services. Without them, uploads fail with a 503 error. This guide uses the standard image.

  • Chat with documents: click + in the chat and attach a file, or add files under Workspace > Knowledge.
  • Web search: set a provider in Admin Settings > Web Search. On :slim the built-in DDGS provider is greyed out.
  • Tools for your model: to let a local model call tools, see our tool-server guide for Open WebUI.

Users and permissions#

Admins manage everything; everyone else gets only what you allow.

  • Sign-ups: in Admin Settings decide whether new users can register and whether the admin must approve them.
  • Groups: use Admin Settings > Groups to give people different permissions and model access.
  • Hide controls: v0.12.4 added Visible controls (UI_VISIBILITY) so you can hide single buttons from users. Admins still see everything.
  • Two-factor sign-in: optional since v0.12.0.

What changed since 2025#

The big ones are the licence, a desktop app and the 0.12 features.

ChangeWhat it means for you
Licence (since v0.6.6, 19 Apr 2025)Code up to v0.6.5 stays BSD-3. Newer code adds a branding clause (below).
Desktop appAn "Early Alpha" native app exists for Windows and macOS with no Docker. It is an alternative, not a replacement. For a server, use Docker.
v0.12.x featuresRealtime voice calls, two-factor sign-in, replies in shared chats, skills loaded on demand, hidden controls for users
Image :slimA small image that needs external services for RAG and speech
Image tags:main and :latest are rolling; pin a vX.Y.Z tag

The branding rule in plain words: do not change, remove or hide the "Open WebUI" name or logo. You may rebrand if you have 50 or fewer end users in a rolling 30 days, have written permission as a substantive contributor, or hold an enterprise licence. A small banner like "Managed by YourOrg" is allowed; "Powered by Open WebUI" alone as a rebrand is not. The 50 is not a user cap - it only decides whether you may rebrand. This is not legal advice.

Other layouts#

You wantChange
Ollama already runs on the hostDelete the ollama: service, set OLLAMA_BASE_URL=http://host.docker.internal:11434, and add extra_hosts: ["host.docker.internal:host-gateway"] on Linux. Ollama must listen on 0.0.0.0 (OLLAMA_HOST).
Ollama on another machineSet OLLAMA_BASE_URL=http://<server-address>:11434
One all-in-one containerThe ollama image variant exists, but separate containers let you pin and update each one
OpenAI-compatible cloud models tooAdd the connection in Admin Settings > Connections

Troubleshooting#

ProblemSolution
Page says "Open WebUI backend required"The browser could not reach the backend. Map to container port 8080, test curl http://localhost:3000/api/config, then open a private window
No models listed, cannot reach OllamaUse http://ollama:11434 in Compose. For a host Ollama use host.docker.internal
Connect call failed ('127.0.0.1', ...)localhost inside the container is the container itself. Use a service name or host.docker.internal
https://yourdomain.com is not an accepted originAdd every address you use to CORS_ALLOW_ORIGIN, separated by semicolons
Garbled ## and ** while streamingProxy buffering. In nginx set proxy_buffering off
Model picker spins, /api/models returns 500An endpoint is unreachable. Remove it or lower AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST
Upload returns 503You use :slim without an external embedding service. Use the standard image
no such table or table ... already existsA migration did not finish. Stop, restore your backup, start again
GPU unusedToolkit missing or Docker not restarted after nvidia-ctk runtime configure
Everyone logged out after recreatingWEBUI_SECRET_KEY is missing or changes. Keep it fixed in .env
Saved tool connections stop working after an updateENABLE_DIRECT_INTEGRATIONS defaults to off. Enable it in Admin Settings > Integrations

Self-signed certificates#

If you see [SSL: CERTIFICATE_VERIFY_FAILED] with a document parser, embedding service or web loader, set REQUESTS_VERIFY=false, AIOHTTP_CLIENT_SESSION_SSL=false or ENABLE_WEB_LOADER_SSL_VERIFICATION=false. Only do this on a network you trust.

Known bugs#

One failed-migration report (table chat has no column named timer_at, issue #29290, 31 Aug 2026) was on a dev build, not a stable release. The fix is the same: restore your backup.

Diagnostics#

Run in terminalbash
docker compose ps
docker logs open-webui --tail 100
docker logs ollama --tail 100
docker inspect open-webui

Updates and backups#

Back up first, then change the tag, pull and restart. Database migrations run at start-up and cannot be undone by rolling back the container, so the backup is your way back.

  1. Read the release notes.
  2. Make a backup (below).
  3. Edit the image tag in docker-compose.yml.
  4. Run docker compose pull && docker compose up -d.
  5. Check docker compose logs open-webui --tail 20 and log in.

Ollama v0.40.2 and later upgrade old models in the background and keep backup copies on disk. If you go back below 0.40, you must pull those models again.

Linux#

Run in terminalbash
cd ~/docker_volumes/open-webui
mkdir -p backups
docker compose stop open-webui
tar czf backups/owui-$(date +%F).tgz open-webui .env docker-compose.yml
docker compose start open-webui

Schedule it every night at 03:00 with crontab -e:

File contentbash
0 3 * * * cd $HOME/docker_volumes/open-webui && docker compose stop open-webui && mkdir -p backups && tar czf backups/owui-$(date +\%F).tgz open-webui .env docker-compose.yml && docker compose start open-webui

Windows (PowerShell)#

Codepowershell
Set-Location "$HOME\docker_volumes\open-webui"
New-Item -ItemType Directory -Force backups | Out-Null
docker compose stop open-webui
tar -czf "backups\owui-$(Get-Date -Format yyyy-MM-dd).tgz" open-webui .env docker-compose.yml
docker compose start open-webui

Run it every night with Task Scheduler: create a basic task at 03:00 that starts pwsh.exe with -File pointing to a script holding the lines above.

Mac (Terminal)#

Same commands as Linux. For the schedule, use crontab -e the same way.

Restore test#

A backup you never restored is a hope, not a backup. Unpack it into a scratch folder and start a throwaway copy on another port.

Run in terminalbash
mkdir -p /tmp/restore && tar xzf backups/owui-*.tgz -C /tmp/restore
docker run --rm -d --name owui-test -p 3100:8080 \
  -e WEBUI_SECRET_KEY=test \
  -v /tmp/restore/open-webui:/app/backend/data \
  ghcr.io/open-webui/open-webui:v0.12.4

Open http://localhost:3100, log in with your normal account and check your chats. Then remove the test copy:

Run in terminalbash
docker rm -f owui-test

On Windows, unpack with tar -xzf into a scratch folder and use that folder in the -v option. The Ollama folder holds large models you can pull again, so back it up only if your bandwidth is limited.

Performance and resources#

The model, not Open WebUI, uses most of your memory.

  • Open WebUI itself is light: the docs measured about 300 MiB of RAM idle for the slim image on v0.12.3. That is not a minimum, and the standard image uses more.
  • Pick a model that fits your GPU memory or RAM. A small 3B model runs on 8 GB; larger models need more.
  • The :cuda Open WebUI image only moves Open WebUI's own embedding, reranking and speech models to the GPU. Ollama needs its own GPU access.
  • On a low-power machine with SQLite, keep DATABASE_ENABLE_SESSION_SHARING off.
  • Watch usage live with:
Run in terminalbash
docker stats

FAQ#

Is it free for a company? Yes, but keep the "Open WebUI" branding unless you have 50 or fewer end users, written permission, or an enterprise licence.

Why not use :main or :latest? They are rolling builds from the main branch, not stable releases. A pinned vX.Y.Z tag never changes under you.

Can I run it without Docker? Yes, with pip install open-webui on Python 3.11 or 3.12. Python 3.13 is not supported. Docker is the recommended way.

Will updating delete my chats? No, your data lives in the mounted folder. The risk is rolling back after a migration, so always keep a backup.

Should I use the all-in-one Ollama image? It works, but separate containers let you update and pin each program on its own, which is why this guide uses two.

Sources#

Checked against the official docs on 2026-10-26. Versions and release dates were confirmed with the GitHub API, and the image tags v0.12.4 (GHCR) and 0.40.11 and 0.40.11-rocm (Docker Hub) were confirmed to exist. Not run end to end by DIY Lab Hub. Items we could not confirm: whether Docker Desktop passes an NVIDIA GPU to containers on Windows (see Docker's GPU docs), and the Reddit and GitHub comment threads for 0.12.x, which were not read in full.