The Setup Wizard#

What Is It?#

The Setup Wizard is the guided, browser-based experience that walks you from bare hardware in a box to a running, production-grade self-hosted solution . It’s the front door to PSW — the one thing you open the first time.

You don’t memorize commands, edit YAML by hand, or SSH into anything. You open a web page, answer questions, click buttons, and by the end you have a full platform: a Proxmox hypervisor, the seven core apps , HTTPS with valid certificates, Single Sign-On , and automatic convergence — all driven from your laptop.

Think of the wizard as a recipe that also does the cooking for you. It knows every step, in the right order, and it refuses to let you skip one that matters.

Why Does It Exist?#

Setting up a self-hosted stack from scratch involves dozens of independent steps — each with its own pitfalls, gotchas, and ways to fail silently. You’d normally:

  • Build a bootable installer USB
  • Run the Proxmox installer and partition disks correctly for ZFS (a modern filesystem with snapshots and data integrity)
  • Configure the network, gateway, and DNS servers
  • Connect to your router (OPNsense ) and generate API keys
  • Plan storage pools for the apps you expect to run
  • Define targets (containers where apps will live)
  • Configure providers for DNS, DHCP (automatic IP assignment), and ACME (automatic TLS — the encryption behind HTTPS)
  • Initialize the user project , generate secrets , set up SOPS (a tool for encrypting secrets in git)
  • Back up the master encryption key somewhere safe
  • Run bootstrap and hope you didn’t skip anything

Doing that by hand is hours of work, easy to get wrong, and nearly impossible to reproduce. The wizard encodes the whole procedure in a linear flow, runs each step against the real tooling, and checks that the previous step actually worked before unlocking the next. It’s the opposite of “ten paragraphs of install instructions in a README.”

How It Works (In One Sentence)#

The wizard is a series of screens backed by phase detection: each screen produces real artifacts (files, configs, a USB stick, a running container), and the next screen unlocks only when those artifacts exist on disk.

There are no hidden flags or “completed” checkmarks stored somewhere. If nodes/small/hardware.yml exists, the hardware step is done. If .psw/converge/state.yml shows last_result: success, the whole wizard is done. This means you can quit in the middle, come back days later, and pick up exactly where you left off. Delete a file and that step unlocks again.

Starting the Wizard#

You launch the wizard with:

psw wizard

