Noust 3.3 changelog
What changed in 3.3.
Changes since Noust 3.2.1. Upgrade notes are in UPGRADING-3.3.md; the databases area is described in databases.md.
3.3 shows the databases a server really has. Until 3.2 the Databases area only saw engines installed on the host, so a database in a Docker container (most of them, on the servers this release was measured against) did not exist for Noust; an engine that refused Noust's account was shown as an engine with nothing in it; and an application was linked to a database only if Noust had written its connection string. None of that changes anything on the server by itself: what is new is what Noust sees and says, and the actions it offers.
Databases in containers (item 67)
- Instances. A database server is now either the host's engine (as before) or a container
Noust finds on its own: the official images of PostgreSQL, MySQL, MariaDB, Redis, Valkey and
MongoDB and their common derivatives (PostGIS, TimescaleDB, pgvector, Bitnami). Each one is
named by a key that stays the same when the container is recreated:
postgresql@<project>.<service>for a Compose service,postgresql@<container>otherwise. - The same code as the host. Listing databases, users and tables, sizes, the data browser,
read-only queries, dumps, restores, backup policies and links all work on an instance: the
engine's own client runs inside the container through
docker exec. The container's passwords are read inside the container and never reach the host's process list; the ones Noust holds itself travel in the environment of thedockerprocess. - Whose it is. A container's databases belong to the application its Compose project is (the project label against the application's project or name), so a stack's Postgres and Redis show as its application's.
- What differs. Start, stop and restart act on the container. Installing, uninstalling and configuring do not exist for an instance: the image decides the engine, and Noust says so. When only the application's own account is available (no root password in the container's environment), the instance is marked as limited access.
- Every
--engineoption ofnoust dbtakes an instance key, andnoust db engineslists the containers after the host's engines.
An engine that does not let Noust in says so (item 68)
- A listing that failed used to come back empty: a MySQL that asked root for a password showed as a server with no databases and no users, and its tracked databases as gone. Now the engine's own answer is shown, verbatim, with the fix above it, and the tracked databases of an engine that could not be read are marked "not verified" instead of missing.
- For MySQL/MariaDB and Redis the fix is in the console: store the account Noust signs in with
(
PUT /api/databases/engines/{engine}/credentials, sudo mode, audited asdb.credentials.set). The engine is asked first, and nothing is saved if it refuses.noust db configgoes through the same check, and--passwordalone asks for the password without echo, so it never reaches the shell history or the process list. - PostgreSQL and MongoDB do not sign in with a stored password; their message says what Noust
needs instead (peer authentication for
postgres, the administrator Noust created).
Links that already exist (item 69)
- Noust reads each application's environment and finds the databases it names: connection URLs
(
DATABASE_URL,REDIS_URL, Prisma, Laravel, JDBC, driver suffixes) and theDB_*andREDIS_*sets. A reference resolves to one engine: by port on this machine (a container's published port included), by service name inside the application's Compose project, never by guessing between two. Nothing of the application runs, no password is kept or shown, and a.envthat leads outside the application's tree is not read. - The databases list shows those applications as detected beside the database. "Record the
link" makes one a link without touching the application (
POST .../links/detected, audited asdb.link.record), one at a time and only when the operator asks: an application's.envis written by whoever deploys it, so what it names is a claim. A database another application owns is refused, an existing link is never overwritten, and the recorded link carries no account, so a later password rotation never writes the new password into an application that merely named the account.noust db adopttracks databases and records no links.
Counts (item 70)
MySQL counts tables in its listing; Redis reports keys rather than tables; a count the listing does not know is shown as unknown, never as 0.
Engines: versions and settings (item 71)
- Choose what to install. PostgreSQL 14 to 18 (from the PostgreSQL project's repository, or
the distribution's when it is the one asked for), MariaDB 10.11, 11.4 or 11.8 (from MariaDB's
repository) or the distribution's, MySQL from the distribution, Redis or Valkey, MongoDB 7.0
or 8.0, as far as each upstream publishes for the server's release. Every upstream signing key
is pinned by fingerprint and checked before apt trusts it, MongoDB's included. MySQL and MariaDB,
and Redis and Valkey, cannot be installed beside each other, a check the install itself makes.
Only the pinned primary key is written to apt's keyring (exported from a throwaway keyring, so
a file that also carries another key or a secret key is refused); a leftover source for another
release (
mongodb-org-7.0.listunder an 8.0 install) is refused with the file to remove; a source Noust added is removed again whenapt-get updatethen fails;apt-get installruns with--no-remove; and the version that ends up running is compared with the one asked for. Installing needs sudo mode in the API, as removing does.GET /api/databases/engines/catalogandnoust db catalogsay what this server can install;noust db install postgresql --version 17. - Settings. A closed set per engine (connections, memory, listen addresses, slow-query
logging, time zone...) with the current value, a recommendation for this server's memory and
cores, and whether it needs a restart:
noust db settings <engine> [KEY=VALUE ...], the console's Settings page,GET/PUT /api/databases/engines/{engine}/settings(sudo mode, audited asdb.settings.change). Values go into Noust's own include file, never the distribution's; each change is checked with the engine's own tool, applied (reload, restart or at runtime, as the setting needs), and put back with the engine's journal in the error if the engine does not come back. Opening an engine beyond the loopback asks for confirmation, and theens-mediumprofile refuses it; removing a listen setting counts as opening it unless what the engine falls back to is known to be the loopback, and the live value is read back after the change. A Redismaxmemorybelow what it already uses,appendonly noandsave offask for confirmation too (confirmin the API, which answersconfirmation_requiredwith every warning;confirm_exposureis still honoured), memory settings are capped at 90% of the RAM, and Redis writes a snapshot before any restart Noust makes. Changes to one engine are serialised, a symlinked configuration file or directory is refused, every PostgreSQL step targets the same cluster (several online clusters are refused), a slow start that is still loading is waited for, and the previous files are put back whatever interrupts the change.
Exposure without false alarms (item 72)
- A port Docker publishes that the
DOCKER-USERchain refuses on the public interface is shown as closed by the firewall, not exposed; an IPv6 publication on a server without an IPv6 route likewise. The database exposure check reads the chain with the same code as the server's security check. - An image that only mentions a database in its name (
zabbix/zabbix-server-mysql) is no longer taken for one: there is one reader of images, which compares the repository name. A container of an image that reader does not know (a registry other than Docker Hub, an image id,supabase/postgres) is still reported when it publishes a database's own port. - A rule in
DOCKER-USERthat jumps to another chain before the drop leaves the port exposed: where the packet goes is not proven closed.
Compose stacks running outside their unit (item 73)
A Compose application whose unit is stopped while its containers run (started by hand with
docker compose up -d) used to show as stopped and raise "the unit is not running" while it
served. It is now its own state, "Running outside Noust" (amber), in noust list, the console,
the overview and the fleet summary; the monitor says once (app.outside_unit) that a reboot would
not bring it back. noust app reclaim <domain> (and the button on the application's page,
POST /api/apps/{domain}/reclaim, sudo mode, audited as apps.reclaim) enables and starts the
unit after the same docker compose up --dry-run rehearsal adoption uses; if that rehearsal would
recreate a container it refuses unless --accept-recreate is given.
Fixes
- A Redis container counts as installed even when the host has no
redis-cli. - A container's MongoDB archive is restored into the database asked for: the archive says which
database it holds and
mongorestorerenames it (--nsFrom/--nsTo), so restoring "as a new database" or testing a dump never touches the original. Container archives pass verification. - A container's log masks the passwords in it (generated root passwords, the container's secret
variables, passwords in logged statements) and needs
databases.manage. - One
.envthat is not UTF-8 no longer breaks the databases list; a.envis opened once, without following links, and at most 1 MiB of it is read. - Changing only the password of an engine's stored account is tested with the stored user, as it is saved.
3.3.1
- Sudo mode lasts while you work. Confirming it's you opens sudo mode for 15 minutes, and
every action that needs it extends that again, up to 2 hours from the confirmation and never
past the session. An account with a second factor confirms with one: a code from its
authenticator, a backup code or a passkey; the password is not asked again. The windows and the
password are settings (
auth.sudo.idle_minutes,auth.sudo.max_minutes,auth.sudo.require_password); theens-mediumprofile keeps the password and at most 10 and 30 minutes. The confirmation dialog says how long it lasts. - A pending SSH or firewall change says whether it is proven. The banner watches for the new
SSH login that proves the way in still works, names it when it arrives (who, from where, when)
and only then offers Keep; before, Keep answered "No new SSH login since the change". Undoing no
longer shows that error left over from an earlier Keep. The window before a change undoes
itself is 5 minutes instead of 2.
noust server security pendingshows the same. - Refusing root logins works for Ubuntu and Debian administrators (issue #14). A sudoers rule
separated by a tab, which is how Debian and Ubuntu ship
%sudo, was not read, so a member ofsudowas "an account that cannot use sudo" and the fix was refused. Sudoers is now read as sudo reads it (aliases, host and runas lists, negation,Defaultsscopes, includes); a rule that does not run every command as root no longer counts as a way to root. - Install hints name this server's package manager (issue #12):
apton Debian and Ubuntu,dnfon Fedora and RHEL,zypperon openSUSE, all three only when Noust cannot tell. - Every "how to fix" names a command that exists (issue #13). The failed-units finding pointed
at
noust server units --failed; it now opens the services list filtered to the failed units (noust service list --all --state failed,GET /api/services?state=failed). A test checks every command and route Noust suggests; it found and fixed ten more. noust web startwithout root says so (issue #11) instead of ending in a traceback.- The engines page no longer contradicts itself. The server's own engines are now listed under
"On this server" when engines in containers are listed below them, and a server engine that is
not installed but runs in a container says so beside its state ("Not installed", "1 in a
container").
GET /api/databases/enginesreports each engine'sengine_type, so a client never splits an instance key. - A container's missing password or authorization is only a warning when something can reach
it. A Redis or MongoDB in a container is judged by what Docker publishes of its port: on its
Docker networks only it is the image's default and is not reported; published on the server's
loopback it names the address and says any process on the server can reach it; published on any
other address it says anything that reaches that port can. MongoDB containers are now asked
whether they enforce authorization, and their fix is the compose file's
(
MONGO_INITDB_ROOT_USERNAMEandMONGO_INITDB_ROOT_PASSWORD, which take effect on an empty data volume) rather than/etc/mongod.conf. The servers' own Redis and MongoDB keep their warnings. - Every page of the console shows that it is loading. The Databases list drew its header over a blank area until the list, the backup policies and the open ports had all answered; it now keeps its filters and a table of placeholder rows from the first frame, and says nothing about the databases (no "No databases yet", no count) before it knows them. The rows wait at most a second for the notice above them: the open-port check can take seconds and no longer holds them back. The same gap is closed on a service's page, the unit file, the table of tables, the connection tab, the logs (a pane still waiting for its first lines draws the shape of lines and is marked busy), the servers count of Settings > Central, and on the figures, cards and sections that were placeholders a screen reader could not tell from content. A route that keeps the console waiting more than a second (the session, a page's code) now shows a skeleton in the page's place instead of leaving the previous page, or nothing, on screen. A test opens every route of the console against a server that never answers and refuses a page that shows nothing, a placeholder a screen reader cannot hear, or an empty state, so a new page cannot ship without its loading state.