Hetzner Runtime Config
Date: 2026-05-05
Objective
Make the first runtime config setup on Hetzner repeatable without copying commands by hand and without risking checked-in secrets.
The helper creates real, ignored runtime config files from the checked-in templates. It does not fill in secrets. A human still replaces placeholders with real staging values on the Hetzner host.
What Was Added
scripts/hetzner-config.mjsSafe runtime config initializer for Hetzner.package.jsonscripts:hetzner:config:planhetzner:config:statushetzner:config:checklisthetzner:config:init:dryhetzner:config:init
Files Managed
The helper can create these ignored runtime files when they are missing:
configs/hetzner/studio-api.envconfigs/hetzner/zweistein-server.envconfigs/hetzner/zweistein-query-engine.envconfigs/hetzner/zweistein-ingestion-worker.envconfigs/hetzner/frontend-build.envconfigs/hetzner/reverse-proxy.envconfigs/hetzner/backup.envconfigs/hetzner/Caddyfile
The .env files are created with mode 0600. The Caddyfile is created with mode 0644.
configs/hetzner/frontend-build.env is intentionally a Compose-level file, not only a frontend file. It also carries the first infrastructure values that Docker Compose needs before services start:
POSTGRES_PASSWORDMINIO_ROOT_USERMINIO_ROOT_PASSWORD
configs/hetzner/backup.env also points backup and restore commands at that same Compose env file through:
COMPOSE_ENV_FILE=configs/hetzner/frontend-build.env
That keeps backup and restore rehearsals in the same Compose variable context as the normal stack commands.
Safety Rules
- Existing runtime config files are skipped, not overwritten.
- No config values or secrets are printed.
- Real
configs/hetzner/*.envfiles stay ignored by.gitignore. configs/hetzner/Caddyfilestays ignored by.gitignore.- The helper creates placeholder copies only;
npm run hetzner:env:checkis still the strict gate before builds.
Server Workflow
Run this on the Hetzner host after the source tree has been synced:
npm run hetzner:config:plan
npm run hetzner:config:status
npm run hetzner:config:checklist
npm run hetzner:config:init:dry
npm run hetzner:config:init
Then edit the newly created runtime files on the server and replace every placeholder with real staging values.
After editing:
npm run hetzner:env:check
That strict check fails if a required runtime file is missing, if an obvious placeholder remains, or if a required key is empty. It prints only file names, key names, and line numbers.
Runtime Value Checklist
The checklist command is a safe preparation step before editing real server files:
npm run hetzner:config:checklist
It reads only the checked-in .example templates and prints:
- required keys that must be filled before
hetzner:env:checkcan pass; - other placeholders that must be replaced before
hetzner:env:check; - staging defaults that should be reviewed;
- optional blank keys;
- the target runtime file and file mode for each template.
It does not read configs/hetzner/*.env, does not print runtime values, and does not create files.
Local Verification Evidence
node --check scripts/hetzner-config.mjspassed.npm run hetzner:config:planprints the runtime config workflow.npm run hetzner:config:statusreports the expected missing runtime files locally without printing values.npm run hetzner:config:checklistprints the server value checklist from templates only.npm run hetzner:config:init:dryshows the files it would create and writes nothing.npm run legacy:verifyincludesscripts/hetzner-config.mjsas a required foundation file.
Current Boundary
The actual npm run hetzner:config:init command should be run on Hetzner, not on this local Mac, because the real runtime config files are server-local and intentionally ignored.