A PostgreSQL cluster/database freezes its encoding at initdb / CREATE
DATABASE time; a C (non-UTF-8) locale yields a SQL_ASCII cluster, which
makes psycopg3 return bytes and crashes SQLAlchemy. Harden the installer
and add a reusable pattern for future DB installers:
- ensure_utf8_locale_active: generate AND activate en_US.UTF-8 for the
install process before the server package runs initdb; abort if the
locale is not actually available
- create the database explicitly with TEMPLATE template0 ENCODING 'UTF8'
LC_COLLATE/LC_CTYPE 'en_US.UTF-8' instead of inheriting the cluster
default
- assert_db_encoding_utf8: post-install guard, abort with an actionable
message if pg_encoding_to_char is not UTF8 (catches old SQL_ASCII DBs
on re-run too)
- credentials/README docs use su - postgres -c (minimal LXCs have no sudo)
prompt_lxc_config asks 'SSH-Root-Login erlauben? [Y/n]' (env-presettable
via SSH_ROOT_LOGIN, validated, normalized to yes|no). The bootstrap passes
the value into the container; configure_ssh_root_login writes
/etc/ssh/sshd_config.d/zz-root-login.conf (yes -> PermitRootLogin yes,
no -> prohibit-password) and reloads sshd.
- source build.func (script was unrunnable without it)
- validate DB_NAME/DB_USER/DB_PORT/NEXUS_APP_IP before SQL/pg_hba use
- rotate password when role exists but credentials file is missing
webapp-pattern ct/install pair: PGDG repo, database nexus with
least-privilege owner role, pg_hba allowlist restricted to the nexus
app LXC (explicit reject for everything else), pgvector created by the
installer, credentials/DSN summary in /root/nexus-db.credentials.
Idempotent re-runs keep role/db and do not rotate the password.
A reboot before the first deploy would leave the enabled unit in failed
state; the condition keeps it inert until start.sh exists (same guard as
nexus-worker.service).
- new install/nexus-runtime.sh (idempotent, re-runnable on an existing
LXC): uv for the nexus user (manages Python 3.12), tesseract deu+eng,
nexus-worker.service unit (ConditionPathExists guards the skeleton
phase), sudoers extended to cover the worker service
- nexus-install.sh: RUNTIME section now invokes nexus-runtime.sh at the
end of the install (after base sudoers/units, which it extends)
Installs Node.js and act_runner in host mode, registers the runner as
the unprivileged webapp user, and wires up systemd units for the runner
and the next-start service. A narrow sudoers rule lets the runner restart
only webapp.service; the build/deploy itself is driven by the repo workflow.