Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ ansible-playbook -i inventory -K playbooks/lab-stack.yml --tags baseline
ansible-playbook -i inventory -K playbooks/lab-stack.yml --tags ocr
ansible-playbook -i inventory -K playbooks/lab-stack.yml --tags virtualbox
ansible-playbook -i inventory -K playbooks/lab-stack.yml --tags docker
ansible-playbook -i inventory -K playbooks/lab-stack.yml --tags local_llm
ansible-playbook -i inventory -K playbooks/lab-stack.yml --tags grobid
ansible-playbook -i inventory -K playbooks/lab-stack.yml --tags languagetool
ansible-playbook -i inventory -K playbooks/lab-stack.yml --tags quarto
Expand All @@ -107,6 +108,49 @@ ansible-playbook -i inventory -K playbooks/lab-stack.yml --tags desktop

You can also combine tags, e.g. `--tags baseline,docker,grobid,vscode`.


## Local LLMs (Ollama + Open WebUI)

This repository includes a `local_llm` role that runs:

- `ollama` (API on `127.0.0.1:11434`)
- `open-webui` (web UI on `http://127.0.0.1:3000`)

Both services run in Docker with persistent storage under `/opt/local-llm`:

- Ollama models: `/opt/local-llm/ollama`
- Open WebUI data: `/opt/local-llm/open-webui`

Install only this role:

```sh
ansible-playbook -i inventory -K playbooks/lab-stack.yml --tags local_llm
```

Optional model preloading (to avoid manual `ollama pull` after install):

```yaml
# host_vars/localhost.yml
local_llm_models:
- llama3.2:3b
```

Optional Open WebUI defaults:

```yaml
# host_vars/localhost.yml
local_llm_webui_auth: true
local_llm_webui_default_models: "llama3.2:3b"
```

Quick checks:

```sh
curl -s http://127.0.0.1:11434/api/tags | jq .
curl -I http://127.0.0.1:3000
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' | grep -E 'ollama|open-webui'
```

## Day-to-day

### Update software and configuration
Expand Down
3 changes: 3 additions & 0 deletions playbooks/lab-stack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
- role: docker
tags: [docker]

- role: local_llm
tags: [local_llm]

- role: grobid
tags: [grobid]

Expand Down
30 changes: 30 additions & 0 deletions roles/local_llm/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
local_llm_docker_packages:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
local_llm_python_packages:
- python3-docker

local_llm_data_dir: /opt/local-llm
local_llm_network_name: local-llm

local_llm_ollama_image: ollama/ollama:latest
local_llm_ollama_container_name: ollama
local_llm_ollama_port: 11434
local_llm_ollama_models_dir: "{{ local_llm_data_dir }}/ollama"

local_llm_webui_image: ghcr.io/open-webui/open-webui:main
local_llm_webui_container_name: open-webui
local_llm_webui_port: 3000
local_llm_webui_data_dir: "{{ local_llm_data_dir }}/open-webui"
local_llm_webui_auth: true
local_llm_webui_default_models: ""

# Keep empty by default to avoid pulling large models unintentionally.
# Example:
# local_llm_models:
# - llama3.2:3b
local_llm_models: []
77 changes: 77 additions & 0 deletions roles/local_llm/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
- name: Install Docker Engine packages
ansible.builtin.dnf:
name: "{{ local_llm_docker_packages }}"
state: present
update_cache: true
Comment on lines +2 to +6

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Configure Docker CE repository in local_llm role

This role installs docker-ce* packages immediately, but it never adds the Docker CE repository first. In this codebase, that bootstrap step exists only in roles/docker/tasks/main.yml (dnf config-manager addrepo ...), while the updated README now instructs running --tags local_llm standalone. On a fresh Fedora machine, that standalone path will fail during package resolution before containers are created. Add repo setup (or make local_llm depend on the docker role) before this install step.

Useful? React with 👍 / 👎.


- name: Install Python Docker SDK dependency for Ansible modules
ansible.builtin.dnf:
name: "{{ local_llm_python_packages }}"
state: present

- name: Enable and start Docker service
ansible.builtin.systemd:
name: docker
enabled: true
state: started

- name: Create local LLM data directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0755"
loop:
- "{{ local_llm_data_dir }}"
- "{{ local_llm_ollama_models_dir }}"
- "{{ local_llm_webui_data_dir }}"

- name: Ensure dedicated Docker network exists
community.docker.docker_network:
name: "{{ local_llm_network_name }}"
state: present

- name: Run Ollama container
community.docker.docker_container:
name: "{{ local_llm_ollama_container_name }}"
image: "{{ local_llm_ollama_image }}"
restart_policy: unless-stopped
state: started
recreate: true
published_ports:
- "127.0.0.1:{{ local_llm_ollama_port }}:11434"
volumes:
- "{{ local_llm_ollama_models_dir }}:/root/.ollama"
networks:
- name: "{{ local_llm_network_name }}"

- name: Wait for Ollama API to be reachable
ansible.builtin.wait_for:
host: 127.0.0.1
port: "{{ local_llm_ollama_port }}"
timeout: 120

- name: Run Open WebUI container
community.docker.docker_container:
name: "{{ local_llm_webui_container_name }}"
image: "{{ local_llm_webui_image }}"
restart_policy: unless-stopped
state: started
recreate: true
published_ports:
- "127.0.0.1:{{ local_llm_webui_port }}:8080"
volumes:
- "{{ local_llm_webui_data_dir }}:/app/backend/data"
env:
OLLAMA_BASE_URLS: "http://{{ local_llm_ollama_container_name }}:11434"
WEBUI_AUTH: "{{ local_llm_webui_auth | default('True') | string }}"
DEFAULT_MODELS: "{{ local_llm_webui_default_models | default('') | string }}"
networks:
- name: "{{ local_llm_network_name }}"

- name: Pull configured Ollama models (optional)
ansible.builtin.command: "docker exec {{ local_llm_ollama_container_name }} ollama pull {{ item }}"
loop: "{{ local_llm_models }}"
changed_when: false