3.7 KiB
NextWorkspace — Architecture & Workflow
Deployment Model
Single unified script: tools/nextwks.sh (replaces old deploy.sh/install.sh).
| Flag | When to use |
|---|---|
--install |
First-time setup on a bare VM (prompts for config) |
--update |
Rebuild & restart with latest code (uses saved secrets) |
--destroy |
Full greenfield redeploy (tears down everything, rebuilds from /opt/backup/.env) |
Run WITHOUT sudo. The script invokes sudo only where needed (apt, writing to /opt/, iptables).
Key Architecture Decisions
1. Rootless Podman (no sudo for containers)
All podman / podman-compose commands run as the normal user. Containers are rootless.
- Caddy binds to host ports 8080 and 8443 (not 80/443 — unprivileged)
- iptables
PREROUTING+OUTPUT -o loredirect 80→8080, 443→8443 - Firewall rules applied by
tools/firewall-routing.sh, persisted vianetfilter-persistent
2. Ephemeral Build Directory
- Fresh
git clone --depth 1into/tmp/nextwks-build/every time - No persistent
/opt/NextWksrepo (eliminates git permission issues) - Binary + configs built as user, then
sudo cpto/opt/nextworkspace/
3. File Ownership
All files in /opt/nextworkspace/ and /opt/backup/ are chown -R to the non-root user after every deploy.
4. Config Templates with Placeholder Substitution
Config files live in git with {PLACEHOLDER} syntax. The script substitutes values at deploy time:
| File | Placeholders |
|---|---|
config/caddy/Caddyfile |
{DOMAIN}, {TLS_EMAIL} |
config/authelia/configuration.yml |
{DOMAIN}, {JWT_SECRET}, {SESSION_SECRET}, {STORAGE_ENCRYPTION_KEY}, {SMTP_HOST}, {SMTP_PORT}, {SMTP_USER}, {SMTP_PASS} |
config/authelia/users_database.yml |
{ADMIN_PASSWORD_HASH}, {TLS_EMAIL} |
compose/stack.yaml |
{AUTHELIA_SECRET} (= SESSION_SECRET) |
Configs are regenerated on every run (install, update, destroy) from templates + saved secrets.
5. Secrets Vault (/opt/backup/.env)
All values are single-quoted to protect $ signs (bcrypt hashes). Survives --destroy. Sourced with set +u to avoid errors from $ in values.
6. Container Lifecycle
Containers are stopped before copying the new binary (avoids "Text file busy"). Restarted after deploy.
Network Architecture
Internet :443 → [iptables REDIRECT] → host :8443 → [Caddy container :443]
Internet :80 → [iptables REDIRECT] → host :8080 → [Caddy container :80]
Caddy (rootless, nextwks-net)
├── auth.{DOMAIN} → Authelia :9091 (internal, no host port)
├── app.{DOMAIN} → Launcher :9000 (internal, forward auth via Authelia)
└── www.{DOMAIN} → static files
All three containers on nextwks-net (rootless podman bridge).
Only Caddy has host ports (8080, 8443). Authelia and Launcher are internal-only.
Firewall (tools/firewall-routing.sh)
- NAT redirects: 80→8080, 443→8443
- INPUT rules: allow lo, established, SSH (22), Caddy ports (8080, 8443), app ports (8000)
- Global DROP at end for all other unsolicited inbound
- Persisted:
netfilter-persistent save(runs on every deploy mode)
Versioning
- Format:
MILESTONE.FEATURE.PATCH.BUILD(e.g.,0.1.0.0032) - Bump VERSION, update CHANGELOG.md,
git tag v$(cat VERSION)on every change
Workflow
- Edit code in dev clone
git commit -m "msg" && git tag v$(cat VERSION) && git push origin main --tags- On server:
./nextwks.sh --update
Health Check
The script polls podman exec launcher curl -sf http://127.0.0.1:9000/health up to 15×3s. Launcher container uses alpine:latest with curl installed at startup via apk add --no-cache curl.