HOME LAB / ENGINEERING NOTES
From idea to implementation
My implementation notes: configuration examples, integration decisions and lessons from building a home infrastructure and local AI agent.
Implementation snapshot: 11 September 2026. Examples use illustrative values.
01 — Network foundation
I started with router diagnostics, configuration exports, DHCP leases and static DNS records. After the RouterOS and RouterBOARD updates, I verified connectivity and introduced WireGuard. I configured the VPN listener, DNS access, LAN forwarding and Internet NAT as distinct pieces.
/system resource print
/system routerboard print
/ip dhcp-server lease print
/ip dns static print
/interface wireguard peers print
/ip firewall filter print stats
/ip firewall nat print statsI use these read-only checks to separate tunnel establishment from reachability. A successful WireGuard handshake does not by itself prove that DNS and the forwarding rules work.
02 — Persistent services on Synology
I defined small Compose deployments, persistent data directories, restart policies and a common Homepage entry point. Monitoring runs independently from the AI PC. The following is an illustrative excerpt; image tags and addresses need to be selected for the destination environment.
services:
uptime-kuma:
image: louislam/uptime-kuma:2
restart: unless-stopped
ports:
- "${NAS_LAN_IP}:3001:3001"
volumes:
- ./uptime-kuma:/app/data
mem_limit: 384m
security_opt:
- no-new-privileges:trueMusic files live in their own share, mounted read-only by Navidrome. Its database is a separate writable mount. Caddy terminates HTTPS and connects to Navidrome inside Docker, so the music container needs no published host port.
services:
navidrome:
image: deluan/navidrome:${NAVIDROME_VERSION}
restart: unless-stopped
volumes:
- ./data:/data
- ${MUSIC_DIRECTORY}:/music:ro
environment:
ND_SCANNER_SCHEDULE: "@every 15m"
ND_ENABLEINSIGHTSCOLLECTOR: "false"
# No published ports; reverse proxy uses the Docker network.03 — Moving the main AI workload to the PC
The small CPU model on the NAS was useful as a proof of concept but too slow for comfortable conversation. I moved the main Open WebUI instance to the GPU PC and connected it to the Windows Ollama service.
services:
open-webui:
image: ghcr.io/open-webui/open-webui:${WEBUI_VERSION}
restart: unless-stopped
environment:
ENABLE_OLLAMA_API: "True"
OLLAMA_BASE_URLS: "http://host.docker.internal:11434"
WEBUI_AUTH: "True"
ENABLE_VALVE_ENCRYPTION: "True"
WEBUI_SECRET_KEY: "${WEBUI_SECRET_KEY}"
volumes:
- ./data:/app/backend/data
- ${CONTEXT_DIRECTORY}:/home-ai/context:ro
- ${DRAFT_DIRECTORY}:/home-ai/drafts:rwSeeing a model in a dropdown did not prove that inference was working. I checked model discovery directly from the WebUI container, distinguished the native Ollama API from its OpenAI-compatible endpoint and tested an actual response.
04 — Turning chat into an agent
I implemented separate Python tools, registered their function specifications in Open WebUI and assigned their IDs to the model. Native function calling and a Polish system instruction make the tools available during a conversation. Configuration is stored persistently, rather than relying on a one-off chat instruction.
Model profile:
function_calling: native
tools:
- internet_search
- pc_observer
- ssh_operator
- file_workspace
- google_workspace
- image_workflowFor PC diagnostics, I used a limited PowerShell HTTP interface with token authentication. I fixed header handling and read application inventory from the registry. For SSH, I created a separate service with device configuration, keys and an operation log. These are different permission models: PC inspection does not imply read-only SSH.
My practical acceptance check: ask for real installed applications, perform a search with returned sources, and read a device identity over SSH. A model saying it has a capability is not a successful test.
05 — Gmail, calendar and source dates
I enabled Gmail and Calendar APIs and connected a local OAuth bridge. The callback listens on PC loopback. The public application information and privacy pages are static pages on the NAS; they do not expose the bridge.
I moved the OAuth application out of Testing. This avoids the seven-day refresh-token rule associated with that testing setup, but it does not mean tokens can never expire or be revoked. It also does not mean the application has completed Google verification.
Tests exposed two important errors: a recent-mail query could return fewer messages than requested, and the model could confuse a receipt date with an event date. I added explicit retrieved counts and an ICS event-proposal path. Email replies follow a draft-and-approval process.
06 — ComfyUI and resource management
I added ComfyUI as a CUDA container on the same Docker network. The container reuses model directories through read-only mounts and stores results separately. The agent translates the description into an image prompt, submits a workflow through the API and polls the job history.
I tested different image workflows, then selected Qwen-Image for the main path. Generated results still need review: a successful render can contain extra objects or inaccurate anatomy. Consistent characters across scenes remain a development goal.
OLLAMA_KEEP_ALIVE=5m
OLLAMA_MAX_LOADED_MODELS=1
OLLAMA_NUM_PARALLEL=1
# The image tool requests model unloading before/after GPU work.
# ComfyUI runtime: --cache-none --disable-smart-memoryDocker Desktop introduces an additional RAM limit through WSL. I account for that separately from the PC’s 64 GB. I do not treat a model staying in filesystem cache as a guaranteed RAM-to-VRAM residency feature.
07 — Publishing on my own NAS
I use a static website: no application login, form handler, database or browser-side code is needed for this portfolio. Caddy serves the public directory over HTTPS and renews certificates automatically while DNS and challenge reachability remain correct.
www.example.com {
root * /portfolio/current
encode gzip
header {
X-Content-Type-Options nosniff
Referrer-Policy strict-origin-when-cross-origin
Content-Security-Policy "default-src 'none'; style-src 'self'; img-src 'self'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'"
-Server
}
file_server
}I keep website files separate from deployment scripts, certificate keys and application secrets. Before changing the proxy, I keep the old configuration for rollback. The public page describes the architecture without publishing a device-access map.
What I want to improve next
- RAG over selected documents, with source and version information.
- Spoken Polish, family access and a clearer mobile experience.
- PC wake-up on demand, stronger GPU scheduling and repeatable media workflows.
- Server-enforced action approval, independent backups and restore tests.
The useful lesson from this build is to test each link in the chain. A green container, a visible model and an available API are necessary steps; the real acceptance criterion is the task being completed correctly.