User Project#
What Is It?#
A User Project is a folder on your computer that describes your entire self-hosted solution . Think of it as the blueprint for your entire infrastructure — it tells PSW which apps you want, where they should run, how your network is configured, and what secrets (passwords, API keys) are needed.
It’s also a git repository (a version-controlled folder where every change is recorded as a snapshot you can inspect or undo), meaning every change you make is tracked. You can go back in time, see what changed, and even push your project to a remote server (like Forgejo ) to trigger automatic deployments .
Why Does It Exist?#
Without a User Project, you’d have to SSH into servers and manually configure everything — install apps, set up databases, configure networking. If something breaks, you’d have to remember what you did and redo it.
With a User Project, your entire self-hosted solution is defined in simple text files. If your server explodes, you can rebuild everything from scratch just by running PSW against your project folder. It’s your self-hosted solution’s source of truth.
What’s Inside?#
Here’s what a typical User Project looks like:
my-project/
├── project.yml # Your project's identity (domain, settings)
├── network.yml # Your network layout (servers, IPs, DNS)
├── state.yml # PSW's memory of what it has done
│
├── nodes/ # Your physical Proxmox servers
│ └── pve1/ # One folder per Proxmox node (named after the node)
│ ├── hardware.yml # What hardware this node has (GPUs, USB devices)
│ └── storage.yml # How storage is configured on this node
│
├── services/ # Your apps (one folder per app)
│ ├── postgres/
│ │ └── service.yml
│ ├── traefik/
│ │ └── service.yml
│ └── jellyfin/
│ ├── service.yml # which target the app runs on
│ ├── routing/jellyfin-routes.yml # Traefik rule for this app
│ ├── bundles/config.yml # Bundle declaration → Backrest plan for this app
│ └── monitoring/jellyfin-scrape.yml # Prometheus scrape target
│
├── secrets/ # Encrypted passwords and API keys
│ ├── infra.yml # SSH keys, server credentials
│ ├── apps.yml # App-specific passwords
│ └── providers.yml # DNS/DHCP provider credentials
│
└── roles/ # App deployment recipes and templates (auto-managed)Cross-app concerns like routing, backups, and monitoring aren’t configured in one big file — each app owns its own small convention
fragment under services/<app>/. The aggregator apps (Traefik, Backrest, Prometheus) collect those fragments at convergence
time; the aggregated output directories are .gitignored because they’re derived from the per-app files.
The Files You Care About#
| File | What It Does |
|---|---|
project.yml | Your project’s main config — domain name, provider settings, ACME (automatic SSL certificate) config |
network.yml | Defines your infrastructure — Proxmox nodes, targets, IPs, DNS |
services/*/service.yml | One per app — says which app runs on which target , and whether it’s active or being removed |
secrets/*.yml | Encrypted credentials — safe to commit to git because they’re encrypted with SOPS |
External systems like your router (OPNsense
) and DNS (Cloudflare
) are configured as providers
, not as apps — the backend choice lands in project.yml and the credentials land in secrets/providers.yml.
The Files PSW Manages For You#
| File | What It Does |
|---|---|
state.yml | PSW’s logbook — records every operation (add, remove, deploy) with timestamps |
roles/ | App deployment recipes (app metadata ) and templates copied from the app catalog — you don’t edit these |
services/<aggregator>/<output-dir>/ | Traefik/Prometheus/Backrest config collected from per-app fragments. .gitignored and rebuilt on every convergence
run — the per-app files in services/<app>/ are the source of truth. |
How Do You Create One?#
The easiest way is through the project launcher
: run psw wizard, pick “New Project”, choose a folder, and the Setup Wizard
walks you through everything from there — entering your domain, configuring storage, and all the way through bootstrap
.
If you prefer the CLI, you can create one manually:
psw -C ~/my-project project init --domain mylab.example.comThis creates the PSW-specific directories (services/, secrets/, roles/, nodes/) and marks the project as ready for use.
How Do You Use It?#
Everything you do with PSW happens in the context of a User Project. You point PSW at your project with the -C flag:
# Add an app
psw -C ~/my-project app add jellyfin
# Check for problems
psw -C ~/my-project project check
# See what's installed
psw -C ~/my-project project status
# Preview what a deployment would do
psw -C ~/my-project project planThe Git Workflow#
Because your project is a git repo, the typical workflow looks like this:
- Make changes locally — add apps, tweak configs
- Commit your changes —
git commit - Push to Forgejo —
git push - Automatic deployment — the convergence engine detects the changes and deploys them
This is the GitOps workflow — you never SSH into your servers to make changes. You edit files in your project, push, and PSW takes care of the rest.
Key Ideas#
- One project = one self-hosted solution — each project describes a complete self-hosted solution
- Git-tracked — every change is versioned, so you can always roll back
- Secrets are encrypted — passwords and API keys are stored safely using SOPS encryption
- Declarative — you describe what you want, and PSW figures out how to get there via convergence
- Self-contained — given the project files and the SOPS decryption key, you can rebuild your entire self-hosted solution from scratch