Manage Ollama setup for homelab host

This commit is contained in:
juvdiaz 2026-06-27 23:55:21 -06:00
parent 172bcca975
commit 5504dd7b2e
7 changed files with 172 additions and 4 deletions

View File

@ -853,6 +853,15 @@ other nodes can reach `OLLAMA_HOST=http://192.168.100.73:11434`:
./jeannie website-ollama-listen ./jeannie website-ollama-listen
``` ```
The Debian host bootstrap also manages Ollama when `debian_pc_install_ollama`
is true: it installs the service, stores models under `/data/ollama/models`,
binds the API to the configured LAN listener, and pulls the lightweight
`ai_gateway.model`. On an existing host, apply just this setup with:
```bash
./jeannie ollama-setup
```
`jeannie` also has a backstage local AI helper for doctor commands. `jeannie` also has a backstage local AI helper for doctor commands.
It is not a separate CLI action; when `ai_gateway.enabled` is true in It is not a separate CLI action; when `ai_gateway.enabled` is true in
`homelab.yml`, doctor commands try the configured Ollama model and silently `homelab.yml`, doctor commands try the configured Ollama model and silently

View File

@ -853,6 +853,15 @@ other nodes can reach `OLLAMA_HOST=http://192.168.100.73:11434`:
./{{ main_script }} website-ollama-listen ./{{ main_script }} website-ollama-listen
``` ```
The Debian host bootstrap also manages Ollama when `debian_pc_install_ollama`
is true: it installs the service, stores models under `/data/ollama/models`,
binds the API to the configured LAN listener, and pulls the lightweight
`ai_gateway.model`. On an existing host, apply just this setup with:
```bash
./{{ main_script }} ollama-setup
```
`{{ main_script }}` also has a backstage local AI helper for doctor commands. `{{ main_script }}` also has a backstage local AI helper for doctor commands.
It is not a separate CLI action; when `ai_gateway.enabled` is true in It is not a separate CLI action; when `ai_gateway.enabled` is true in
`homelab.yml`, doctor commands try the configured Ollama model and silently `homelab.yml`, doctor commands try the configured Ollama model and silently

View File

@ -31,6 +31,8 @@ The playbook:
- installs user-level pipx CLI tools such as `yt-dlp`, plus runtime helpers - installs user-level pipx CLI tools such as `yt-dlp`, plus runtime helpers
such as `ffmpeg` and `nodejs` such as `ffmpeg` and `nodejs`
- installs `age` and `sops` for encrypted repo-managed secrets - installs `age` and `sops` for encrypted repo-managed secrets
- installs and configures Ollama for homelab-local AI helpers when
`debian_pc_install_ollama` is true
- installs Zoom from the upstream `.deb` when requested on amd64 - installs Zoom from the upstream `.deb` when requested on amd64
- adds `jv` to expected local groups - adds `jv` to expected local groups
- enables core services such as Docker, containerd, kubelet, ssh, cron, and - enables core services such as Docker, containerd, kubelet, ssh, cron, and
@ -48,3 +50,13 @@ After the playbook finishes, initialize the Debian host's SOPS identity with:
./jeannie secrets-init ./jeannie secrets-init
./jeannie secrets-check ./jeannie secrets-check
``` ```
Ollama is configured to listen on the LAN and keep models under
`/data/ollama/models`. The default lightweight model for `jeannie` diagnostics is
pulled during bootstrap. To apply only the Ollama setup on an existing Debian
host, run:
```bash
./jeannie ollama-setup
./jeannie ai-check
```

View File

@ -3,10 +3,16 @@ debian_pc_user: jv
debian_pc_user_home: "/home/{{ debian_pc_user }}" debian_pc_user_home: "/home/{{ debian_pc_user }}"
debian_pc_kubernetes_minor_version: v1.36 debian_pc_kubernetes_minor_version: v1.36
debian_pc_enable_passwordless_sudo: true debian_pc_enable_passwordless_sudo: true
debian_pc_install_ollama: true
debian_pc_ollama_install_url: https://ollama.com/install.sh
debian_pc_ollama_host: 0.0.0.0:11434
debian_pc_ollama_models_dir: /data/ollama/models
debian_pc_ollama_default_model: qwen2.5:0.5b
debian_pc_persistent_directories: debian_pc_persistent_directories:
- /data/openebs/local/registry - /data/openebs/local/registry
- /data/docker - /data/docker
- /data/ollama/models
- "/home/{{ debian_pc_user }}/git-server" - "/home/{{ debian_pc_user }}/git-server"
- "/home/{{ debian_pc_user }}/backups/gitea" - "/home/{{ debian_pc_user }}/backups/gitea"
- "/home/{{ debian_pc_user }}/backups/gitea-restore-drills" - "/home/{{ debian_pc_user }}/backups/gitea-restore-drills"

View File

