* Open WebUI: stop when the install fails three times
The retry loop fell through to "Installed Open WebUI" after the third
failure, so the service was created without a binary and the URL never
answered (#18020). The last attempt now runs under the error trap, which
shows uv's output.
* Open WebUI: keep the retry inline, no function in the script
* Open WebUI: same retry on the update paths, and drop the numba constraint
The migration and update loops in ct/openwebui.sh fell through to
"Updated" after three failures just like the install did. The numba
constraint from #11874 guarded openai-whisper's old pin; 0.12.x depends
on faster-whisper and never resolves numba.
* formatting
Since 20261018 Silo uses ~/.silo as its config directory and stops when that path is a file, which the version file named after the app was. Update moves an existing file to ~/.silo_s3.
dl.packager.io is frozen at 1.5.1, and its key only has a SHA1 self-signature, which apt's sqv rejects on Debian 13 since 2026-02-01. Upstream's installer moved to go.packager.io (InvenTree #12824), which carries 1.6.0 for Ubuntu 24.04/26.04 and Debian 13; update migrates installs on those releases and accepts Debian.
start-from-init ran in the background with its output thrown away, and the PID to kill was guessed from lsof after 60 seconds; when the process had already ended, kill got no PID and failed with exit 2. init and setup finish on their own, report their errors, and the service only starts once its user exists.
Installs since February 2025 use PostgreSQL and have no db.sqlite, so the update stopped at the backup (#17971). mkdir -p lets a rerun get past the backup folder a failed run left behind.
FlareSolverr hands LANG to Chrome as --accept-lang, which takes a language tag; with the container locale Cloudflare challenges looped unsolved (discussion #17987, FlareSolverr discussion #1717). Update adds the line to existing units.
composer update re-resolved every dependency instead of using the composer.lock the release ships, and Composer now blocks svg-sanitize 0.22.0 while resolving (#17955, Bubka/2FAuth#575). Upstream's own image only runs composer install, as does our update.
v5 reworks a lot and is still a prerelease; once it is marked stable, install and update would move to it untested. Filter on the v4. tag prefix until v5 is checked (discussion #17952).
Since 1.0.0-rc.1 the command asks for the Application URL when --url is missing. Behind $STD the question is invisible, so install and update hung until Enter was pressed (#17944).
16.9.0 replaced the Tanuki wrapper; a fresh server.sh install now creates onedev.server.service, so the install failed on 'Unit onedev.service not found' (#17943). Upgraded installs keep their onedev.service, so update uses whichever unit exists.
Only GET /api/init creates the scheduled jobs and starts the scheduler; upstream's image calls it after every start, the LXC never did (#17945). An ExecStartPost hook now waits for the app and Redis like app-start.sh, and update adds it to existing installs.
* Bytestash: modify backup process
Removed backup creation for /opt/bytestash/data and added CLEAN_INSTALL_KEEP variable to preserve data during installation.
* Increase default CPU and RAM values
LocalAI resolves meta backends from the GPU vendor it reads out of sysfs,
which is the host's card even with no driver installed in the container,
so llama-cpp became cuda12-llama-cpp and never started (#17908). Force
the default capability unless a CUDA, ROCm or Level Zero runtime is
present; update adds the pin to existing installs.
* Immich: restore geodata/countryInfo.txt on update and pin 3.3.1
Updates run between the 3.3.0 pin and #17823 wrote countryInfo.txt into
app/ instead of geodata/, so metadata extraction failed and new uploads
never got thumbnails. The download sat inside the release block, so a
plain update could not repair it until the next pin. 3.3.1 only bumps
version fields in the manifests.
* Immich: follow base-images for libheif like the other libraries
The hard pin from #16996 sat at 1.23.2 while upstream moved to 1.23.5.
IPP 4.0 lets visitors upload through a second, optional app in the same
repository. The addon now builds the whole workspace, so upload-app is
always ready, and can run it as immich-proxy-upload on port 3001: asked
on install (var_ipp_uploads), offered in the menu of an existing
install, rebuilt and restarted on update with its config.json kept, and
removed on uninstall. It stays off unless chosen.
The path refactor in #16487 deleted the wrapper instead of pointing it
at the new layout, while the documented way to create the first
superuser is still wanderer-pb superuser upsert. Calling pocketbase
directly without --dir would quietly start an empty database. Install
the wrapper again, and write it on every update so containers set up
since then get it too.
pnpm 12.10 refuses a frozen install when the lockfile names apps/web but
that directory has no package.json, which is how the runtime tree is
built, so the update to 2.8.0 failed. Filter to @tracearr/server... the
way the upstream Dockerfile does, and copy assets/logo.png, which the
e-mail and newsletter code reads.
v3 compiles PAPERLESS_CONSUMER_IGNORE_PATTERNS as regex, so a v2 glob
like "*.tmp" kills the consumer on start and "._*" ignores every file.
The update now rewrites such globs to anchored regex instead of only
warning.
The four units had no Restart=, so an OOM kill during a large mail
import left the webserver and task queue down. Install sets
Restart=on-failure and the update adds it to existing units.
Only the libopenjp2-7 runtime was installed, so meson found no OpenJPEG
and libvips came out without jp2kload. Immich lists .jp2 as supported
and the upstream image builds against libopenjp2-7-dev. Install the
headers, and rebuild libvips on update when it lacks jp2kload, the way
ImageMagick is rebuilt when its LibRaw define is missing.
* RustDesk Server: run the official server, make the web UI optional
lejianwen/rustdesk-server has had no commit since 2025-09-01 and is 45
commits behind upstream, missing the UDP reflection/amplification and
mio security fixes. hbbs, hbbr and utils now come from
rustdesk/rustdesk-server; the packages and units are the same, so
existing installs upgrade in place and keep their keys.
lejianwen/rustdesk-api is unmaintained as well and becomes an opt-in
prompt. When chosen it is configured with this server's IP and key
instead of its 192.168.1.66 defaults, and Debian gets an admin password
like Alpine did. ~/rustdesk.creds now always holds the server key, and
the API is only updated where it is installed.
* RustDesk Server: answer the web UI prompt with var_rustdesk_api
yes or no skips the question, so unattended installs can choose; unset still asks.
* RustDesk Server: write the creds file with heredocs
Matches how the other install scripts write their creds; the file is unchanged.
* Caddy: install the .deb from GitHub instead of the Cloudsmith repo
Cloudsmith answered 402 Payment Required for two days once Caddy's
bandwidth quota ran out, and upstream expects it to happen again
(caddyserver/dist#142). Each GitHub release carries the identical
package (same SHA256), so Debian installs it from there now. The update
drops the Cloudsmith source, seeds ~/.caddy from the installed package
so nothing is reinstalled, and keeps a customised Caddyfile.
* Remove outdated comment from caddy.sh
---------
Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com>
Thirty-six yes/no prompts for optional components in 26 install
scripts could only be answered at the terminal, so an unattended
install stopped at the first one. Each now takes var_<app>_<option>
first and still asks when it is unset. The four conditions that only
matched a single y now take yes as well, since that is what the website
sends.
The thirteen install scripts and two update paths that run an external
installer each carried the same warning and y/N read. The core helper
does the same and also takes var_external_installer, so unattended
installs can answer it. Needs community-scripts/core
feat/confirm-external-installer merged first.
* hermesagent: match the relaunched root gateway regardless of interpreter
Upstream now starts the gateway through its bundled python via runpy, so
the venv-path pattern from #17126 matched nothing and the root gateway
survived; its writes then raced chown -R and aborted the update.
* Clean up comments in hermesagent.sh
Removed comments regarding hermes update and matching patterns.
* Radicale: install the bcrypt and argon2 extras
The update replaces pyproject.toml and the venv, so a bcrypt or argon2
module added by hand was gone afterwards and Radicale refused to start
with htpasswd_encryption = bcrypt, argon2 or autodetect. The service now
runs uv with --extra bcrypt --extra argon2, which upstream already
defines. The update patches existing units and restarts the service.
* Radicale: enable the extras only once the code defines them
argon2 is an extra since v3.5.4 and uv refuses unknown extras, so patching
before the release update broke older installs. Patch after it instead,
and only when pyproject.toml defines the extra.
* docker: take the lxcfs toggle, TCP socket and Compose choice from app variables
var_docker_lxcfs is exported for the core helper; var_docker_tcp_socket
(no/local/all) and var_docker_compose replace the prompts when set, so
the website generator can offer them. Alpine wrote daemon.json through
silent(), which captured the output and left the file empty.
* docker: use var_docker_socket, the name the catalog record already carries
v2.0.0 reads the config strictly and stops at the v1 keys the addon
wrote (server.port, conditionals, indexingIntervalMinutes). It also only
imports the old BoltDB when server.database.migrateFrom names it and no
database.db is left in the working directory. The update now rewrites
the config, renames the database and points migrateFrom at it, keeping
a copy of the v1 config. New installs write the v2 layout.
* Immich: survive an interrupted update and a failing ML build
An update cancelled midway leaves /opt/immich/app empty, and the next run
died at once on cd /opt/immich/app/bin to enable maintenance mode (#17796).
Maintenance mode is now only toggled when immich-admin exists.
uv sync fetching the ml-models git dependency failed with "Could not resolve
host" behind DNS filters such as AdGuard while DNS itself worked (#17797);
running uv one download at a time got through. Retries now do that.
If machine learning still cannot be built, the update no longer stops with
maintenance mode on and every service down: it finishes, starts the web
service, puts the previous version back into ~/.immich so the next update
retries, and exits with an error. Updates are also announced as taking
5-15 minutes, since the first report came from cancelling one that looked
stuck.
* Immich: point immich-ml at the moved ml_start.sh and clear failed units
The update moves ml_start.sh into app/machine-learning, but only rewrote
immich-web's ExecStart, so older containers kept starting ML from
/opt/immich/ml_start.sh and failed with 203/EXEC. Rewrite immich-ml the same
way. A crash loop before the update can also leave both units rate-limited,
which makes the final restart fail; reset them first.
The build-debian tarballs of v2.3.0 carry the Alpine build of
better_sqlite3.node, so the DB migration dies with ERR_DLOPEN_FAILED on
libc.musl and Homarr no longer starts after an update (#17783,
homarr-labs/homarr#7097). When the bundled module is musl, swap in
better-sqlite3's own glibc prebuild for the running Node ABI. The update runs
the check outside the release check, so containers already on 2.3.0 are
repaired too; it does nothing once upstream ships a correct tarball.
pip3 installed into the system Python with PIP_BREAK_SYSTEM_PACKAGES and
stopped once a dependency wanted a newer idna than Debian's python3-idna,
which pip cannot uninstall (no RECORD file) (#17784). The analyzers now run
from /opt/kima-hub/venv on a uv-managed Python 3.13.
* Refactor: Ubuntu VM
Updated licensing information and improved error handling. Refactored OS selection logic and cloud-init prompts.
* Update license URL in ubuntu-vm.sh
* Immich: pin v3.3.0 again
Reapplies #17759, reverted in #17767 because machine learning broke on 3.3.0; the fixes follow.
* Immich: run machine learning on Python 3.13
3.3.0 requires Python >=3.12 for machine learning and upstream builds both its CPU and OpenVINO images on 3.13. The CPU path still pinned 3.11, so uv sync failed on every update and install (#17762).
* Immich: stop when uv sync keeps failing
After three failed attempts the loop still reported the machine-learning step as done, so an update ended in "Updated successfully" with no usable venv and immich-ml failing on start. Exit with an error instead.
* Immich: only mention the HEIC patch when it applies
On 3.3.0 the sharp call it rewrites is gone, so every run printed "pattern not found, skipped" into the spinner line and then "Patched". Check for the pattern first and stay silent otherwise.
* BookOrbit: retry the client build when the bundler crashes
Since rolldown 1.2.8, vite build dies at random with SIGSEGV or SIGBUS
(rolldown#10860, closed upstream), which stopped the 3.3.0 update with exit
139 (#17753). Retry the client build up to three times before giving up.
uv venv on an existing .venv asks before replacing it and refuses outright
without a terminal, so updates through update-apps.sh failed and left the
services stopped (#17734). --clear replaces it the way the interactive prompt
did.
* Refactor archlinux-vm.sh and update defaults
Refactor Arch Linux VM script to improve structure and update default settings.
* Update Arch Linux VM script to use dynamic ISO path
Caddy served all of /opt/webtrees through file_server, so media under
data/media could be fetched by URL without passing webtrees' privacy rules.
webtrees only protects data/ with an .htaccess, which Caddy ignores. Deny the
folders webtrees' own nginx guide keeps private, plus dotfiles.
update_script adds the rule to existing Caddyfiles on every update, not only
when a new release is out, and keeps the old file if the result fails
caddy validate.
0.21 requires POCKETBASE_PROXY_SECRET on both services, or every
incoming federation request is rejected; both read the same .env.
Each plugin archive holds one directory, which the deploy strips, so all
three landed flat in plugins/ and overwrote each other. wanderer only
loads direct child directories, so it found none. Existing installs are
moved over on the next update.
nginx passed X-Accel-Mapping on every request. Stylesheets are sent
from tmp/, outside that mapping, so Rack redirected nginx to their
filesystem path and the page loaded without CSS. Existing installs get
the line removed on update.
The update called yarn, which is not installed, ran Rails without the
production environment or rbenv on PATH, asked for PostgreSQL 16 on a
17 install and left sidekiq running.
Contribution docs, the PR template and the workflows that talk to the
testing repository use the new name. Migration PRs are matched by either
name, and three license headers pointed at contributor forks.
The URLs came from the ML Dockerfile on main, which upstream moved into
scripts/install-intel-runtime.sh, so update failed on the grep and
OpenVINO installs found no packages.
* Scripts: close every msg_info block with msg_ok
Past-tense msg_info calls that should have been msg_ok, notices that opened a block before a prompt, and blocks without a closing msg_ok. These already left a stale spinner; with core's block stack they would resume it after every later msg_ok.
* Generate passwords with random_password
openssl rand -base64 | tr -dc | head -c returned fewer characters than asked for, and the unfiltered | cut variants put / and + into passwords that end up in DSNs and sed expressions. Secrets an app decodes as base64 are unchanged. Requires community-scripts/core#61.
* Keep data directories through CLEAN_INSTALL instead of copying them
create_backup copied uploads, storage and similar directories twice per update and needed their size again in free space. CLEAN_INSTALL_KEEP moves them aside instead. Only directories the upstream release does not ship, in scripts that restored right after the fetch. Requires community-scripts/core#61.
* Drop the 300s uv timeout overrides
setup_uv exports UV_HTTP_TIMEOUT=600 now; the scripts' 300 only lowered it. Requires community-scripts/core#61.
* Use the release helpers instead of hand-rolled version checks and downloads
Legacy /opt/*_version.txt files move to ~/.<app> on the next update.
* homeassistant: use get_latest_github_release
Engine pull requests were only filtered by invalid/wontdo/wontfix/duplicate,
so every "Generate app headers" run from core landed in the changelog. They now
honour the same "automated pr" label as this repository's own bot PRs.
A fix to tools/addon/all-templates.sh got no comment because only ct/ and
install/ were recognised. Each script now also says where it runs, read from
what it calls: the Proxmox VE host, inside a guest, or PBS/PMG/PDM.
It called GitHub Models, which GitHub retired on 2026-07-30. The endpoint now
answers every request with a plain-text "OK", so each mention ended in "Could
not parse the model response". The /pocketbase slash-command bot is unaffected.
linkding stores them in data/favicons and data/previews and upstream maps both
under /static next to the collected assets, with a sandbox CSP. The nginx site
only aliased the collected assets, so every downloaded image answered 404; the
update rewrites that block even without a new release.
Upstream's nginx.conf gained a resolver ${NGINX_RESOLVER} line that its Docker
entrypoint fills from /etc/resolv.conf; the script substitutes a fixed list of
placeholders, so the literal variable reached nginx and the config test failed.
Take the nameservers from resolv.conf the same way.
On the run in which steamcmd replaces itself, the app_update that follows fails
with 'Missing configuration', and the same command succeeds once it runs again.
A login-only run first takes that self-update out of the install and update.
The noavx2 requirements pin numpy 1.23.5 and onnx 1.14.1, neither of which ships
a cp312 wheel, so on 3.12 uv built them from source: numpy fails without
distutils and onnx without cmake. Every noavx2 pin has a 3.11 wheel.
@lobehub/ui 5.50.0 dropped NeuralNetworkLoading, and LobeHub 2.2.18 still imports
it through a ^5.47.0 range with lockfile: false, so every fresh build now fails.
Upstream moved off it on main; the pin only applies while package.json still asks
for a 5.4x range, so it stops on its own with the next stable release.
The repo also publishes web-clipper-v* releases, and whenever upstream marked one
of those as latest the update offered to replace the server with the browser
extension. A v prefix makes core pick the newest stable server release itself
instead of trusting that marking.
2.16.0 ships production.conf only as a template that its s6 prepare step renders,
so installs outside Docker never listened on 81; render it on install, on update
while keeping a custom admin port, and on update for containers already stuck on
2.16.0. Certbot's upgrade died on a dist-info without RECORD, which pip's own
--ignore-installed hint does not clear, so drop such records before upgrading.
Upstream renamed OBSIDIAN_VERSION in its Dockerfile to IGNIS_OBSIDIAN_PIN, so the
grep matched nothing - and under pipefail the assignment itself aborts, which is why
the fallback on the next line never ran. The tag already carries the pairing
(0.8.13+obsidian.1.13.7), so read it there and keep both Dockerfile keys as a
fallback.
* Docker-LXC: remove Portainer installation from install
Removed Portainer installation prompts and related code.
* Remove Portainer installation instructions from docker.sh
Removed instructions for installing Portainer as an addon.
* scanopy: use prebuilt server binary, relax release profile for generate-fixtures
* scanopy: drop unneeded rust build, use committed UI fixtures
* scanopy: restore generate-fixtures build, ui/src/lib/data is gitignored and incomplete
* scanopy: serve the UI from the binary, drop the source build
Upstream confirmed in scanopy/scanopy#698 that scanopy-server-linux-*
has carried the built UI since v0.17.13, fixtures and service logos
included, served on the same port as the API. The release notes never
said so, which is why we kept building it.
That removes the source tarball, the Rust toolchain and
generate-fixtures, Node with npm ci and npm run build, and
SCANOPY_WEB_EXTERNAL_PATH. With the variable set to a directory that no
longer has an index.html, v0.17.14 and earlier refuse to start, so the
update deletes the line rather than leaving it. build-essential,
libssl-dev and pkg-config go too: the release binary is static-pie with
no INTERP segment, so it has no runtime library dependencies.
/opt/scanopy stays, now only for .env and oidc.toml, and the unit's
WorkingDirectory follows it out of the removed backend directory. Since
nothing wipes that directory any more, the config survives an update on
its own, which is the report upstream passed on of an update coming
back without SCANOPY_WEB_EXTERNAL_PATH and the server starting API-only.
The update keeps the running binary until the new one answers
/api/health and puts it back if it does not, so a bad release leaves a
working server instead of a stopped one.
check_for_gh_release now keys on scanopy-server, matching the version
file the binary deploy writes; the Scanopy key belonged to the tarball
that is gone. Existing containers run one extra update, then agree.
* Refactor scanopy-install.sh for server setup
Updated installation script to configure Scanopy server and removed daemon configuration section.
* Update service names from 'scanopy-server' to 'Scanopy'
* Fix case sensitivity in fetch_and_deploy_gh_release
* scanopy: make the rollback restore the whole old setup
The health check put the previous binary back but nothing else, and the
source tree it needs was already gone by then: the update deleted
/opt/scanopy/ui before starting the new server, and removed
SCANOPY_WEB_EXTERNAL_PATH from the env at the same time. A failed
health check therefore left the old binary running without the UI it
serves from disk, so the rollback produced an API-only server.
Back up the env file and the unit alongside the binary, restore all
three when the check fails, and delete the source tree only once the
new server has answered. Nothing the old version needs is removed
before the new one has proven itself.
* scanopy: name the deployed binary what the unit starts
singlefile mode writes the asset to <target>/<app name>:
local target_file="$app"
[[ "${USE_ORIGINAL_FILENAME:-false}" == "true" ]] && target_file="$filename"
so with the app renamed to Scanopy the binary lands at /usr/bin/Scanopy
while the unit starts /usr/bin/scanopy-server, and the service never
comes up on a fresh install. Rename it after the deploy, the same way
the daemon block already renames "Scanopy Daemon".
Keeping the app name is what matters here: it is also the version file
(~/.scanopy), and changing it would make every existing container
report an update it does not need.
* immich: keep geodata linked and the build deps present on update
The update wipes $APP_DIR before redeploying, which takes the geodata
symlink the install created with it, and never puts it back. The app
then finds no reverse-geocoding data and the web UI does not come up.
The library recompile assumes headers that only reached the install
list later, so a container built before that fails at the first missing
one - LCMS2 in the reported case.
* immich: keep geodata linked and the build deps present on update
The update wipes $APP_DIR before redeploying, which takes the geodata
symlink the install created with it, and never puts it back. The app
then finds no reverse-geocoding data and the web UI does not come up.
The library recompile assumes headers that only reached the install
list later, so a container built before that fails at the first missing
one - LCMS2 in the reported case.
* Let uv see the project before syncing it
uv refuses to run when a project pins a required-version it does not
match, in either direction: RomM pins ==0.12.13, which fails against
both the 0.10.3 a container was built with and the 0.12.17 latest
installs.
UV_PROJECT_DIR points setup_uv at the project so it reads that pin. It
is a prefix like PYTHON_VERSION and UV_VERSION, and the call sits
directly under fetch_and_deploy: the project is on disk by then, and
the deploy has closed its message block, which setup_uv needs since it
opens one of its own. That is also the only call needed - nothing
between the old early call and the deploy uses uv or Python, so the two
collapse into one.
Two things found along the way:
UV_PYTHON was set as a command prefix on setup_uv in 14 places. setup_uv
reads PYTHON_VERSION, never UV_PYTHON, and a prefix assignment does not
outlive the call, so those pins did nothing. They now use
PYTHON_VERSION, which installs the interpreter they were asking for.
Five update scripts had no setup_uv at all while their install
counterpart pinned a Python version. They now carry the same pin.
immich is left out: it runs uv through sudo -u inside a retry loop.
* yubal: drop the uv 0.7.19 pin
The pin came in with the script and was never explained. uv 0.7.19 is
from 2025-07-02; yubal's uv.lock has carried revision 3 since at least
2025-12-27, and older uv refuses a newer lockfile revision. The script
runs uv sync --frozen, so there is no fallback.
yubal declares no required-version of its own, so latest is what it
gets - and if it ever pins one, that pin is now honoured.