Post

Open WebUI Self-Hosted Setup Guide (Docker + Ollama + PostgreSQL on Ubuntu)

Open WebUI Self-Hosted Setup Guide (Docker + Ollama + PostgreSQL on Ubuntu)

A minimal guide to running Open WebUI on a Linux (Ubuntu) server using a single docker-compose.yml, with Ollama running on the host and PostgreSQL as the database backend.


Requirements

  • Ubuntu Linux server
  • Docker installed
  • Ollama installed on the host (not as a container)

Why host-based Ollama?
Running Ollama directly on the host avoids the complexity of GPU passthrough, device mounts, and container-specific Ollama configuration. The WebUI container reaches it via host.docker.internal.


1. Install Ollama on the Host

1
curl -fsSL https://ollama.com/install.sh | sh

Expose Ollama to Docker containers

By default Ollama only listens on 127.0.0.1. The WebUI container needs to reach it, so bind Ollama to all interfaces.

Edit the systemd service override:

1
sudo systemctl edit ollama

Add the following and save:

1
2
[Service]
Environment="OLLAMA_HOST=0.0.0.0"

Restart the service:

1
2
sudo systemctl daemon-reload
sudo systemctl restart ollama

2. Open Required Firewall Ports

PortPurpose
11434Ollama API — Docker container → host
3000Open WebUI — browser → server
1
2
sudo ufw allow 11434
sudo ufw allow 3000

3. Create the Project Directory

1
2
mkdir -p ~/docker/open-webui
cd ~/docker/open-webui

4. Create docker-compose.yml

1
nano docker-compose.yml

Paste in the following, then save with Ctrl+OEnter and exit with Ctrl+X.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
networks:
  open-webui-net:
    name: open-webui-net

services:
  postgres:
    image: postgres:18
    container_name: open-webui-postgres
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - open-webui-pgdata:/var/lib/postgresql
    networks:
      - open-webui-net

  open-webui:
    image: ghcr.io/open-webui/open-webui:${WEBUI_TAG}
    container_name: open-webui
    restart: unless-stopped
    ports:
      - "${WEBUI_PORT}:8080"
    environment:
      WEBUI_SECRET_KEY: ${WEBUI_SECRET_KEY}
      WEBUI_AUTH: ${WEBUI_AUTH}
      WEBUI_ADMIN_EMAIL: ${WEBUI_ADMIN_EMAIL}
      WEBUI_ADMIN_PASSWORD: ${WEBUI_ADMIN_PASSWORD}
      # ENABLE_SIGNUP: ${ENABLE_SIGNUP}
      # DEFAULT_USER_ROLE: ${DEFAULT_USER_ROLE}
      CORS_ALLOW_ORIGIN: ${CORS_ALLOW_ORIGIN}
      OLLAMA_BASE_URL: ${OLLAMA_BASE_URL}
      DATABASE_URL: ${DATABASE_URL}
      HF_TOKEN: ${HF_TOKEN}
      USER_AGENT: ${USER_AGENT}
    volumes:
      - open-webui:/app/backend/data
    extra_hosts:
      - host.docker.internal:host-gateway
    depends_on:
      - postgres
    networks:
      - open-webui-net
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

volumes:
  open-webui:
    external: true
  open-webui-pgdata:
    external: true

5. Create .env

Use .env.example below as reference. Create your actual .env in the same directory:

1
nano .env

Fill in all required values (see the reference below), then save with Ctrl+OEnter and exit with Ctrl+X.

.env.example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# -------------------------------------------------------
# Open WebUI
# -------------------------------------------------------

# Port exposed on the host
WEBUI_PORT=3000

# Pin to a specific release tag — do not use "main" in production
# Check the version in the github repo: https://github.com/open-webui/open-webui
WEBUI_TAG=v0.8.10

# Generate a strong random string: openssl rand -hex 32
WEBUI_SECRET_KEY=

