The "one box on a Pi, in a homelab, or a small VPS" path. ~5 minutes of configuration. Suitable up to a few dozen developers on a single tenant (or a few small tenants).
┌──────────────┐ TLS ┌──────────────┐
│ internet │ ─────► │ Caddy │
└──────────────┘ │ :80/:443 │
└──────┬───────┘
│ HTTP
┌───────────────────────┼────────────────────────┐
│ │ │
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌───────────────┐
│ skill-pool │ sqlx │ Postgres │ files │ /var/lib/ │
│ server │ ──────► │ │ ───────► │ skill-pool/ │
│ :8080 │ │ :5432 │ │ bundles/ │
└────────────┘ └────────────┘ └───────────────┘
▲
│ HTTP (SSR)
┌─────┴──────┐
│ skill-pool│
│ web │
│ :3000 │
└────────────┘
sudo apt install -y postgresql-16
sudo -u postgres psql <<'SQL'
CREATE ROLE skillpool LOGIN PASSWORD 'changeme';
CREATE DATABASE skillpool OWNER skillpool;
\c skillpool
CREATE EXTENSION IF NOT EXISTS vector;
SQLRun migrations against the new database. The server binary itself does not auto-migrate on startup — migrations are a separate step so a broken deploy can never run a migration as a side effect:
sqlx migrate run --source server/migrations \
--database-url 'postgres://skillpool:changeme@localhost/skillpool'# Either from the pre-built Docker image, or build from source:
cargo build --release -p skill-pool-server
sudo install -o root -g root -m 0755 \
target/release/skill-pool-server /usr/local/bin/sudo useradd --system --home /var/lib/skill-pool --shell /usr/sbin/nologin skillpool
sudo mkdir -p /var/lib/skill-pool/bundles /etc/skill-pool
sudo chown -R skillpool:skillpool /var/lib/skill-pool
sudo cp packaging/systemd/skill-pool-server.service /etc/systemd/system/
sudo install -o skillpool -g skillpool -m 0600 \
packaging/systemd/skill-pool-server.env.example \
/etc/skill-pool/skill-pool-server.env
sudoedit /etc/skill-pool/skill-pool-server.env # paste real DSN + secrets
sudo systemctl daemon-reload
sudo systemctl enable --now skill-pool-server
journalctl -u skill-pool-server -fsudo cp packaging/proxy/Caddyfile /etc/caddy/Caddyfile
sudoedit /etc/caddy/Caddyfile # set your real domain + email
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddyFor wildcard certs (tenant subdomains), add a DNS provider plugin and
uncomment the acme_dns line in the Caddyfile.
sudo -u skillpool skill-pool-server admin tenant-create \
--slug acme --name "Acme Inc."
sudo -u skillpool skill-pool-server admin token-create \
--tenant acme --name bootstrap
# → prints the raw token once. Save it; use as `Authorization: Bearer …`.Two things to back up:
- Postgres:
pg_dump skillpoolnightly. The DB carries metadata, tenants, tokens, themes, audit log. - Bundle storage: tar
/var/lib/skill-pool/bundlesweekly (changes slowly; bundles are immutable once published).
For object-storage backends (S3/GCS), enable bucket versioning instead.
The deploy / rollback workflow that uses these backups is documented
in docs/ops/rollback.md (forward-only sqlx migrations + restore from
snapshot — read it before your first production deploy).
- Need read replicas? See
docs/deploy/kubernetes.md— same image, just setSKILL_POOL_DATABASE_READ_URLand the server routes reads. - Want declarative config? See
docs/deploy/nixos.md.