Homelab guide
The homelab setup installs TestLookup on K3s, a small Kubernetes distribution, on one machine or more. The overlay in the repository holds no site addresses. Every value for your site lives in a configuration file outside the repository.
Follow the sections below in order:
- Check the requirements.
- Create the cluster, if you need one.
- Fill in the site file and deploy.
- Set up backups that are kept off the cluster.
The K3s deployment guide on GitHub has the full eleven-step deploy table, site patches (including one that switches to a hosted AI provider) and a troubleshooting table.
Requirements
Section titled “Requirements”The homelab overlay expects:
- K3s with its built-in Traefik ingress and
local-pathstorage. - The databases running inside the cluster.
- One copy of each service.
Its ingress uses plain HTTP. To use HTTPS, add TLS in front of Traefik or add a certificate with a site patch. The deploy script builds the backend, frontend and MCP server images from a clean checkout, and pushes them to a registry inside the cluster (by default registry.local:30500). The bundled job queue and file store are Valkey 8.1 and versitygw, but their settings keep the REDIS_* and MINIO_* names.
You need:
- A Linux machine running K3s with Traefik and the
local-pathStorageClass. For a small team, the guide’s starting estimate is about 8 GB of memory and 100 GB of SSD for a year of results. Add about 8 GB of memory for the 7B AI model inside the cluster. Check this against the sizing guide on GitHub and your own volume. - A build machine with
kubectlpointed at the cluster,git,curl,openssl, and Docker or Podman. It can be the same machine. On Windows, run the scripts from Git Bash. - On the build machine,
registry.localmust point to the node, andregistry.local:30500must be trusted as a registry without HTTPS. In Docker, add it toinsecure-registriesindaemon.json. In Podman, add it toregistries.conf, or setK3S_REGISTRY_FALLBACK=<node-ip>:30500in the site file. - On every node, a registry mirror for
registry.local:30500in/etc/rancher/k3s/registries.yaml. The one-node commands below andbootstrap-homelab.shboth write it.
Create the cluster (optional)
Section titled “Create the cluster (optional)”Skip this step if you already run K3s with Traefik and local-path storage.
One node, by hand. Run these commands on the node:
curl -sfL https://get.k3s.io | sh -sudo mkdir -p /etc/rancher/k3ssudo tee /etc/rancher/k3s/registries.yaml >/dev/null <<'YAML'mirrors: "registry.local:30500": endpoint: - "http://127.0.0.1:30500"YAMLecho "127.0.0.1 registry.local" | sudo tee -a /etc/hostssudo systemctl restart k3sThen copy /etc/rancher/k3s/k3s.yaml to the build machine, replace 127.0.0.1 in it with the node’s address, and point KUBECONFIG at it.
Several nodes, over SSH.
- Create the site file (step 1 of the next section).
- In it, set
K3S_NODEStoname=ippairs (the first node runs the K3s server), and setK3S_NODE_USERandK3S_LB_RANGE(the MetalLB address range). - From the repository root, run
./homelabsetup/bootstrap-homelab.shto create the cluster only (ormake k8s-bootstrap-homelab). To create the cluster and deploy TestLookup in one step, run./homelabsetup/bootstrap-homelab.sh --deploy(ormake k8s-oneclick-homelab).
Over SSH, the script installs a fixed K3s release (K3S_VERSION overrides it), joins the other nodes, and installs MetalLB and cert-manager. It also writes the registry mirror on every node and saves a kubeconfig. Running it twice is safe, so run it again after you add a node.
Configure and deploy
Section titled “Configure and deploy”Run these steps from the repository root on the build machine.
-
Create the site file outside the repository:
Terminal window mkdir -p ~/.config/testlookupcp homelabsetup/homelab.env.example ~/.config/testlookup/homelab.envTo keep it somewhere else, point
TESTLOOKUP_HOMELAB_ENVat the file. A variable already set in your environment wins over the file. -
Fill in the file from your real cluster:
K3S_HOSTis the host name the ingress answers on. The default,testlookup.localhost, works in a browser on the node itself.K3S_REGISTRYis the registry the nodes pull images from (defaultregistry.local:30500).K3S_LLM_MODELandK3S_EMBEDDING_MODELchoose the AI models pulled into Ollama (defaultsqwen2.5:7b-instructandnomic-embed-text:v1.5).ADMIN_USERNAMEandADMIN_EMAILname the first administrator. LeaveADMIN_PASSWORDunset, and the deploy generates a password.
Do not put any other secrets in this file. The deploy generates them.
-
Put any change to the manifests for your site in a folder of strategic-merge patches, and name that folder in
K3S_PATCHES_DIR. Do not edit the tracked overlay or usekubectl patch, because the next deploy would undo those changes. -
Run the first install:
./homelabsetup/deploy-homelab.sh(ormake k8s-deploy-homelab). -
At the end, the script prints the Traefik address. If your host name needs a hosts-file line, it prints that too. Add that line on every machine that will open TestLookup.
-
Open
http://<K3S_HOST>(on the node itself,http://testlookup.localhostby default). Sign in with the administrator login that the script printed once and saved tohomelabsetup/.homelab-credentials.
On an existing installation, the deploy refuses to change the ingress host name unless you set ALLOW_HOST_CHANGE=true. That way a missing site file cannot quietly move your installation.
Deploy and update
Section titled “Deploy and update”Each deploy does the following:
- Tags its images
build-<UTC time>. - Builds an overlay for this deploy that replaces
BUILD_TAG_PLACEHOLDERwith that tag. - Runs the database updates (migrations) with the new image, and then applies it.
- Checks that every pod runs the image just pushed, and that
/health/versionreports the deployed commit.
An image built on your own computer is not automatically available to K3s. After each healthy deploy that pushed a build, the script runs homelabsetup/prune-registry.sh. By default it keeps the newest three dated builds of each image. Pass --keep-builds=N to keep more builds you can go back to, or --skip-registry-prune to keep everything.
To update an existing installation and reuse the registry and the downloaded models, run this (or make k8s-deploy-homelab-update):
./homelabsetup/deploy-homelab.sh --skip-registry --skip-modelsThe guide’s troubleshooting table covers the common failures:
- Every pod shows
ImagePullBackOff. The node has no registry mirror for the registry name. The deploy checks for this and explains it. - A push fails with “server gave HTTP response to HTTPS client”. The build machine does not trust the registry that has no HTTPS.
- The deploy stops because the cluster “already serves TestLookup on host”. The site file did not load, or it names a different host.
- “Deployment DEGRADED” with exit code 1. A new pod failed, and the previous version (ReplicaSet) kept serving.
Teardown
Section titled “Teardown”./homelabsetup/deploy-homelab.sh --teardowndeletes the application and keeps the data volumes (PersistentVolumeClaims).--teardown-alldeletes the namespace and all data, after you type a confirmation../homelabsetup/bootstrap-homelab.sh --teardown(ormake k8s-teardown-homelab) removes K3s from every configured node.
Data stays on each node’s disk
Section titled “Data stays on each node’s disk”The homelab local-path volumes keep your data on the nodes’ own disks. Adding copies of the application does not copy those disks, so losing a disk or a node can lose data.
Back up to another machine
Section titled “Back up to another machine”Keep matching backups of PostgreSQL, MongoDB and file storage outside the cluster’s machines. The homelab overlay includes the Kubernetes backup component. A daily job at 02:17 writes one archive to the testlookup-backups volume claim. That claim sits in the same cluster, so it does not protect you if you lose a node.
To take a backup now and copy the archives off the cluster:
kubectl -n testlookup create job testlookup-backup-now --from=cronjob/testlookup-backupkubectl -n testlookup wait --for=condition=complete job/testlookup-backup-now --timeout=30mkubectl -n testlookup cp <pod-that-mounts-testlookup-backups>:/backups ./testlookup-backupsThe backup component guide on GitHub shows how to find a pod that mounts the claim and how to schedule the copy. Valkey, ChromaDB and Ollama are left out on purpose, because they are rebuilt or reloaded after a restore.
Also keep the application encryption key (APP_SECRET_KEY) and the generated credentials file with your backups on the other machine. On a first install, the deploy creates every secret at random, prints it once and saves it to K3S_CREDENTIALS_FILE (default homelabsetup/.homelab-credentials). It creates the first administrator only if there is none, with ADMIN_PASSWORD or a random password saved to the same file. Later deploys never create a new testlookup-secrets Secret over the existing one, and never change an existing administrator.
Practice a restore
Section titled “Practice a restore”Practise a restore on a spare test installation before you need one. The restore runbook on GitHub walks through it:
- Pick the archive.
- Stop the application and the backup job.
- Set
RESTORE_FILEandRESTORE_CONFIRM=yeson thetestlookup-restore-requestConfigMap. - Run the restore job. It checks every checksum before it touches any data.
- Run the database updates (migrations).
- Empty the job queue.
- Start the application again.
Afterwards, open a recent run and check that its report, attachments and AI analysis load. Then upload one new report and check that it goes all the way to a finished report.
Security and offline installs
Section titled “Security and offline installs”Do not reuse addresses, passwords, host names or tokens from any guide, example or earlier installation. Find your own. Treat published capacity figures as starting estimates, and check them against your own volume.
Offline mode for AI can allow checked local or private-network services. Turning it on does not, by itself, cut an installation off from the internet. An installation with no network connection at all (air-gapped) also needs:
- The images copied in beforehand. The offline bundle no longer includes MongoDB, so copy that image separately.
- The AI model and embedding files, prepared separately.
- A review of every integration.
- Tests that it starts and processes a report with the network blocked.
The sections below come from TestLookup’s own deployment guide. They summarise the ingress and the backup tools.
TLS, backups and limits
Section titled “TLS, backups and limits”TLS and ingress
Section titled “TLS and ingress”| Mode | How traffic reaches TestLookup | TLS |
|---|---|---|
| K3s (homelab) | Traefik on plain HTTP | None built in. Use it on a private network, or add TLS in front of Traefik or with a site patch |
Behind any proxy, set CORS_ORIGINS and PUBLIC_BASE_URL to the address users open. Set TRUSTED_PROXY_IPS to the proxy’s address range only, and never to *. The API uses it to find the real client address for rate limits and the activity log. The overlays include commented-out defaults for each platform.
Backups
Section titled “Backups”| Mode | Tooling | What it covers |
|---|---|---|
| Compose | make backup (QUIESCE=1 stops the app first, BACKUP_DIR sets the destination) and make restore FILE=... [FORCE=1] [CONFIRM=yes] | PostgreSQL dump, MongoDB archive, object-store data and a manifest with the Alembic head, in one .tar.gz |
| Kubernetes | The backup component: a daily CronJob to a PVC plus an on-demand restore Job | Same three stores; enabled in the K3s and OpenShift + Artifactory overlays, opt-in elsewhere |
| Managed services | Your provider’s snapshots and point-in-time recovery | Back up PostgreSQL, MongoDB and the bucket together |
Valkey (or Redis), ChromaDB and Ollama are left out of the archive on purpose. They can be rebuilt, and replaying an out-of-date job queue after a restore is dangerous. Back up PostgreSQL, MongoDB and file storage together so that the links between them stay correct. Copy the archives to another machine, and test a restore. The homelab overlay uses the Kubernetes backup job described in Off-node backup above, and the administration guide covers backup and restore in detail.
Important limits
Section titled “Important limits”-
K3s
local-pathstorage keeps data on one machine’s disk, whether the cluster has one node or more. Extra copies of the application do not protect you if that disk fails. Keep backups on another machine and practise restoring them. -
Offline mode for AI allows local or private addresses that pass TestLookup’s checks. It is not a network firewall. Download the images, software packages and models before you cut the installation off from the network.
Next, use day-two operations to check the running installation.