@ -229,6 +229,54 @@
mode: "{{ '0755' if item.startswith(debian_pc_user_home) else '0775' }}" mode: "{{ '0755' if item.startswith(debian_pc_user_home) else '0775' }}"
loop: "{{ debian_pc_persistent_directories }}" loop: "{{ debian_pc_persistent_directories }}"
- name: Check whether Ollama is already installed
ansible.builtin.shell:
cmd: command -v ollama >/dev/null 2>&1
register: debian_pc_ollama_installed
changed_when: false
failed_when: false
when: debian_pc_install_ollama | bool
- name: Install Ollama from official Linux installer
ansible.builtin.shell: |
set -euo pipefail
tmp_installer="$(mktemp)"
trap 'rm -f "${tmp_installer}"' EXIT
curl -fsSL "{{ debian_pc_ollama_install_url }}" -o "${tmp_installer}"
sh "${tmp_installer}"
args:
executable: /bin/bash
when:
- debian_pc_install_ollama | bool
- debian_pc_ollama_installed.rc | default(1) != 0
- name: Ensure Ollama model storage exists on data disk
ansible.builtin.file:
path: "{{ debian_pc_ollama_models_dir }}"
state: directory
owner: ollama
group: ollama
mode: "0775"
when: debian_pc_install_ollama | bool
- name: Ensure Ollama systemd override directory exists
ansible.builtin.file:
path: /etc/systemd/system/ollama.service.d
state: directory
mode: "0755"
when: debian_pc_install_ollama | bool
- name: Configure Ollama for homelab LAN access and data storage
ansible.builtin.copy:
dest: /etc/systemd/system/ollama.service.d/homelab.conf
mode: "0644"
content: |
[Service]
Environment="OLLAMA_HOST={{ debian_pc_ollama_host }}"
Environment="OLLAMA_MODELS={{ debian_pc_ollama_models_dir }}"
register: debian_pc_ollama_override
when: debian_pc_install_ollama | bool
- name: Enable expected services when installed - name: Enable expected services when installed
ansible.builtin.systemd: ansible.builtin.systemd:
name: "{{ item }}" name: "{{ item }}"
@ -237,6 +285,42 @@
loop: "{{ debian_pc_enable_services }}" loop: "{{ debian_pc_enable_services }}"
failed_when: false failed_when: false
- name: Enable and start Ollama
ansible.builtin.systemd:
name: ollama
enabled: true
state: "{{ 'restarted' if debian_pc_ollama_override.changed | default(false) else 'started' }}"
daemon_reload: true
when: debian_pc_install_ollama | bool
- name: Wait for Ollama API
ansible.builtin.uri:
url: http://127.0.0.1:11434/api/tags
method: GET
status_code: 200
register: debian_pc_ollama_api
retries: 12
delay: 5
until: debian_pc_ollama_api.status == 200
when: debian_pc_install_ollama | bool
- name: Pull default Ollama model for backstage homelab diagnostics
ansible.builtin.shell: |
set -euo pipefail
if ollama list | awk -v model="{{ debian_pc_ollama_default_model }}" 'NR > 1 && ($1 == model || $1 == model ":latest") { found = 1 } END { exit found ? 0 : 1 }'; then
echo present
exit 0
fi
ollama pull "{{ debian_pc_ollama_default_model }}"
echo pulled
args:
executable: /bin/bash
register: debian_pc_ollama_pull_result
changed_when: "'pulled' in debian_pc_ollama_pull_result.stdout"
environment:
OLLAMA_HOST: http://127.0.0.1:11434
when: debian_pc_install_ollama | bool
- name: Check whether config archive is present - name: Check whether config archive is present
ansible.builtin.stat: ansible.builtin.stat:
path: "{{ debian_pc_config_archive }}" path: "{{ debian_pc_config_archive }}"

View File

@ -86,6 +86,8 @@ services:
ollama: ollama:
host: debian host: debian
url: http://192.168.100.73:11434 url: http://192.168.100.73:11434
bind_address: 0.0.0.0:11434
models_dir: /data/ollama/models
rpi_dns: rpi_dns:
host: rpi4 host: rpi4
pihole_dns_port: 53 pihole_dns_port: 53

54
jeannie
View File

