This repository provisions a dedicated Mac mini as a stable, reproducible local LLM host.
It is designed for long-term maintenance: safe upgrades, explicit model management, and clear separation between host provisioning (Ansible) and service deployment (Docker containers).
-
Review the architecture:
- Read AGENTS.md for project principles and guardrails
- Read docs/ARCHITECTURE.md for system design
-
Set up your environment:
- Install requirements:
pip install -r requirements.txt - Configure target host in
inventory/hosts.yml - Review defaults in
group_vars/all.yml
- Install requirements:
-
Create data directory on target Mac:
# SSH to the Mac mini and create the directory (default: /opt/local-llm) ssh user@macmini "sudo mkdir -p /opt/local-llm && sudo chown $(whoami) /opt/local-llm"
Or customize the path by setting
local_llm_data_dirin your inventory. -
Install prerequisites (fresh Mac only):
ansible-playbook playbooks/preflight.yml
-
Verify connectivity:
ansible-playbook playbooks/verify.yml
-
Provision the host:
ansible-playbook playbooks/site.yml --check # Dry-run first ansible-playbook playbooks/site.yml # Apply
See docs/SETUP.md for detailed setup instructions.
.
├── AGENTS.md # Project principles and guardrails
├── README.md # This file
├── ansible.cfg # Ansible configuration
├── requirements.txt # Python/Ansible dependencies
├── Makefile # Helper commands (lint, check, verify, provision)
│
├── inventory/
│ └── hosts.yml # Target host definitions
│
├── group_vars/
│ ├── all.yml # Global defaults and configuration
│ └── macmini.yml # Host-specific overrides (minimal)
│
├── playbooks/
│ ├── site.yml # Main provisioning entrypoint
│ └── verify.yml # Connectivity and health checks (read-only)
│
├── roles/
│ ├── common/ # Shared setup and assertions
│ ├── homebrew/ # Homebrew installation
│ ├── ollama/ # Ollama LLM runtime
│ ├── docker_desktop/ # Docker Desktop
│ ├── models/ # Model reconciliation
│ ├── openwebui/ # OpenWebUI deployment
│ └── README.md # Role documentation
│
├── compose/
│ └── openwebui/
│ └── .env.example # Environment template (no secrets)
│ # (compose.yml generated from template)
│
├── docs/
│ ├── SETUP.md # Setup and operational guide
│ ├── ARCHITECTURE.md # System design and components
│ └── UPGRADES.md # Upgrade procedures and policy
│
├── .gitignore # Excludes secrets and cache
├── .yamllint # YAML linting configuration
└── .ansible-lint # Ansible linting configuration
✅ Idempotent — Run playbooks repeatedly; always converges safely
✅ Version-pinned — All components have explicit versions; no "latest" by default
✅ Safe by default — No upgrades, no destructive actions unless explicitly enabled
✅ Clear separation — Host provisioning, service deployment, and model management are independent
✅ GitOps-ready — All state declared in version control; reproducible from git checkout
✅ Observable — Health checks and verification tasks included
✅ Maintainable — One role per subsystem; clear responsibilities
All configuration is centralized in version control:
- Global defaults:
group_vars/all.yml - Host overrides:
group_vars/macmini.yml(keep minimal) - Target host:
inventory/hosts.yml - Execution modes: Variables in
group_vars/all.ymlenable_upgrades— Opt-in upgrades for Homebrew, Docker, Ollamaollama_models_refresh— Repull all declared modelsollama_models_prune— Remove unmanaged models (dangerous)
- Models: Declared in
ollama_modelslist (single source of truth)
See docs/SETUP.md for configuration details.
Use the Makefile for common tasks:
make help # Show all available commands
# Testing (run before committing)
make test # Run all validation checks (fast)
make test-all # Run all tests including linting
make lint # Run ansible-lint and yamllint
make syntax-check # Check Ansible playbook syntax
make validate # Run custom validation scripts
# Provisioning
make verify # Test connectivity to target host
make check # Dry-run provisioning (--check mode)
make provision # Run provisioning (no upgrades)
make provision-upgrade # Run provisioning with upgrades enabled
# Maintenance
make clean # Clean Ansible cacheOr run playbooks directly:
ansible-playbook playbooks/verify.yml
ansible-playbook playbooks/site.yml --check
ansible-playbook playbooks/site.yml -e enable_upgrades=trueSee TESTING.md for detailed testing documentation.
This project follows strict principles (see AGENTS.md):
- Idempotency (must): All tasks safe to run repeatedly.
- Version pinning (must): No implicit "latest" upgrades.
- Safe upgrades (must): Explicit opt-in; include rollback procedures.
- Separation of concerns (must): Each role has a clear, narrow responsibility.
- Explicit configuration (must): All state declared in version control.
- Observability (should): Health checks and clear error messages.
Upgrades are intentional and controlled:
- Update version variable in
group_vars/all.yml - Test in
--checkmode first - Apply with
enable_upgrades=true - Verify with
playbooks/verify.yml - Document changes and any breaking behavior
See docs/UPGRADES.md for detailed procedures per component.
- Localhost-only by default — No remote exposure without explicit configuration
- No secrets in repo —
.envfiles and credentials are gitignored - SSH key-based auth — Passwords not supported
- Minimal elevation — Use sudo sparingly; document why
- Target: macOS 12+, Mac mini or equivalent
- Control node: macOS, Linux, or WSL with Ansible 2.13+
- SSH: SSH keys required; password auth not supported
- AGENTS.md — Project intent, guardrails, and guidance for contributors
- TESTING.md — Testing strategy, CI workflows, and validation details
- docs/SETUP.md — Setup and operational procedures
- docs/ARCHITECTURE.md — System design and component overview
- docs/UPGRADES.md — Upgrade procedures and rollback plans
- roles/README.md — Role responsibilities and structure
Follow the principles in AGENTS.md:
- Keep changes small and reviewable
- Maintain role separation
- Pin all versions
- Include verification steps
- Update documentation
See LICENSE for details.