# First-run admin account — only used on initial startup
# If no admin exist in DB, this admin account is created automatically
WEBUI_ADMIN_EMAIL=
WEBUI_ADMIN_PASSWORD=

# Auth settings
WEBUI_AUTH=true

# Allow sign up, new users are assigned the 'user' role
# ENABLE_SIGNUP=true
# DEFAULT_USER_ROLE=user

# Only required if exposing via a public domain through Cloudflare Tunnel.
# Internally, traffic is routed via Pi-hole DNS + Nginx Proxy Manager.
# Set this to your public-facing domain (no trailing slash).
# e.g. https://chat.yourdomain.com
CORS_ALLOW_ORIGIN=

# HuggingFace API token — required only if downloading private or gated models
HF_TOKEN=

# Identifies this instance in HTTP request headers sent to external services
USER_AGENT=

# -------------------------------------------------------
# Ollama Backend
# -------------------------------------------------------

OLLAMA_BASE_URL=http://host.docker.internal:11434

# -------------------------------------------------------
# PostgreSQL
# -------------------------------------------------------

POSTGRES_USER=openwebui

# Generate a strong password: openssl rand -hex 24
POSTGRES_PASSWORD=

POSTGRES_DB=openwebui_db

# Must stay in sync with the three values above
# Format: postgresql://[POSTGRES_USER]:[POSTGRES_PASSWORD]@postgres:5432/[POSTGRES_DB]
DATABASE_URL=postgresql://xxxxxx:yyyyyyy@postgres:5432/zzzzzzz

Key values to fill in

VariableHow to generate / what to set
WEBUI_SECRET_KEYopenssl rand -hex 32
WEBUI_ADMIN_EMAILYour admin login email
WEBUI_ADMIN_PASSWORDA strong password
POSTGRES_PASSWORDopenssl rand -hex 24
DATABASE_URLReplace xxxxxx, yyyyyyy, zzzzzzz with your POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB

WEBUI_ADMIN_EMAIL and WEBUI_ADMIN_PASSWORD are only used on the very first startup to seed the admin account. If an admin already exists in the database, these values are ignored.


6. Create Docker Volumes

The docker-compose.yml declares both volumes as external: true, so they must exist before the stack starts. Run this once:

1
2
docker volume create open-webui
docker volume create open-webui-pgdata

7. Start the Stack

Make sure you are in the project directory (~/docker/open-webui) and both files are present:

1
2
ls
# docker-compose.yml  .env

Then bring up the stack:

1
docker compose up -d

Check that both containers are running:

1
docker compose ps

Tail the logs to confirm a clean startup:

1
docker compose logs -f open-webui

Look for a line indicating the server is listening (e.g. Uvicorn running on http://0.0.0.0:8080). The healthcheck gives the container up to 30 seconds before it starts probing.


8. Access Open WebUI

Open a browser and navigate to:

1
http://<your-server-ip>:3000

On first load, the admin account you set in .env is automatically created. Log in with WEBUI_ADMIN_EMAIL and WEBUI_ADMIN_PASSWORD.


Useful Commands

TaskCommand
Stop the stackdocker compose down
Restart a single servicedocker compose restart open-webui
Pull latest imagesdocker compose pull
View live logsdocker compose logs -f
Check Ollama is reachablecurl http://localhost:11434 (from host)

Troubleshooting

WebUI can’t reach Ollama
Confirm Ollama is bound to 0.0.0.0 (sudo systemctl show ollama | grep Environment) and that port 11434 is open in the firewall.

Container exits immediately
Check docker compose logs postgres — a missing or mismatched DATABASE_URL is the most common cause.

Admin account not created on first boot
Ensure WEBUI_AUTH=true and that WEBUI_ADMIN_EMAIL / WEBUI_ADMIN_PASSWORD are set before the first docker compose up. They have no effect after the initial database seed.

This post is licensed under CC BY 4.0 by the author.