Then open the printed URL (defaults to http://127.0.0.1:8111) in your browser. With no arguments, you get a project launcher where you can create a new project or open an existing one. Pick “New Project”, choose a folder, and the wizard starts. The project folder you pick here is locked in — the wizard shows it as read-only from that point on.

If you already know the project path, you can skip the launcher:

psw wizard --project ~/homelab
FlagWhat it does
--projectOpen a specific user project directly (creates the directory if it doesn’t exist)
--portPort for the server (default 8111)
--hostBind address (default 127.0.0.1)
--reload / --no-reloadAuto-restart the wizard when PSW source changes (default on — the wizard is operator-facing dev tooling, so a fresh git pull takes effect immediately). Pass --no-reload for a stable single-process run when you don’t want hot-reload churn.

psw wizard vs psw dashboard. They’re two separate CLIs for two separate jobs: psw wizard is the installer UI (project launcher + pre-bootstrap screens, ending in the Launch step). psw dashboard is the operations UI — it only opens a project that’s already been bootstrapped and refuses to run otherwise. Once your platform is live, you switch from psw wizard to psw dashboard.

Restoring a project after disaster? PSW has two recovery scenarios depending on what you saved off-box:

  • Have your git remote available? Most common case. git clone it back, drop your age key in, and the wizard auto-routes you past Start to wherever the project left off — Hardware → Plan → Install → Launch with all your old identities intact. (A one-click wizard UI for this is planned.)
  • No git, just a panic-backup tarball? Use the “Restore from a previous project” card at the top of the Start screen. Pick the tarball — the form pre-fills with the old domain, Cloudflare token, OPNsense credentials, SMTP relay. Review, hit Submit, continue Hardware → Plan → Install → Launch like a fresh install but with old credentials.

The full umbrella story (three artifacts: git remote + panic backup + per-app bundles, restored separately) lives in disaster-recovery.md . The secrets-side deep dive is in secrets.md § Reusing Secrets Across Project Rebuilds .

After the Wizard#

Once bootstrap succeeds, you stop using psw wizard entirely. Day-to-day you open the deployed dashboard at https://dashboard.<your-domain> — same UI (psw-ui is shared), but served from the core target, protected by Authelia SSO, and talking to your real infrastructure instead of local files. If you want to run it from your laptop instead (for scripting, or while travelling), psw dashboard --project ~/homelab launches the same code locally against the project on disk. Either way you’re in operations mode now.

The Five Steps#

The progress bar at the top of every page shows five dots — these are the user-visible steps of the wizard. Several have internal sub-phases that share a dot (for example, USB creation lives under Hardware). You always see where you are: green check for done, pulsing blue for active, grey for ahead.

DotScreenWhat it does
1. Start/wizard/startOne-screen setup: domain, SSH key, OPNsense, Cloudflare — plus inline age-key backup
2. Hardware/wizard/hardwareBuild a bootable USB, boot each server from it, import the hardware inventory
3. Plan/wizard/deploy-planPick apps, define targets, and let AI plan your ZFS storage layout
4. Install/wizard/installGenerate an unattended Proxmox installer; after install, carve LXC targets
5. Launch/wizard/bootstrapReview the plan, run bootstrap, and deploy your user apps

1. Start#

What you do: Fill out a single form with everything PSW needs to get going:

  • Your project domain (e.g. homelab.example.com) and the ACME/admin email for Let’s Encrypt and service admin accounts. Use a real inbox you check.
  • The path to your SSH key.
  • OPNsense URL + API key + API secret — you can paste the contents of OPNsense’s key.txt file directly and PSW will split out the key/secret for you. A “Test” button verifies the credentials live.
  • Cloudflare API token.
  • SMTP relay (host, port, username, password, from-address, TLS) — used by apps like Authelia (password-reset mail) and Alertmanager (email alerts). Leave blank if you don’t want mail; anything that tries to send will fail softly.

What PSW does on submit: Validates every field, hits the OPNsense API to confirm it’s reachable, then in one transaction:

  1. Writes project.yml with your domain and canonical admin email, .psw/workstation.yml with your local SSH key path, and network.yml with gateway/network facts seeded from OPNsense.
  2. Initializes SOPS so every following write is encrypted.
  3. Encrypts the OPNsense + Cloudflare credentials into secrets/providers.yml.
  4. Encrypts the SMTP block into secrets/smtp.yml.
  5. Runs psw project init in-process, scaffolding the whole user project (services/, roles/, nodes/, secrets/).
  6. Registers the local-dns + dhcp provider backends in project.yml (both reuse the same OPNsense connection).
  7. Sets the network-verified checkpoint — the network step is done too.
  8. Returns the path of secrets/.age-key so the page can flip into backup mode inline.

What it produces: A complete, initialized user project with encrypted provider secrets, ready for hardware discovery.

The inline backup panel: The moment psw project init succeeds, the Start page switches to a backup panel: “Download age-key”, then an acknowledgment checkbox. Losing the age key means losing every encrypted secret in your project — passwords, tokens, certificates — with no recovery path. PSW refuses to unlock Launch until you’ve clicked “I’ve backed it up”. This is a hard gate, not a suggestion.

Resuming into backup mode: If you bailed after init but before acknowledging, the Start page detects this on reload (via the age-key-acknowledged checkpoint) and lands you directly on the backup panel — it doesn’t re-ask for credentials you’ve already given it.

Why this matters: The old wizard split these into seven separate screens (Welcome, Network, Providers, Initialize, Review, Backup Key, Bootstrap-gate). They all produced artifacts that could be derived or collected in one pass — so Start now does the lot, with a single commit, a single error if anything fails, and a single progress bar.

2. Hardware#

What you do: This dot has two sub-phases served from the same URL (the page flips automatically based on whether any nodes/*/hardware.yml exists yet):

  • Phase A — Build a USB. Plug a USB drive into your laptop and pick the Proxmox VE ISO you downloaded. The wizard auto-detects the drive the moment you insert it, then installs Ventoy (a bootloader that lets you put multiple ISOs on one USB), copies the Proxmox ISO onto it, creates a persistence partition (a writable space tools can save data to across boots), and adds two small helper programs:

    • server-inspector — detects your server’s CPU, RAM, disks, NICs (network interfaces), and GPU
    • disk-wiper — safely wipes old partition tables and RAID metadata that would otherwise trip up the automated Proxmox install
  • Phase B — Discover hardware. Physically take the USB to your server, boot from it, and follow the on-screen steps: select the Proxmox ISO from Ventoy, choose the persistence partition, wait for the End User License Agreement screen (don’t click anything — press Ctrl+Alt+F3 to get a terminal), check your network (ping — and dhclient -v if DHCP didn’t kick in), mount the USB data partition, then run server-inspector <name> (where <name> is a short name for this server like small or white). Shut down and bring the USB back.

What PSW does: When Phase A finishes, you’ve got a single bootable USB that can discover hardware, wipe disks, and install Proxmox. When you re-insert the USB after Phase B, the wizard reads hardware-<name>.yml from the persistence partition and imports it into nodes/<name>/hardware.yml. Multi-node setup? Repeat the physical steps on each machine — every server writes its own file.

What it produces: nodes/*/hardware.yml — a complete inventory of every piece of your server that PSW needs to know about.

Why it matters: Storage planning, target sizing, and GPU-aware app placement (think Tdarr or Immich using hardware transcoding) all read from this file. See hardware for the full schema and infrastructure for what PSW does with it.

3. Plan#

What you do: Four substeps on one screen:

  1. Select apps. Core apps are always included (locked on). Check whatever else you want.
  2. Show prompt. PSW renders a self-contained prompt — your hardware, the storage guide, placement rules, app metadata, JSON schema — and gives you a Copy button.
  3. Paste response. You paste the prompt into whatever AI you already use (Claude.ai , ChatGPT, Gemini, a local LLM via Ollama , whichever). You paste its JSON reply into PSW.
  4. Review. Read-only summary of the applied plan. Regenerate to re-ask; Re-paste to try a different reply; Next to move on.

What PSW does:

  • Picking apps writes deployment-plan.yml with just user_apps: [...].
  • Show-prompt step renders the planner prompt (~70 KB) and also writes it to .psw/plan-prompt.md so you can diff across runs.
  • Paste-response step validates the AI’s JSON against a strict pydantic schema. On success it atomically writes deployment-plan.yml (filled with targets + resources + mounts + cluster topology), every node’s storage.yml (ZFS pools + datasets + NFS), and every node’s proxmox-storage.yml (installer-focused). On failure it surfaces a pointer-and-message error list you can copy back to your AI.

What it produces: deployment-plan.yml, nodes/<name>/storage.yml, and nodes/<name>/proxmox-storage.yml.

Why it matters: The planner decides what you’d otherwise have to solve by hand — bin-packing apps into targets given GPU / NFS / USB / memory constraints, plus designing the ZFS layout. PSW never calls an AI itself; you bring whichever AI you already pay for. See AI Planner for the protocol and deployment plan for the file it produces.

4. Install#

What you do: Another two-phase screen:

  • Phase A — Proxmox install. All installer fields are pre-filled with smart defaults but editable:

    • Network: gateway and DNS server (pre-filled from the OPNsense connection)
    • Proxmox settings: admin email (defaults to admin@yourdomain), root password (leave empty to auto-generate), timezone and keyboard (auto-detected from your workstation)
    • Per node: static IP (pre-filled from the DHCP pool) and install disk

    Plug the USB back into each server and boot from it.

  • Phase B — Configure infrastructure. Confirm your targets. Names, apps, CPU, memory, and rootdisk size all come from the Plan ’s resources block — a single source of truth for per-target sizing. Tweak anything you want, then PSW sets up the Proxmox node, applies the ZFS storage layout, and imports the node into the project.

What PSW does:

  • Phase A generates an automated Proxmox installer ISO — a custom ISO that runs the Proxmox installer with your exact answers already filled in — and writes it to the USB. Before you boot, PSW registers and verifies a DHCP reservation for each node’s chosen IP so the router knows about the server before it’s even on. The server boots, installs itself unattended, reboots, and comes online at its static IP. The wizard polls over SSH and marks the step complete only once every node answers pveversion successfully. If the selected management NIC negotiates below gigabit, PSW shows an amber warning (bad cable, slow switch port, flaky NIC) but doesn’t block you.
  • Phase B runs psw target add for each target, then psw node setup-proxmox, psw node apply-storage, and psw node import.

What it produces: Running Proxmox nodes plus network.yml populated with management.hosts.* and targets.*. The proxmox-installed checkpoint key lands in .psw/checkpoints.yml once the live probe confirms the install really finished.

Why it matters: From here on, the wizard talks to a real server, not just local files.

5. Launch#

What you do: Review the summary at the top of the page (domain, targets, providers, apps), watch psw project check run live against your project (it re-runs every time you land on the page), and when everything is green click Start Bootstrap.

What PSW does: Runs psw deploy bootstrap with your bootstrap target, streaming the full output into a terminal panel right in the page. The phase bar tracks progress through bootstrap’s seven internal steps . If anything fails, you see it live — no hidden logs to dig up. When bootstrap completes successfully, PSW loops over the user apps from your deployment plan and adds them one by one via psw app add. They’ll deploy on the next convergence tick five minutes later. Finally, PSW commits and pushes your project to Forgejo so convergence has the initial state on record.

What it produces: A fully operational self-hosted solution. The dashboard you were using on your laptop silently transitions to operations mode — the same UI, now talking to the real deployed dashboard at https://dashboard.yourdomain. See bootstrap for the full list.

Hard gate on the age key. /wizard/bootstrap refuses to render until the age-key-acknowledged checkpoint is set. If you somehow reach it without having acknowledged the backup (e.g. the checkpoint got deleted), you’re sent back to /wizard/start — which detects the “init done, not acknowledged” state and jumps directly to the backup panel.

Phase Detection — The Magic Behind Resuming#

The wizard’s progress is never stored in a “current step” flag. Instead, it’s derived from the files on disk every time you load a page. Each phase is considered “done” when its output artifact exists or when its checkpoint marker is set. This is the chain:

Artifact / checkpointPhase
project.yml with domainStart: form submitted
nodes/*/hardware.ymlHardware: import done
network.yml:management.gateway + network-verified checkpointStart: network verified (set during Start submission)
deployment-plan.yml with targetsPlan: apps picked
nodes/*/storage.ymlPlan: AI layout produced
network.yml:management.hosts.*.ip + proxmox-installed checkpointInstall: Phase A done
network.yml:targets populatedInstall: Phase B done
providers in project.yml + ssh_key_path in .psw/workstation.ymlPSW init done
age-key-acknowledged checkpointBackup done — Launch unlocks
.psw/converge/state.yml existsBootstrap started
.psw/converge/state.yml with last_result: successOperational

Behind the 5 progress-bar dots the wizard tracks a richer set of sub-phases (e.g. USB_CREATED, NETWORK_CONFIGURED, STORAGE_PLANNED). They project onto their parent dot so the bar stays simple, but they still drive which sub-screen you land on when you revisit a step.

Why checkpoints instead of reading secrets? The previous design ran a SOPS decrypt subprocess on every wizard page load to check OPNsense connectivity. That was both slow and silently fragile — a transient sops error would silently drop the user back a step with no message. Checkpoints live in a single plain-text YAML file (.psw/checkpoints.yml) mapping <name>: <ISO timestamp> — fast to read, easy to inspect, and safe to delete (remove a key to re-unlock its step).

This means:

  • Close your laptop and come back tomorrow — the wizard picks up where it was
  • Delete an artifact or checkpoint to redo a step — the phase drops back, that step unlocks
  • Inspect it at any time with psw project status — same signal, command-line output

There’s no “state” that can get out of sync with reality. The files are the state.

What the Wizard Uses Under the Hood#

The wizard is a UI on top of the normal PSW granular commands — there’s no secret “wizard-only” code path. The dashboard just orchestrates them in the right order, with the right arguments, and streams their output to your browser:

StepRuns
Startpsw project init, psw sops setup, psw provider configure local-dns opnsense-unbound, psw provider configure dhcp opnsense-kea (all in-process, no subprocess)
Hardwarepsw usb install-ventoy, add-iso, create-persistence, add-to-ventoy, link-iso-persistence; then psw node import --from <usb-path>
PlanWrites deployment-plan.yml; then psw node plan (AI)
Installpsw node generate-install-iso → auto-installer on USB; then psw target add, psw node setup-proxmox, psw node apply-storage, psw node import
Launchpsw deploy bootstrap --target <name>; then psw app add for each user app in deployment-plan.yml; then git commit + git push

If you ever want to see exactly what ran, scroll the terminal panel — the full command and its output are right there.

How the Wizard Feels Alive#

A normal web form is one-way: you type, click, wait, refresh. The wizard is different — the moment something happens in the real world (you plug a USB in, your server finishes booting, a command prints a line), the page updates on its own. No refresh, no polling by hand.

This is done with Server-Sent Events (a simple web protocol where the server pushes live updates to the browser) fed by three independent background watchers that the dashboard runs alongside its normal web pages:

WatcherWhat it watchesWhere you see it
USB watcherPlug/unplug events on your workstationHardware step — the device list appears and disappears in real time as you insert or remove sticks
Command streamerThe live output of whatever psw command is currently runningEvery step with a terminal panel (Hardware, Plan, Install, Launch) — you read output line-by-line as it happens, like tailing a log
Node proberWhether each Proxmox node is reachable over SSH yetInstall step — a pulsing yellow dot turns green the moment your server finishes its automated install and comes online

The watchers are fully independent — the USB detector knows nothing about the terminal, the terminal knows nothing about node probing, and so on. One watcher crashing wouldn’t freeze the others. They just each publish their own stream of events, and the pages that care subscribe to the ones they need.

One command at a time. The command streamer runs psw as a subprocess, streams its output, and refuses to launch a second command while one is still running (you get a clear “already running” error, not a silent crash). That matters — two concurrent bootstraps writing to the same project would corrupt state.

Multi-node aware. If you’re installing Proxmox on several nodes at once, the prober runs a separate SSH check per IP and stops watching each one the moment it reports a healthy pveversion. The Install step only unlocks once every node turns green.

Common Questions#

Do I have to use the wizard? No — every step is a granular PSW command . Power users can run the whole sequence from the CLI. The wizard is a convenience layer, not a replacement.

Can I go backwards? Yes. Click any completed dot in the progress bar to revisit it. The page re-renders with your current values pre-filled. Changes are safe — each screen writes its artifact when you move forward.

What if bootstrap fails? Bootstrap is idempotent. Re-running it resumes from where it stopped — see bootstrap . The Launch screen lets you retry with one click, or pass --clean if you want a fresh start.

What if I don’t have OPNsense? Install one first — the wizard’s Start step needs to reach it before anything else happens. The standalone psw opnsense CLI group has commands for provisioning one on Proxmox if you want to do that before starting the wizard.

Can I run the wizard against an existing project? Yes — the wizard uses phase detection , so an existing, partly-configured project just opens at whatever step is next. This is how you resume a half-finished setup.

What happens after the wizard is done? The dashboard switches to operations mode and starts talking to the deployed dashboard at https://dashboard.yourdomain. From there you add apps , monitor runs, and edit config — all via GitOps , no more wizards.

Key Ideas#

  • Five screens, end to end — Start, Hardware, Plan, Install, Launch — from bare hardware to live platform
  • Phase-driven, not step-driven — progress is detected from real artifacts, never from hidden flags
  • Resumable by design — quit at any moment, come back later, continue
  • Thin UI over real commands — every screen maps to granular PSW commands you could also run yourself
  • Refuses to skip the hard parts — OPNsense credentials, storage planning, and the age-key backup all have to work before bootstrap, not after