Configuration scripts for my personal homelab: workstation provisioning, local AI stacks, and desktop app deployment. Everything is idempotent — scripts can be re-run safely and skip whatever is already installed.
| Project | What it does | Getting started |
|---|---|---|
| Fedora Setup | Provisions a Fedora workstation: GitHub SSH key, git identity, Homebrew, devops tooling (gcloud, AWS CLI, kubectl, helm, terraform, kind, Docker, Podman Desktop), remote desktop (RDP), Tailscale, a fully local AI agent stack, desktop apps, and containerized web services (Open WebUI, Glance) | Fedora |
| Windows Setup | Bulk app installation for Windows 10/11 via a winget manifest | Windows |
fedora/setup.sh orchestrates the scripts in fedora/applications/:
homelab.sh— base system: SSH key, git identity, Homebrew, devops tooling, remote desktop (GNOME RDP on port 3389; prompts once for credentials — the session must be logged in, so enable GNOME auto-login for unattended access), and Tailscale (at boot)hermes.sh— fully local AI agents, no cloud accounts: LM Studio (headless, at boot) serving Qwen, with Hermes and pi pointed at it; the Hermes web dashboard runs at bootdesktop.sh— desktop apps: VS Code and Steam (RPM), Slack, Flatseal, Zotero (Flathub)docker/docker-compose.yml— containerized web services: Open WebUI (browser chat against the LM Studio endpoint) and the Glance dashboard, whose config is version-controlled indocker/config/
After setup, these are reachable in a browser (localhost locally, or over your tailnet via the HTTPS addresses below):
| Service | Address | Login | What it's for |
|---|---|---|---|
| Glance | http://localhost:8181 | none | Homelab dashboard: service health, bookmarks |
| Open WebUI | http://localhost:8080 | none (auth disabled) | Browser chat with the local Qwen model |
| Hermes dashboard | http://localhost:9119 (moves to http://<machine>.<tailnet>.ts.net:9119 once the tailnet is joined) |
none on localhost; basic auth on the tailnet (credentials in ~/.hermes/.env) |
Hermes config, API keys, sessions |
| Speedtest Tracker | http://localhost:8765 | admin@example.com / password — change it |
Hourly internet speed tests with history graphs |
| LM Studio API | http://localhost:1234/v1 | none | OpenAI-compatible inference endpoint (API, not a UI) |
Speedtest Tracker ships with the default login above; change the password under Settings → Profile after first sign-in.
git clone https://github.com/stevenplatt/homelab.git
cd homelab/fedora
./setup.shWhen finished it prints your git identity and SSH public key for pasting into github.com/settings/keys. Log out and back in afterward so the docker group membership takes effect.
Two things need a one-time manual step after setup.sh finishes.
Tailscale — setup.sh handles this at the end of its run: it runs sudo tailscale up (which prints a login URL — open it in a browser to authenticate, first run only) and prints the tailnet URLs for every service. HTTPS is terminated by a Caddy container (fedora/docker/config/Caddyfile) that fetches real .ts.net certificates from tailscaled automatically — this requires MagicDNS and HTTPS enabled on your tailnet (admin console → DNS). From any device on your tailnet:
| Service | HTTPS (Caddy) | Plain-http fallback |
|---|---|---|
| Glance | https://<machine>.<tailnet>.ts.net/ |
:8181 |
| Open WebUI | https://<machine>.<tailnet>.ts.net:8443/ |
:8080 |
| Hermes dashboard | https://<machine>.<tailnet>.ts.net:9443/ |
:9119 |
| Speedtest Tracker | https://<machine>.<tailnet>.ts.net:7443/ |
:8765 |
| LM Studio API | https://<machine>.<tailnet>.ts.net:5443/v1 |
:1234/v1 |
setup.sh prints these with your machine's actual MagicDNS name filled in. Caddy's HTTPS ports are deliberately different from the backends' — the backends keep their plain-http listeners (traffic is WireGuard-encrypted on the tailnet either way; HTTPS adds browser secure-context features like microphone access for Open WebUI's voice input). Tailscale Serve is deliberately not used — its proxies conflict with services listening directly on the same ports; setup.sh clears any leftover serve configuration.
The Hermes dashboard additionally validates the Host header (a DNS-rebinding defence), so setup.sh binds it to the tailnet DNS name directly, which engages Hermes' basic-auth gate — the username/password are generated once into ~/.hermes/.env (HERMES_DASHBOARD_BASIC_AUTH_*). Caddy preserves the Host header, so the check passes through the proxy. The Glance dashboard links use the HTTPS addresses automatically (via fedora/docker/config/.env), so they work from any tailnet device.
Hermes ↔ Slack uses Slack Socket Mode — an outbound connection from the machine, so no Tailscale, ports, or Nous account are involved. Create the Slack app once by hand: run hermes slack manifest --write, paste the manifest at api.slack.com/apps (create new app → from manifest), and install it to your workspace. Then export the tokens and re-run setup:
export SLACK_BOT_TOKEN=xoxb-... # bot token from the installed app
export SLACK_APP_TOKEN=xapp-... # app-level (socket mode) token
export SLACK_ALLOWED_USERS=U... # your slack member id — required, denies all others
./setup.shThe script stores the tokens in ~/.hermes/.env and installs the gateway as a service so Slack survives reboots. Invite the bot to a channel (/invite @Hermes) or just DM it. Full walkthrough: Hermes Slack setup.
Everything runs locally: the Qwen model via LM Studio, no cloud accounts. Run hermes in a terminal to chat, or message it on Slack once the gateway is connected. The web dashboard at localhost:9119 manages config and sessions. Hermes builds skills and memory across sessions, so it improves with use — see the Hermes docs for the full guide.
Some first requests to try (terminal or Slack):
summarize the hardware in this machine and how much disk space is free
watch the file ~/Desktop/homelab.log and tell me if any service failed
clone github.com/glanceapp/glance, read the widget docs, and suggest three widgets for fedora/docker/config/glance.yml in my homelab repo
every weekday at 8am, check my kind cluster is healthy and message me on slack if not
The last one exercises Hermes' cron + gateway features; the others exercise shell, file, and coding tools. Note that hosted tools (web search, image generation) are disabled in this local-only setup — Hermes will say so if a request needs them.
A winget manifest bulk-installs applications on Windows 10/11. From an administrator PowerShell:
winget import -i windows\win11_deploy.json