Single-Host Container Deployment¶
For multiple API or worker replicas, one-shot migrations, and optional shared login throttling, see Distributed Deployment.
Hubuum can be installed on one host with Docker Compose or rootful Podman Compose. The installer uses published container images by default and can deploy either:
all: backend API, frontend, Caddy, Postgres, and Valkey.backend: backend API, Caddy, and Postgres.
Both modes can also use an existing Postgres server instead of creating a Postgres container.
Requirements¶
- Linux host with inbound TCP
80and443open. - DNS for the selected hostnames pointing at the host.
openssland either Docker with the Docker Compose plugin or rootful Podman withpodman compose.gitis only required when using--build-from-source.- Run the script as
rootor throughsudo. - Rootless Podman is not supported by this installer because Caddy binds privileged host ports
80and443.
Container Engine¶
By default, the installer auto-detects a compose engine:
- Docker Compose is preferred when both Docker and Podman are available.
- Podman Compose is used when Docker Compose is unavailable.
- Use
--engine dockeror--engine podmanto force one engine.
Podman support is intended for rootful installs only.
All-In-One¶
sudo ./scripts/install-single-host.sh \
--web hubuum.example.com \
--api hubuum-api.example.com \
--email admin@example.com
This creates /opt/hubuum by default, generates /opt/hubuum/.env, writes compose.yml and Caddyfile, pulls published images, and starts the stack.
HTTP Availability Model¶
Single-host installations run a primary and standby backend behind Caddy. The
primary retains the default all runtime role, including background workers;
the standby uses the HTTP-only api role. All-mode installations likewise run
two frontend containers. Caddy actively checks backend /readyz and frontend
HTTP health, excludes unavailable replicas, and retries connection failures
against a healthy replica.
A changed Caddy configuration can close a client connection before its first
HTTP response while the old HTTP server shuts down. This is distinct from an
unavailable backend. The live rollout test permits one retry of a read-only
/healthz or /readyz probe after such a pre-response disconnect, within the
original seven-second deadline, and reports these retries separately. HTTP
errors, connection refusal, timeouts, partial responses, and repeated
disconnects still fail the test. This does not authorize replaying writes or
promise uninterrupted TCP connections across a proxy configuration reload.
The generated configuration keeps the backend active-check interval at five seconds but defines only one health-checked reverse proxy per listener. In an all-mode public API deployment, each backend receives at most one public Caddy probe, one private BFF Caddy probe, and one Compose health check every five seconds. Increasing the interval would reduce this already small load but would also delay failed-upstream removal and rolling-update convergence. The readiness handler performs its schema and maintenance checks on one pooled connection.
The frontend BFF reaches the backend through a private Caddy listener, so it uses the same readiness-aware backend pool as direct API traffic. All-mode installs also use the bundled Valkey service for shared backend login throttling across the two API replicas.
This removes planned HTTP interruption during rolling-compatible updates. It does not provide host, Caddy, PostgreSQL, or Valkey high availability because those components still have one instance on the same machine. Background work also pauses while the primary is replaced. Database migrations must remain compatible with the old application version for the duration of the rolling update; a migration that blocks application queries can still cause request latency even though an HTTP replica remains online.
The resource-revision release supports the ordinary adjacent-release API overlap certified by CI: drain old workers first, run the migration while an old API replica serves requests, and then roll API replicas. Quiesce backup, restore, and import operations during the transition because backup v4 and import v2 intentionally reject their older formats. Do not start new workers until the candidate schema is ready.
The standby owns its own database pool and application memory. Capacity-plan external PostgreSQL servers for both API pools, the primary's task-lease connection, migration/administration work, and operational headroom. See Distributed Deployment for the connection-budget formula.
Runtime metrics are also process-local. /metrics targets the worker-enabled
primary, while /metrics/standby targets the HTTP-only standby; scrape both as
separate Prometheus targets. They intentionally do not fail over to one another,
because doing so would mix independent counters and produce invalid rates.
The installer also creates an empty /opt/hubuum/auth.toml for local-only
authentication. This empty placeholder is mode 0644 so the non-root API can
read it. Before adding provider credentials, apply the protected ownership and
permissions described below. Use --auth-config to point the deployment at an
existing external-auth TOML file instead.
By default, the installer starts the stack directly with Compose. Pass --systemd to also write /etc/systemd/system/hubuum.service, enable it, and start the stack through that unit.
Default app images:
- Backend:
ghcr.io/hubuum/hubuum-server:main - Frontend:
ghcr.io/hubuum/hubuum-frontend:main
Optional monitoring¶
Add --monitoring during installation, or run
sudo /opt/hubuum/update-single-host.sh --monitoring to enable Prometheus and
Grafana later. Caddy serves /grafana/ and /prometheus/ on the frontend domain
in all mode, or the API domain in backend mode. This works with each
shared-host routing mode. Grafana has its own login; Prometheus uses a separate
Caddy password gate. Both have generated credentials in the root-readable
.env, private container ports, persistent volumes and pinned images.
The shared operator package documents credential
retrieval, seven dashboards, SLOs, limits, notifications and a walkthrough.
The installer loads those same dashboards and rules for both process targets;
distributed deployments can consume the files directly or through Prometheus
Operator. Updates preserve credentials and data, stop/uninstall preserve volumes,
and explicit --purge removes them. External monitoring is needed to report a
complete host outage independently.
Choosing Image Tags¶
Fresh installations follow main for both application images. Use --tag
to select a shared tag, or --server-tag and --frontend-tag to choose each
independently. --backend-tag is an alias for --server-tag.
# Follow development builds for both applications.
sudo ./scripts/install-single-host.sh \
--web hubuum.example.com \
--api hubuum-api.example.com \
--email admin@example.com \
--tag main
# Pin the server while following frontend stable releases.
sudo ./scripts/install-single-host.sh \
--web hubuum.example.com \
--api hubuum-api.example.com \
--email admin@example.com \
--server-tag v0.0.14 \
--frontend-tag latest
Per-application tags override --tag regardless of argument order. Explicit
--backend-image and --frontend-image references take precedence over tag
options for their application. Otherwise, tag options retain the configured
image repository, including a custom registry, and replace its tag or digest.
The resulting image references are saved as BACKEND_IMAGE and
FRONTEND_IMAGE in .env. Re-running either script without tag options keeps
those choices, including installations previously configured to follow main.
latest follows stable releases and main follows development builds when you
run an update. A version tag such as v0.0.14 stays on that version; the scripts
still pull the tag, so an image republished under the same tag can change. Use a
full image reference with @sha256:... when you need immutable image contents.
Each selected tag must exist in its image repository; server and frontend
release versions can differ. The server latest alias will first be published
by the next stable release containing this change. Until then, use main or an
existing version such as --server-tag v0.0.14.
Image tag options do not change management-script refs (--script-ref) and
cannot be used for source builds, which select code through --backend-ref
and --frontend-ref when installing.
Frontend BFF And Public API¶
All-in-one installs expose both public hostnames:
--webserves the frontend.--apiserves the backend API directly for integrations and API users.
The frontend is still configured as a BFF-style service. Browser clients can call frontend-owned routes such as /api/v0/auth/login, /api/v1/..., and /api/hubuum/...; the frontend reaches the backend over the private compose network with:
The private Caddy listener load-balances both ready API replicas. Backend bearer tokens remain server-side for frontend browser flows, while the backend API is still publicly available on the API hostname.
Shared Host Routing¶
You can use the same DNS name and public 80/443 ports for both frontend and API by setting --web and --api to the same hostname. In that case, choose an explicit routing mode:
sudo ./scripts/install-single-host.sh \
--web hubuum.example.com \
--api hubuum.example.com \
--shared-host-routing direct \
--email admin@example.com
Modes:
direct: sends backend-owned paths such as/api/v0...,/api/v1...,/api-doc..., and/swagger-ui...through one load-balanced backend handler, routes/metricsand/metrics/standbyto stable process targets, and sends everything else to the frontend. This is the recommended shared-host mode now that frontend-owned BFF routes live under/_hubuum-bff/....prefixed: exposes the backend under/hubuum-api/, including stable/hubuum-api/metricsand/hubuum-api/metrics/standbyprocess targets, and sends everything else to the frontend. This is useful if you want to avoid exposing backend routes at their native paths.bff: sends all traffic to the frontend. The backend is not directly exposed by Caddy in this mode; use it only when frontend proxy coverage is the intended public API surface.
The frontend makes shared-host deployments easier by keeping internal/BFF routes under /_hubuum-bff/..., which does not collide with direct backend routes like /api/v1/....
Curl Install¶
The installer is self-contained enough to run directly from the repository:
curl -fsSL https://raw.githubusercontent.com/hubuum/hubuum/main/scripts/install-single-host.sh \
| sudo bash -s -- \
--web hubuum.example.com \
--api hubuum-api.example.com \
--email admin@example.com
Use a branch, tag, commit SHA, or PR ref by changing the raw GitHub URL. Pass the same ref with --script-ref so the installed management helpers come from the same source:
REF=main
curl -fsSL "https://raw.githubusercontent.com/hubuum/hubuum/${REF}/scripts/install-single-host.sh" \
| sudo bash -s -- \
--script-ref "${REF}" \
--web hubuum.example.com \
--api hubuum-api.example.com \
--email admin@example.com
Examples:
- Branch:
REF=my-feature-branch - Tag:
REF=v0.1.0 - Commit:
REF=1a2b3c4d5e6f... - Pull request head:
REF=refs/pull/123/head
For pull requests from forks, GitHub may not expose package/image changes yet; use --build-from-source when you need to test code from that PR rather than published images.
The script installs management helpers into the install directory:
install-single-host.shupdate-single-host.shsingle-host-rollout.shstop-single-host.shuninstall-single-host.sh
Backend Only¶
Equivalent explicit form:
sudo ./scripts/install-single-host.sh \
--mode backend \
--api hubuum-api.example.com \
--email admin@example.com
Existing Postgres¶
Pass --database-url to skip the managed Postgres container:
sudo ./scripts/install-single-host.sh \
--web hubuum.example.com \
--api hubuum-api.example.com \
--email admin@example.com \
--database-url 'postgres://hubuum:secret@postgres.example.com:5432/hubuum?sslmode=require'
This uses the default single-role topology. To isolate the runtime credential, opt into split roles and provide both URLs:
sudo ./scripts/install-single-host.sh \
--web hubuum.example.com \
--api hubuum-api.example.com \
--email admin@example.com \
--database-role-mode split \
--database-url 'postgres://hubuum_runtime:runtime-secret@postgres.example.com:5432/hubuum?sslmode=require' \
--migration-database-url 'postgres://hubuum_migrator:migration-secret@postgres.example.com:5432/hubuum?sslmode=require'
For split mode, the runtime and migration identities must be provisioned before installation. The migration URL is injected only into the transient migration container and the isolated restore executor; API and worker containers receive only the runtime URL. See PostgreSQL Database Roles for the required grants, managed-service workflow, and upgrade diagnostics.
Database TLS behavior follows the URL's sslmode:
sslmode=disableuses an unencrypted connection.sslmode=preferverifies the server certificate and hostname when the server offers TLS, but permits plaintext when the server does not support TLS.sslmode=requirerequires TLS and verifies the server certificate and hostname.- URLs without
sslmodedisable TLS forlocalhostand IP loopback addresses. Other hosts use the verifiedpreferbehavior.
The platform trust store is used by default. Set PGSSLROOTCERT to a PEM CA
bundle for private certificate authorities, or to system to explicitly use
the platform trust store. Custom CA bundles must be regular files no larger than
4 MiB.
The database must already exist and be reachable from containers on the host. Avoid localhost in the URL unless Postgres is inside the same container; from a container, localhost means the API container itself.
External Authentication Configuration¶
Pass an absolute host path with --auth-config to enable LDAP or another
configured external identity provider:
sudo ./scripts/install-single-host.sh \
--web hubuum.example.com \
--api hubuum-api.example.com \
--email admin@example.com \
--auth-config /etc/hubuum/auth.toml
The installer validates that the file exists and is readable, records its
absolute host path in /opt/hubuum/.env, and bind-mounts it read-only as
/etc/hubuum/auth.toml in the API container. The container receives
HUBUUM_AUTH_CONFIG_PATH=/etc/hubuum/auth.toml automatically. The file is not
copied into the installation directory.
Provider files must be readable by the API process inside the container. The
standard image uses UID/GID 10001:10001; keep root ownership and grant that
group read access before adding credentials. For example:
For a custom image, use its configured process group instead of 10001. The
installer preserves the contents and permissions of existing provider files;
it only repairs permissions on its own empty local-only placeholder.
Re-running the installer without --auth-config preserves the stored host
path. To use a different file while updating, pass it to the update helper:
The update helper validates and persists the new path before rolling both API
containers. Editing the currently mounted host file still requires the API
containers to be replaced because auth-provider configuration is loaded at
startup. Run update-single-host.sh without --auth-config to roll them after
such an edit.
See External Authentication for the TOML schema and LDAP examples.
Source Builds¶
The installer does not clone repositories by default. Use --build-from-source only when you need to build local images from Git refs that are not available as published container images:
sudo ./scripts/install-single-host.sh \
--web hubuum.example.com \
--api hubuum-api.example.com \
--email admin@example.com \
--build-from-source \
--backend-ref main \
--frontend-ref main
In source-build mode, the installer clones the repositories under /opt/hubuum/src and builds local/hubuum-api:single-host and local/hubuum-web:single-host.
Service Management¶
Systemd service management is opt-in. Install with --systemd to manage the stack with systemd:
sudo systemctl status hubuum
sudo systemctl restart hubuum
sudo systemctl stop hubuum
sudo systemctl start hubuum
The containers use restart: unless-stopped; a systemd unit adds an explicit
host-boot contract. It runs compose up -d on start and compose down on stop
from the install directory. An explicit systemctl restart therefore restarts
the entire stack and is not the zero-downtime update path; use
update-single-host.sh for application updates.
Use --service-name NAME to install the unit under a different name. Without --systemd, manage the stack directly with Compose.
Updates¶
The installer copies install-single-host.sh, update-single-host.sh,
single-host-rollout.sh, stop-single-host.sh, and
uninstall-single-host.sh into the install directory. Before pulling or
building application images, the updater refreshes those management scripts
and regenerates compose.yml and Caddyfile from the current installer:
Use the same tag options to override and save the image choices for this and future updates:
# Switch both applications to stable releases once both latest tags exist.
sudo ./update-single-host.sh --tag latest
# Pin only the server; keep the saved frontend choice.
sudo ./update-single-host.sh --server-tag v0.0.17
# Follow server development builds while keeping the frontend on stable releases.
sudo ./update-single-host.sh --tag main --frontend-tag latest
For image-based installs, the update command pulls the latest configured
images. For source-build installs, it fetches the source checkouts and rebuilds
the local app images. Existing .env values, including operator-added
settings, are preserved except for explicit overrides; defaults introduced by a newer installer are appended
only when their keys are missing. On an established rolling installation, it
then queries the candidate's hubuum-admin --migration-mode. A failed or
unrecognized preflight stops the update before application processes are stopped.
The candidate must support this command (0.0.17 or newer).
For the 0.0.16 to 0.0.17 transition, schedule downtime. Quiesce backup, restore, and import operations, stop both APIs and the restore executor, and capture a PostgreSQL snapshot before invoking the updater. Retain matching old binaries and credentials. Pending offline migrations cause the updater to stop both APIs (including primary workers) and the restore executor before migration, then start only candidate processes. If migration fails, old binaries remain stopped. Fix forward or restore the pre-upgrade snapshot; binary-only rollback is unsupported and snapshot recovery loses subsequent writes. See the event migration guide.
With no pending offline migration, it performs the rolling sequence:
- Stop the all-role primary gracefully while the API-only standby continues serving HTTP. This drains old-version workers before schema migration.
- Run embedded database migrations as a one-shot command. If migration fails, restart the unchanged primary.
- Replace the primary backend, wait for
/readyz, reload Caddy, and wait for its passive upstream failure state to clear. - Replace the HTTP-only backend standby and the frontend standby in all mode, wait for their health checks, then reload Caddy.
- Replace the primary frontend in all mode and reload Caddy once more after it is healthy.
If a previous attempt left a primary unhealthy while its standby is healthy, the next run recovers and reloads that primary before touching the only usable standby. The helper also starts a missing or stopped managed PostgreSQL or Valkey service without recreating an existing infrastructure container.
Caddy, PostgreSQL, and Valkey remain running throughout this sequence. Newly pulled images for those infrastructure services are not activated by the rolling application update; schedule a maintenance window when those services themselves need replacement. A changed generated Caddyfile is applied by the normal Caddy reload in the rolling sequence.
Pass --auth-config /absolute/host/path.toml to change the read-only external
authentication file as part of the same update and rolling replacement.
Updates use Compose directly even when the optional systemd unit exists. The unit remains active and continues to provide the host-boot and explicit-stop contract; it is not restarted during an application rollout.
Older installations can use a current copy of either the installer or updater, with or without tag options. Their existing full image references need no conversion: omitting tag options preserves the saved images, while explicit tag options replace the selected tags. Both scripts refresh the installed management helpers and generate the current Compose and Caddy configuration, including standby services for installations that predate rolling updates.
If the installed updater predates these options or automatic configuration refresh, run the current updater directly:
curl -fsSL https://raw.githubusercontent.com/hubuum/hubuum/main/scripts/update-single-host.sh \
| sudo bash -s -- --dir /opt/hubuum
Add --tag or per-application tag options after --dir /opt/hubuum to change
the saved choices during that upgrade. Re-running the current installer works
the same way. Later runs of the refreshed, installed updater pick up deployment
file and management-script changes automatically. The updater refuses to roll
if configuration refresh fails to provide the required services.
The live container contract test covers adoption from the published v0.0.1 image: the old API remains online while the candidate applies all newer migrations, starts a ready standby, and replaces the old primary. Before this specific first-adoption upgrade, prevent new task submissions and allow queued and running tasks to drain. v0.0.1 co-locates lease-unaware task workers with its API and has no API-only standby to own traffic before migration, so the task-lease and task-provenance migrations cannot safely transfer an in-flight task to a new worker. The tested idle-queue upgrade preserves HTTP availability; it does not promise uninterrupted background task execution.
A separate release-blocking test resolves the latest stable release by
immutable image digest. It seeds users, groups, scoped credentials, nested
collections, classes, objects, relations, computed fields, events, exports,
remote targets, and asynchronous work through that release; drains its worker;
and measures candidate migration availability. It then exercises both API
versions against the migrated schema and proves an N-1 application rollback
before restoring the candidate. This certifies only adjacent application
rollback and does not downgrade PostgreSQL or promise compatibility with older
application generations.
Re-running The Installer To Update¶
The installer is idempotent and doubles as an in-place updater. Re-running it against an existing install directory reuses the configuration recorded in .env (mode, hostnames, email, images, refs, ports, network subnet, container engine, and systemd service name) and preserves generated secrets and the managed database, so you only need to pass the arguments you want to change:
# Pull the latest configured images and apply any changed configuration in place.
curl -fsSL https://raw.githubusercontent.com/hubuum/hubuum/main/scripts/install-single-host.sh \
| sudo bash -s -- --dir /opt/hubuum
# Change a single setting (for example, pin a new backend image) and reuse the rest.
sudo ./install-single-host.sh --backend-image ghcr.io/hubuum/hubuum-server:v1.2.3
Any value passed explicitly on the command line overrides the stored one;
everything else is taken from the existing .env. Application containers are
rolled in standby-then-primary order. Infrastructure-container changes remain
pending until the operator schedules their replacement.
Stop And Uninstall¶
Stop the stack:
Equivalent direct form:
Uninstall stops the stack and removes the systemd unit if one exists. By default, it preserves /opt/hubuum and compose volumes:
Use --purge to also remove compose volumes and the install directory:
Parameters¶
Required:
--api: public backend API hostname served by Caddy.--api-port: internal backend API listen port. Default:8080.--email: Let's Encrypt registration email.--web: public frontend hostname. Required only inallmode.
Common optional parameters:
--stop: stop the installed stack and exit.--uninstall: stop the stack, remove the systemd unit if present, and exit.--purge: with--uninstall, also remove compose volumes and the install directory.--dir: install directory. Default:/opt/hubuum.--mode:allorbackend. Default:all.--database-url: existing Postgres URL. If omitted, the installer creates a managed Postgres container.--database-role-mode:single(default) orsplit.--migration-database-url: privileged Postgres URL required with an external database only in split mode.--auth-config: absolute path to a host auth-provider TOML file. The API container mounts it read-only at/etc/hubuum/auth.toml.--engine:auto,docker, orpodman. Default:auto.--tag: shared application image tag. Fresh install default:main; existing installs reuse saved images.--server-tag/--backend-tag: backend tag, overriding--tag.--frontend-tag: frontend tag, overriding--tag.--backend-image: full backend image reference, overriding tag options. Default:ghcr.io/hubuum/hubuum-server:main.--frontend-image: full frontend image reference, overriding tag options. Default:ghcr.io/hubuum/hubuum-frontend:main.--postgres-image: managed Postgres image. Default:docker.io/library/postgres:18-alpine.--valkey-image: frontend session/cache Valkey image. Default:docker.io/valkey/valkey:9-alpine.--caddy-image: reverse proxy image. Default:docker.io/library/caddy:2-alpine.--network-subnet: container bridge subnet and backend client allowlist. Default:172.30.42.0/24.--shared-host-routing: required when--weband--apiare the same inallmode. Accepted values:bff,direct,prefixed.--systemd: install and enable a systemd service.--service-name: systemd service name. Default:hubuum.--no-systemd: skip systemd unit installation. This is the default.--script-base-url: base URL for downloading management scripts during curl-based installs.--script-ref: Git ref used to derive raw GitHub management script URLs.--build-from-source: clone repositories and build app images locally.--backend-ref: source build backend Git ref. Default:main.--frontend-ref: source build frontend Git ref. Default:main.--recreate: regenerate generated secrets. The managed Postgres password is preserved, because the existing database volume was initialized with it and rotating it would break authentication. To reset the database, uninstall with--purgefirst, then reinstall.--no-pull: skip pulling images before starting.
Managed PostgreSQL explicitly sets PGDATA=/var/lib/postgresql/data, matching
its named-volume mount. This preserves the existing layout when refreshing
configuration and avoids PostgreSQL 18's changed default data directory. It does
not perform PostgreSQL major-version upgrades; those still require migration.
The parent directory uses temporary storage to prevent PostgreSQL 18 from creating
an anonymous volume; database contents remain in the persistent named volume.
The restore executor has no HTTP listener, so its container health check measures
process liveness, not restore success or database readiness.
Mounted Secret Files¶
The installer generates environment-backed configuration by default. Native
binaries and custom container deployments can instead select file-backed
credentials through --secret-source file --secret-file-root DIRECTORY or
the corresponding HUBUUM_SECRET_SOURCE and HUBUUM_SECRET_FILE_ROOT variables.
Both the server and administrator support the same file layout and explicit
URL overrides. See the complete mounted-secret deployment example
and split-role mounts.
The installer does not provision secret files or mounts. Merely adding these
variables to a host .env file does not mount files or pass new variables into
an existing Compose service; configure each workload's container environment
and read-only mounts explicitly. Keep the migrator file isolated from API and
worker containers in split mode.
Generated Environment¶
Backend:
HUBUUM_DATABASE_URLandDATABASE_URL: Postgres connection URL.HUBUUM_BIND_IP=0.0.0.0HUBUUM_BIND_PORT: internal backend API listen port. Default:8080.HUBUUM_LOG_LEVEL=infoHUBUUM_TOKEN_HASH_KEY: generated stable token hash key.HUBUUM_REQUIRE_STABLE_TOKEN_HASH_KEY=true: prevents accidental ephemeral startup. The generated installation remains in compatible single-key mode; convert it with the staged procedure in Secret Sources when rotation is required.HUBUUM_CLIENT_ALLOWLIST: defaults to the container bridge subnet.HUBUUM_TRUST_IP_HEADERS=falseHUBUUM_TOKEN_LIFETIME_HOURS=24HUBUUM_MAX_TOKEN_LIFETIME_HOURS=8760HUBUUM_TOKEN_RETENTION_PURGE_ENABLED=trueHUBUUM_TOKEN_RETENTION_DAYS=30HUBUUM_TOKEN_RETENTION_PURGE_INTERVAL_SECONDS=3600HUBUUM_TOKEN_RETENTION_PURGE_BATCH_SIZE=1000HUBUUM_LOGIN_RATE_LIMIT_MAX_ATTEMPTS=5HUBUUM_LOGIN_RATE_LIMIT_WINDOW_SECONDS=300HUBUUM_LOGIN_RATE_LIMIT_BACKEND:valkeyin all-in-one mode andmemoryin backend-only mode.HUBUUM_LOGIN_RATE_LIMIT_VALKEY_URL=redis://valkey:6379/1HUBUUM_AUTH_CONFIG_HOST_PATH: absolute source path on the container host.HUBUUM_AUTH_CONFIG_PATH=/etc/hubuum/auth.toml: read-only path inside the API container.
Frontend, all-in-one mode only:
BACKEND_BASE_URL=http://caddy:8081: private readiness-aware API listener.VALKEY_URL=redis://valkey:6379/0SESSION_TTL_SECONDS=28800SESSION_PREFIX=hubuum:sess:NEXT_PUBLIC_APP_NAME="Hubuum Console"
The backend creates the initial admin user and group on first startup. The generated admin password is not logged; reset it with hubuum-admin in the API container after installation.