Files
Varaverk/Deployment/README-Deployment.md
T

9.3 KiB

━━━━━ DEPLOYMENT ━━━━━

The schema layer. Configurations/*.conf holds every value the ecosystem runs on — and is gitignored, because it holds credentials. This folder holds the templates those confs are built from, and the two scripts that keep the confs in step with them.

Two files that are versioned, and two scripts that reconcile the unversioned confs against them:

  Deployment/master.conf.template     349 vars   ← the versioned schema
  Deployment/host.conf.template       140 vars   ← per-host schema, HOSTN_-prefixed
  Deployment/conf_upgrade.sh                     ← template → conf, values preserved
  Deployment/conf_populate.sh                    ← running services → conf, empty fields only

The templates are the only versioned record of what configuration exists. Nothing else in git knows that a variable is supposed to be there.


━━━ THE PROBLEM THAT BUILT THIS ━━━

Config Holds Secrets, So Config Cannot Be Committed master.conf and host*.conf contain API keys, passwords, SSH key paths and personal hostnames. They are gitignored, along with their .bak files:

.gitignore:4   Configurations/host*.conf
.gitignore:5   Configurations/master.conf
.gitignore:6   Configurations/*.bak

That is correct and non-negotiable. But it creates a problem: if the confs are not in git, then git has no idea a new setting was ever added. A script that starts reading NEW_THRESHOLD works on the machine where it was developed and silently fails everywhere else, because no other node's conf has that key.

A New Node Would Start With Nothing Without a versioned schema there is no way to stand up a second server, or rebuild a wiped one, except by hand-copying a conf from a machine that already works — which means copying its credentials too.

Hand-Editing Confs Across Nodes Does Not Scale Two servers, ~490 variables between them. Adding a setting by hand means editing it on every node, in the right section, with the right default, without disturbing the values already there. Miss one and the failure surfaces days later as a script behaving differently on one host.


━━━ WHAT THIS FOLDER DOES ━━━

🔀 Schema Merge — conf_upgrade.sh

Merges a template into an existing conf while preserving every value the user has already set. Runs automatically from git_pull_execute.sh after every single pull.

  Key in template only  →  ADDED     placeholder/default, filled in once
  Key in conf only      →  REMOVED   deprecated in this version
  Key in both           →  KEPT      the conf's value always wins
  Comments, blank lines →  from the template — structure follows the new version

That last rule is what makes it safe to run unattended forever: the template supplies structure and new keys, never settings. Your values cannot be overwritten by a pull.

The live confs currently match their templates exactly — 349 and 140 variables — which is what a working merge looks like.

🔎 Credential Discovery — conf_populate.sh

Reads settings out of the services actually running on this host and writes them into the host conf: arr API keys from each config.xml, ports from real docker port bindings, paths from real volume mounts, SABnzbd/slskd/qBittorrent credentials from their own config files.

Only fills empty fields unless --overwrite. Manual — it is not scheduled anywhere.


━━━ HOW A CHANGE REACHES EVERY NODE ━━━

You add a variable
    │
    └── edit Deployment/master.conf.template   ← the versioned schema
            │
            git push
            │
            └── every node: git_pull_execute.sh
                    │
                    └── conf_upgrade.sh --template ... --target ... --backup
                            │
                            ADDED   → new key appears with the template default
                            KEPT    → every existing value untouched
                            REMOVED → deprecated keys dropped

This is the rule that follows from it, and it is not optional:

Any conf variable change — add, remove, or rename — must update Deployment/master.conf.template and Deployment/host.conf.template in the same pass as the script change that uses it.

A script merged without its template entry works only on the machine it was written on. Nothing errors; the variable is simply empty everywhere else, and the script takes whatever branch an empty value leads to.


━━━ THE HOSTN_ PLACEHOLDER ━━━

host.conf.template is written with a generic prefix — 149 occurrences of HOSTN_:

HOSTN_SONARR_URL=""
HOSTN_SONARR_API_KEY=""

git_pull_execute.sh substitutes the real identity before merging, so keys match the target:

sed "s/HOSTN_/${MY_ID}_/g; s/REMOTE_ID/${REMOTE_ID}/g" host.conf.template > "$TMPL_RESOLVED"

One template therefore serves every host. HOST1 merges it as HOST1_*, HOST2 as HOST2_*, and a third node would work with no template change at all.


━━━ SCRIPTS IN THIS FOLDER ━━━

Script Role When It Runs
conf_upgrade.sh Merge template into conf — structure forward, values preserved Automatically, after every git pull
conf_populate.sh Detect settings from running services into the host conf Manually — onboarding, or after a key rotation
migrate_data_layout.sh Move everything persisted into the rooted data/ tree Once per host, manually. Idempotent.

📦 Data layout migration — migrate_data_layout.sh

conf_upgrade.sh adds keys the template has and the installation does not; it never rewrites a value you already have. That is exactly what you want from it, and exactly why it cannot perform a layout migration — the paths being moved are existing keys, so their values would keep pointing at the old layout forever while the new directory variables sat beside them unused.

So this rewrites those values and moves the files to match. Both halves or neither.

Deployment/migrate_data_layout.sh --dry-run    # always first
Deployment/migrate_data_layout.sh

It refuses to run while a job from this installation is active — scoped to the installation's own path, because pgrep is system-wide and a box running both a production checkout and a development clone will otherwise always look busy. --force overrides.

Each host runs it itself: data/ is gitignored, so a restructure travels as code and conf while the files stay where they are. See data/README.md for the resulting layout.

Template Role
master.conf.template Shared schema — thresholds, toggles, profiles, orchestrator job lists
host.conf.template Per-host schema — credentials, paths, container names. HOSTN_-prefixed

━━━ SAFEGUARDS WORTH KNOWING ━━━

The install is atomic. conf_upgrade.sh stages the merged conf beside the target and installs it with a rename, never a copy. A cp truncates the live conf and writes into it — and every watchdog sources load_config.sh on every run, so anything reading during that window would get a partial conf with empty path variables. The temp file is staged in the target's own directory deliberately: /tmp is rootfs while the confs are on flash, and a cross-device mv degrades to copy-then-unlink, which is the exact torn write being avoided.

Dry run needs no privilege, writing does. --dry-run prints the full ADDED / REMOVED / KEPT report and is useful to anyone. Installing over a conf under /boot requires root.

conf_upgrade.sh sources nothing — deliberately. No load_config.sh, no common.sh. It is the tool that repairs the conf load_config.sh depends on, so it has to work when that conf is broken, partial, or missing keys. That is also why it uses plain echo rather than log(), and why it has no acquire_lock — concurrency is handled by the atomic rename instead, and since the merge is idempotent, last-writer-wins is identical to running once.

conf_populate.sh refuses to guess a container. An ambiguous name prefix skips the field rather than picking the first match. Writing the wrong container name is worse than writing nothing: an empty field is visibly incomplete and gets fixed, a wrong one silently points the whole stack at the wrong instance. This host has a live example — authelia prefix-matches both Authelia (9091) and Authelia-Secondary (9092).


━━━ THE RECOVERY GAP ━━━

Confs are gitignored, and so are their .bak files. There is no versioned history to revert to, and the single .bak slot is overwritten by whoever writes next:

file modified its .bak
master.conf Jul 28 18:52 Jul 28 18:52
host1.conf Aug 1 21:00 Jul 3 17:46

A bad write to master.conf currently falls back to a file that may predate weeks of edits. Worth knowing before hand-editing a conf, and the reason --backup exists on conf_upgrade.sh at all.

Moving Configurations/ into a private repo would make git diff and git revert the recovery mechanism and give the history for free. That overlaps the existing GitHub-mirror TODO, which is blocked on the same question — see Notes_AI-Design.md, where it also blocks AI-assisted conf writes.