@ -58,6 +58,8 @@ mapping = {
"services.gitea.root_url": "LAB_GITEA_ROOT_URL", "services.gitea.root_url": "LAB_GITEA_ROOT_URL",
"services.gitea.ssh_remote": "LAB_GITOPS_REPO_URL", "services.gitea.ssh_remote": "LAB_GITOPS_REPO_URL",
"services.local_registry.endpoint": "LAB_REGISTRY_ENDPOINT", "services.local_registry.endpoint": "LAB_REGISTRY_ENDPOINT",
"services.ollama.bind_address": "LAB_OLLAMA_BIND_ADDRESS",
"services.ollama.models_dir": "LAB_OLLAMA_MODELS_DIR",
"services.rpi_dns.pihole_web_port": "PIHOLE_WEB_PORT", "services.rpi_dns.pihole_web_port": "PIHOLE_WEB_PORT",
"services.rpi_dns.uptime_kuma_port": "UPTIME_KUMA_PORT", "services.rpi_dns.uptime_kuma_port": "UPTIME_KUMA_PORT",
"ai_gateway.provider": "LAB_AI_GATEWAY_PROVIDER", "ai_gateway.provider": "LAB_AI_GATEWAY_PROVIDER",
@ -3389,9 +3391,10 @@ website_translation_model() {
} }
website_ollama_listen() { website_ollama_listen() {
local bind_address="${WEBSITE_OLLAMA_BIND_ADDRESS:-0.0.0.0:11434}" local bind_address="${WEBSITE_OLLAMA_BIND_ADDRESS:-${LAB_OLLAMA_BIND_ADDRESS:-0.0.0.0:11434}}"
local models_dir="${LAB_OLLAMA_MODELS_DIR:-/data/ollama/models}"
local dropin_dir="/etc/systemd/system/ollama.service.d" local dropin_dir="/etc/systemd/system/ollama.service.d"
local dropin_file="${dropin_dir}/homelab-listen.conf" local dropin_file="${dropin_dir}/homelab.conf"
require_debian_server "website-ollama-listen" require_debian_server "website-ollama-listen"
@ -3402,7 +3405,11 @@ website_ollama_listen() {
echo "Configuring ollama.service to listen on ${bind_address}..." echo "Configuring ollama.service to listen on ${bind_address}..."
sudo mkdir -p "${dropin_dir}" sudo mkdir -p "${dropin_dir}"
printf '[Service]\nEnvironment="OLLAMA_HOST=%s"\n' "${bind_address}" | sudo mkdir -p "${models_dir}"
if id ollama >/dev/null 2>&1; then
sudo chown ollama:ollama "${models_dir}"
fi
printf '[Service]\nEnvironment="OLLAMA_HOST=%s"\nEnvironment="OLLAMA_MODELS=%s"\n' "${bind_address}" "${models_dir}" |
sudo tee "${dropin_file}" >/dev/null sudo tee "${dropin_file}" >/dev/null
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl restart ollama.service sudo systemctl restart ollama.service
@ -3413,6 +3420,42 @@ website_ollama_listen() {
echo "Ollama API is reachable on the Debian host." echo "Ollama API is reachable on the Debian host."
} }
ollama_setup() {
local bind_address="${LAB_OLLAMA_BIND_ADDRESS:-0.0.0.0:11434}"
local models_dir="${LAB_OLLAMA_MODELS_DIR:-/data/ollama/models}"
local model="${LAB_AI_GATEWAY_MODEL:-qwen2.5:0.5b}"
local installer_url="${LAB_OLLAMA_INSTALL_URL:-https://ollama.com/install.sh}"
local tmp_installer
require_debian_server "ollama-setup"
if ! command -v curl >/dev/null 2>&1; then
echo "curl is required to install Ollama." >&2
exit 1
fi
if ! command -v ollama >/dev/null 2>&1; then
echo "Installing Ollama from ${installer_url}..."
tmp_installer="$(mktemp)"
curl -fsSL "${installer_url}" -o "${tmp_installer}"
sudo sh "${tmp_installer}"
rm -f "${tmp_installer}"
else
echo "Ollama is already installed: $(command -v ollama)"
fi
website_ollama_listen
echo "Ensuring Ollama model ${model} is available..."
if ollama list | awk -v model="${model}" 'NR > 1 && ($1 == model || $1 == model ":latest") { found = 1 } END { exit found ? 0 : 1 }'; then
echo "Ollama model ${model} is already present."
else
OLLAMA_HOST=http://127.0.0.1:11434 ollama pull "${model}"
fi
echo "Ollama homelab setup is ready."
}
apps() { apps() {
local buildx_builder_ready=false local buildx_builder_ready=false
local demos_image_built=false local demos_image_built=false
@ -4728,6 +4771,9 @@ case "${1:-}" in
website-ollama-listen) website-ollama-listen)
website_ollama_listen website_ollama_listen
;; ;;
ollama-setup)
ollama_setup
;;
deploy-gitea) deploy-gitea)
deploy_gitea deploy_gitea
;; ;;
@ -4789,7 +4835,7 @@ case "${1:-}" in
nuke nuke
;; ;;
*) *)
echo "Usage: $0 {up|rebuild-cluster|stop-cluster|start-cluster|status|apps|website-translation-model|website-ollama-listen|deploy-gitea|rpi-services|bootstrap-gitea-repo|backup-gitea|drill-gitea-restore|install-gitea-runner|move-prometheus-stack-workers|doctor-versions|doctor-edge|doctor-gitea|doctor-rpi|doctor-cluster|preflight|secrets-init|secrets-check|tailnet-policy-check|ai-index|ai-check|openwrt|nuke}" echo "Usage: $0 {up|rebuild-cluster|stop-cluster|start-cluster|status|apps|website-translation-model|website-ollama-listen|ollama-setup|deploy-gitea|rpi-services|bootstrap-gitea-repo|backup-gitea|drill-gitea-restore|install-gitea-runner|move-prometheus-stack-workers|doctor-versions|doctor-edge|doctor-gitea|doctor-rpi|doctor-cluster|preflight|secrets-init|secrets-check|tailnet-policy-check|ai-index|ai-check|openwrt|nuke}"
exit 1 exit 1
;; ;;
esac esac