Compare commits

...

27 Commits

Author SHA1 Message Date
CanbiZ (MickLesk)
29aa2ff170 Use npm install --no-audit --progress=false
Replace npm install --legacy-peer-deps with npm install --no-audit --progress=false in frontend build scripts (ct/dispatcharr.sh and install/dispatcharr-install.sh). This disables npm audit and progress output during automated builds while keeping the subsequent npm run build step unchanged.
2026-03-19 07:05:12 +01:00
community-scripts-pr-app[bot]
2a7b6c8d86 Update CHANGELOG.md (#13073)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-19 05:56:22 +00:00
CanbiZ (MickLesk)
ba01175bc6 core: reorder hwaccel setup and adjust GPU group usermod (#13072)
* fix(tdarr): use curl_with_retry and verify binaries before enabling service

Tdarr_Updater downloads the actual server/node binaries from tdarr.io at
runtime. If tdarr.io is blocked by local DNS (e.g. OPNsense OISD blocklists),
the updater exits silently with code 0, leaving no binaries on disk. The
subsequent systemctl enable then fails with 'Operation not permitted' (exit 1)
because the ExecStart paths don't exist.

Changes:
- Replace bare curl with curl_with_retry for versions.json and Tdarr_Updater.zip
  downloads to gain retry logic, DNS pre-check and exponential backoff
- Add msg_info before Tdarr_Updater run so users see this step in the log
- Check that Tdarr_Server and Tdarr_Node binaries exist after the updater
  runs; fail immediately with a clear message pointing to tdarr.io connectivity
  instead of letting systemctl fail with a confusing 'Operation not permitted'

Fixes: #13030

* Improve Tdarr installer error handling

Refine post-update validation and failure behavior in tdarr-install.sh: remove a redundant status message, simplify the updater check to only require the Tdarr_Server binary, and replace the previous fatal path with msg_error plus an explicit exit 250. This makes failures (for example when tdarr.io is blocked by local DNS) clearer and avoids false negatives from the Tdarr_Node existence check.

* Use curl_with_retry and handle updater failure

Replace direct curl calls with curl_with_retry for fetching versions.json and downloading Tdarr_Updater.zip to improve network reliability. Add a post-update check that verifies /opt/tdarr/Tdarr_Server/Tdarr_Server exists; if missing, log an error suggesting possible DNS blocking and exit with code 250. Minor cleanup of updater artifacts remains unchanged.

* Reorder hwaccel setup and adjust GPU group usermod

Move setup_hwaccel invocations in emby, jellyfin, ollama, and plex installers to occur after package installation/configuration so GPU drivers/repos are present before enabling hardware acceleration. Update _setup_gpu_permissions to call usermod directly (remove $STD wrapper) when adding service users to render/video groups. Includes minor whitespace/ordering cleanups in the installer scripts.
2026-03-19 06:55:56 +01:00
community-scripts-pr-app[bot]
607eff0939 Update .app files (#13064)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-03-18 21:46:04 +01:00
community-scripts-pr-app[bot]
df51f7114e Update CHANGELOG.md (#13063)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-18 20:41:28 +00:00
CanbiZ (MickLesk)
79805f5f3d Alpine-Ntfy (#13048) 2026-03-18 21:41:02 +01:00
community-scripts-pr-app[bot]
59c601e0e2 Update CHANGELOG.md (#13061)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-18 19:59:24 +00:00
CanbiZ (MickLesk)
7c467bee7b Tdarr: use curl_with_retry and correct exit code (#13060) 2026-03-18 20:58:58 +01:00
CanbiZ (MickLesk)
d2e5991416 qf start service (podman) 2026-03-18 18:52:07 +01:00
community-scripts-pr-app[bot]
73e6d4b855 Update CHANGELOG.md (#13059)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-18 17:37:14 +00:00
community-scripts-pr-app[bot]
a53a851cc9 Update CHANGELOG.md (#13058)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-18 17:35:58 +00:00
Tobias
2001a43229 reitti: fix: v4 (#13039) 2026-03-18 18:35:43 +01:00
community-scripts-pr-app[bot]
d35249b8f4 Update CHANGELOG.md (#13057)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-18 17:35:33 +00:00
CanbiZ (MickLesk)
9f73b6756e refactor(podman): replace deprecated commands with Quadlets (#13052) 2026-03-18 18:35:01 +01:00
CanbiZ (MickLesk)
192e2950e7 chore: fix pocketbase workflows 2026-03-18 16:57:20 +01:00
community-scripts-pr-app[bot]
20c4657f39 Update CHANGELOG.md (#13054)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-18 15:45:45 +00:00
CanbiZ (MickLesk)
e20fed1a2d tools.func Implement pg_cron setup for setup_postgresql (#13053)
* tools.func Implement PostgreSQL setup and upgrade function

Added setup_postgresql function to install or upgrade PostgreSQL, including optional modules and backup restoration.

* correct diff

* Update tools.func

* Update tools.func

* Update tools.func

* Update tools.func
2026-03-18 16:45:11 +01:00
community-scripts-pr-app[bot]
8b4f0f60e1 Update CHANGELOG.md (#13047)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-18 10:57:01 +00:00
CanbiZ (MickLesk)
bd91c4d07f tools: Centralize GPU group setup via setup_hwaccel (#13044)
improve hardware-acceleration setup to centralize service user group management. Install scripts (emby, plex, ollama, channels) now pass a service user to setup_hwaccel (or no user for channels) and have had inline /etc/group sed/usermod tweaks removed. misc/tools.func updated: setup_hwaccel accepts an optional service_user and forwards it to _setup_gpu_permissions, which now adds the service user to render and video groups if provided. This consolidates GPU permission changes in one place and removes duplicated per-service group edits.
2026-03-18 11:56:32 +01:00
community-scripts-pr-app[bot]
6bb264491c Update CHANGELOG.md (#13046)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-18 10:56:01 +00:00
CanbiZ (MickLesk)
852e557b1b Refactor: Jellyfin repo, ffmpeg package and symlinks (#13045)
Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com>
2026-03-18 11:55:38 +01:00
CanbiZ (MickLesk)
c5caca97fd Update termix.sh 2026-03-18 11:55:32 +01:00
CanbiZ (MickLesk)
dff876fb5c termix: migrate to .env 2026-03-18 11:50:41 +01:00
CanbiZ (MickLesk)
02e7e5af8d fix guacd service 2026-03-18 11:39:41 +01:00
CanbiZ (MickLesk)
0f4bfc0b5a add missing func? 2026-03-18 11:15:02 +01:00
community-scripts-pr-app[bot]
152be10741 Update CHANGELOG.md (#13043)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-18 09:54:04 +00:00
CanbiZ (MickLesk)
341489ea3f Termix: add guacd build and systemd integration (#12999) 2026-03-18 10:53:34 +01:00
25 changed files with 608 additions and 141 deletions

View File

@@ -75,7 +75,8 @@ jobs:
const http = require('http');
const url = require('url');
function request(fullUrl, opts) {
function request(fullUrl, opts, redirectCount) {
redirectCount = redirectCount || 0;
return new Promise(function(resolve, reject) {
const u = url.parse(fullUrl);
const isHttps = u.protocol === 'https:';
@@ -90,6 +91,13 @@ jobs:
if (body) options.headers['Content-Length'] = Buffer.byteLength(body);
const lib = isHttps ? https : http;
const req = lib.request(options, function(res) {
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
if (redirectCount >= 5) return reject(new Error('Too many redirects from ' + fullUrl));
const redirectUrl = url.resolve(fullUrl, res.headers.location);
res.resume();
resolve(request(redirectUrl, opts, redirectCount + 1));
return;
}
let data = '';
res.on('data', function(chunk) { data += chunk; });
res.on('end', function() {

View File

@@ -48,7 +48,8 @@ jobs:
const https = require('https');
const http = require('http');
const url = require('url');
function request(fullUrl, opts) {
function request(fullUrl, opts, redirectCount) {
redirectCount = redirectCount || 0;
return new Promise(function(resolve, reject) {
const u = url.parse(fullUrl);
const isHttps = u.protocol === 'https:';
@@ -63,6 +64,13 @@ jobs:
if (body) options.headers['Content-Length'] = Buffer.byteLength(body);
const lib = isHttps ? https : http;
const req = lib.request(options, function(res) {
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
if (redirectCount >= 5) return reject(new Error('Too many redirects from ' + fullUrl));
const redirectUrl = url.resolve(fullUrl, res.headers.location);
res.resume();
resolve(request(redirectUrl, opts, redirectCount + 1));
return;
}
let data = '';
res.on('data', function(chunk) { data += chunk; });
res.on('end', function() {
@@ -125,15 +133,15 @@ jobs:
var osVersionToId = {};
try {
const res = await request(apiBase + '/collections/z_ref_note_types/records?perPage=500', { headers: { 'Authorization': token } });
if (res.ok) JSON.parse(res.body).items?.forEach(function(item) { if (item.type != null) noteTypeToId[item.type] = item.id; });
if (res.ok) JSON.parse(res.body).items?.forEach(function(item) { if (item.type != null) { noteTypeToId[item.type] = item.id; noteTypeToId[item.type.toLowerCase()] = item.id; } });
} catch (e) { console.warn('z_ref_note_types:', e.message); }
try {
const res = await request(apiBase + '/collections/z_ref_install_method_types/records?perPage=500', { headers: { 'Authorization': token } });
if (res.ok) JSON.parse(res.body).items?.forEach(function(item) { if (item.type != null) installMethodTypeToId[item.type] = item.id; });
if (res.ok) JSON.parse(res.body).items?.forEach(function(item) { if (item.type != null) { installMethodTypeToId[item.type] = item.id; installMethodTypeToId[item.type.toLowerCase()] = item.id; } });
} catch (e) { console.warn('z_ref_install_method_types:', e.message); }
try {
const res = await request(apiBase + '/collections/z_ref_os/records?perPage=500', { headers: { 'Authorization': token } });
if (res.ok) JSON.parse(res.body).items?.forEach(function(item) { if (item.os != null) osToId[item.os] = item.id; });
if (res.ok) JSON.parse(res.body).items?.forEach(function(item) { if (item.os != null) { osToId[item.os] = item.id; osToId[item.os.toLowerCase()] = item.id; } });
} catch (e) { console.warn('z_ref_os:', e.message); }
try {
const res = await request(apiBase + '/collections/z_ref_os_version/records?perPage=500&expand=os', { headers: { 'Authorization': token } });
@@ -154,7 +162,7 @@ jobs:
name: data.name,
slug: data.slug,
script_created: data.date_created || data.script_created,
script_updated: data.date_created || data.script_updated,
script_updated: new Date().toISOString().split('T')[0],
updateable: data.updateable,
privileged: data.privileged,
port: data.interface_port != null ? data.interface_port : data.port,
@@ -163,8 +171,8 @@ jobs:
logo: data.logo,
description: data.description,
config_path: data.config_path,
default_user: (data.default_credentials && data.default_credentials.username) || data.default_user,
default_passwd: (data.default_credentials && data.default_credentials.password) || data.default_passwd,
default_user: (data.default_credentials && data.default_credentials.username) || data.default_user || null,
default_passwd: (data.default_credentials && data.default_credentials.password) || data.default_passwd || null,
is_dev: false
};
var resolvedType = typeValueToId[data.type];
@@ -190,7 +198,7 @@ jobs:
var postRes = await request(notesCollUrl, {
method: 'POST',
headers: { 'Authorization': token, 'Content-Type': 'application/json' },
body: JSON.stringify({ text: note.text || '', type: typeId })
body: JSON.stringify({ text: note.text || '', type: typeId, script: scriptId })
});
if (postRes.ok) noteIds.push(JSON.parse(postRes.body).id);
}

View File

@@ -83,7 +83,8 @@ jobs:
const http = require('http');
const url = require('url');
function request(fullUrl, opts) {
function request(fullUrl, opts, redirectCount) {
redirectCount = redirectCount || 0;
return new Promise(function(resolve, reject) {
const u = url.parse(fullUrl);
const isHttps = u.protocol === 'https:';
@@ -98,6 +99,13 @@ jobs:
if (body) options.headers['Content-Length'] = Buffer.byteLength(body);
const lib = isHttps ? https : http;
const req = lib.request(options, function(res) {
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
if (redirectCount >= 5) return reject(new Error('Too many redirects from ' + fullUrl));
const redirectUrl = url.resolve(fullUrl, res.headers.location);
res.resume();
resolve(request(redirectUrl, opts, redirectCount + 1));
return;
}
let data = '';
res.on('data', function(chunk) { data += chunk; });
res.on('end', function() {
@@ -151,7 +159,7 @@ jobs:
method: 'PATCH',
headers: { 'Authorization': token, 'Content-Type': 'application/json' },
body: JSON.stringify({
name: record.name || record.slug,
script_updated: new Date().toISOString().split('T')[0],
last_update_commit: process.env.PR_URL || process.env.COMMIT_URL || ''
})
});

View File

@@ -423,27 +423,46 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details>
## 2026-03-18
### 🆕 New Scripts
- split-pro ([#12975](https://github.com/community-scripts/ProxmoxVE/pull/12975))
## 2026-03-19
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- core: reorder hwaccel setup and adjust GPU group usermod [@MickLesk](https://github.com/MickLesk) ([#13072](https://github.com/community-scripts/ProxmoxVE/pull/13072))
## 2026-03-18
### 🆕 New Scripts
- Alpine-Ntfy [@MickLesk](https://github.com/MickLesk) ([#13048](https://github.com/community-scripts/ProxmoxVE/pull/13048))
- Split-Pro ([#12975](https://github.com/community-scripts/ProxmoxVE/pull/12975))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Tdarr: use curl_with_retry and correct exit code [@MickLesk](https://github.com/MickLesk) ([#13060](https://github.com/community-scripts/ProxmoxVE/pull/13060))
- reitti: fix: v4 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#13039](https://github.com/community-scripts/ProxmoxVE/pull/13039))
- Paperless-NGX: increase default RAM to 3GB [@MickLesk](https://github.com/MickLesk) ([#13018](https://github.com/community-scripts/ProxmoxVE/pull/13018))
- Plex: restart service after update to apply new version [@MickLesk](https://github.com/MickLesk) ([#13017](https://github.com/community-scripts/ProxmoxVE/pull/13017))
- #### ✨ New Features
- tools: centralize GPU group setup via setup_hwaccel [@MickLesk](https://github.com/MickLesk) ([#13044](https://github.com/community-scripts/ProxmoxVE/pull/13044))
- Termix: add guacd build and systemd integration [@MickLesk](https://github.com/MickLesk) ([#12999](https://github.com/community-scripts/ProxmoxVE/pull/12999))
- #### 🔧 Refactor
- Podman: replace deprecated commands with Quadlets [@MickLesk](https://github.com/MickLesk) ([#13052](https://github.com/community-scripts/ProxmoxVE/pull/13052))
- Refactor: Jellyfin repo, ffmpeg package and symlinks [@MickLesk](https://github.com/MickLesk) ([#13045](https://github.com/community-scripts/ProxmoxVE/pull/13045))
- pve-scripts-local: Increase default disk size from 4GB to 10GB [@MickLesk](https://github.com/MickLesk) ([#13009](https://github.com/community-scripts/ProxmoxVE/pull/13009))
### 💾 Core
- #### ✨ New Features
- tools.func Implement pg_cron setup for setup_postgresql [@MickLesk](https://github.com/MickLesk) ([#13053](https://github.com/community-scripts/ProxmoxVE/pull/13053))
- tools.func: Implement check_for_gh_tag function [@MickLesk](https://github.com/MickLesk) ([#12998](https://github.com/community-scripts/ProxmoxVE/pull/12998))
- tools.func: Implement fetch_and_deploy_gh_tag function [@MickLesk](https://github.com/MickLesk) ([#13000](https://github.com/community-scripts/ProxmoxVE/pull/13000))

50
ct/alpine-ntfy.sh Normal file
View File

@@ -0,0 +1,50 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: cobalt (cobaltgit)
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
# Source: https://ntfy.sh/
APP="Alpine-ntfy"
var_tags="${var_tags:-notification}"
var_cpu="${var_cpu:-1}"
var_ram="${var_ram:-256}"
var_disk="${var_disk:-2}"
var_os="${var_os:-alpine}"
var_version="${var_version:-3.23}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /etc/ntfy ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
msg_info "Updating ntfy LXC"
$STD apk -U upgrade
setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy
msg_ok "Updated ntfy LXC"
msg_info "Restarting ntfy"
rc-service ntfy restart
msg_ok "Restarted ntfy"
msg_ok "Updated successfully!"
exit
}
start
build_container
description
msg_ok "Completed successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}"

View File

@@ -110,7 +110,7 @@ function update_script() {
msg_info "Building Frontend"
cd /opt/dispatcharr/frontend
$STD npm install --legacy-peer-deps
$STD npm install --no-audit --progress=false
$STD npm run build
msg_ok "Built Frontend"

6
ct/headers/alpine-ntfy Normal file
View File

@@ -0,0 +1,6 @@
___ __ _ __ ____
/ | / /___ (_)___ ___ ____ / /_/ __/_ __
/ /| | / / __ \/ / __ \/ _ \______/ __ \/ __/ /_/ / / /
/ ___ |/ / /_/ / / / / / __/_____/ / / / /_/ __/ /_/ /
/_/ |_/_/ .___/_/_/ /_/\___/ /_/ /_/\__/_/ \__, /
/_/ /____/

View File

@@ -39,14 +39,23 @@ function update_script() {
msg_ok "Updated Intel Dependencies"
fi
msg_info "Setting up Jellyfin Repository"
setup_deb822_repo \
"jellyfin" \
"https://repo.jellyfin.org/jellyfin_team.gpg.key" \
"https://repo.jellyfin.org/$(get_os_info id)" \
"$(get_os_info codename)"
msg_ok "Set up Jellyfin Repository"
msg_info "Updating Jellyfin"
ensure_dependencies libjemalloc2
if [[ ! -f /usr/lib/libjemalloc.so ]]; then
ln -sf /usr/lib/x86_64-linux-gnu/libjemalloc.so.2 /usr/lib/libjemalloc.so
fi
$STD apt update
$STD apt -y upgrade
$STD apt -y --with-new-pkgs upgrade jellyfin jellyfin-server
$STD apt -y --with-new-pkgs upgrade jellyfin jellyfin-server jellyfin-ffmpeg7
ln -sf /usr/lib/jellyfin-ffmpeg/ffmpeg /usr/bin/ffmpeg
ln -sf /usr/lib/jellyfin-ffmpeg/ffprobe /usr/bin/ffprobe
msg_ok "Updated Jellyfin"
msg_ok "Updated successfully!"
exit

View File

@@ -23,7 +23,7 @@ function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -f /etc/systemd/system/homeassistant.service ]]; then
if [[ ! -f /etc/containers/systemd/homeassistant.container ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi

View File

@@ -89,17 +89,49 @@ EOF
msg_ok "Started Service"
msg_ok "Updated successfully!"
fi
if check_for_gh_release "photon" "komoot/photon"; then
if [[ -f "$HOME/.photon" ]] && [[ "$(cat "$HOME/.photon")" == 0.7 ]]; then
CURRENT_VERSION="$(<"$HOME/.photon")"
echo
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Photon v1 upgrade detected (breaking change)"
echo
echo "Your current version: $CURRENT_VERSION"
echo
echo "Photon v1 requires a manual migration before updating."
echo
echo "You need to:"
echo " 1. Remove existing geocoding data (not actual reitti data):"
echo " rm -rf /opt/photon_data"
echo
echo " 2. Follow the inial setup guide again:"
echo " https://github.com/community-scripts/ProxmoxVE/discussions/8737"
echo
echo " 3. Re-download and import Photon data for v1"
echo
read -rp "Do you want to continue anyway? (y/N): " CONTINUE
echo
if [[ ! "$CONTINUE" =~ ^[Yy]$ ]]; then
msg_info "Migration required. Update cancelled."
exit 0
fi
msg_warn "Continuing without migration may break Photon in the future!"
fi
msg_info "Stopping Service"
systemctl stop photon
msg_ok "Stopped Service"
rm -f /opt/photon/photon.jar
USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "photon" "komoot/photon" "singlefile" "latest" "/opt/photon" "photon-0*.jar"
USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "photon" "komoot/photon" "singlefile" "latest" "/opt/photon" "photon-*.jar"
mv /opt/photon/photon-*.jar /opt/photon/photon.jar
msg_info "Starting Service"
systemctl start photon
systemctl restart nginx
msg_ok "Started Service"
msg_ok "Updated successfully!"
fi

View File

@@ -33,12 +33,16 @@ function update_script() {
$STD apt upgrade -y
rm -rf /opt/tdarr/Tdarr_Updater
cd /opt/tdarr
RELEASE=$(curl -fsSL https://f000.backblazeb2.com/file/tdarrs/versions.json | grep -oP '(?<="Tdarr_Updater": ")[^"]+' | grep linux_x64 | head -n 1)
curl -fsSL "$RELEASE" -o Tdarr_Updater.zip
RELEASE=$(curl_with_retry "https://f000.backblazeb2.com/file/tdarrs/versions.json" "-" | grep -oP '(?<="Tdarr_Updater": ")[^"]+' | grep linux_x64 | head -n 1)
curl_with_retry "$RELEASE" "Tdarr_Updater.zip"
$STD unzip Tdarr_Updater.zip
chmod +x Tdarr_Updater
$STD ./Tdarr_Updater
rm -rf /opt/tdarr/Tdarr_Updater.zip
[[ -f /opt/tdarr/Tdarr_Server/Tdarr_Server ]] || {
msg_error "Tdarr_Updater failed — tdarr.io may be blocked by local DNS"
exit 250
}
msg_ok "Updated Tdarr"
msg_ok "Updated successfully!"
exit

View File

@@ -29,10 +29,116 @@ function update_script() {
exit
fi
if check_for_gh_tag "guacd" "apache/guacamole-server"; then
msg_info "Stopping guacd"
systemctl stop guacd 2>/dev/null || true
msg_ok "Stopped guacd"
ensure_dependencies \
libcairo2-dev \
libjpeg62-turbo-dev \
libpng-dev \
libtool-bin \
uuid-dev \
libvncserver-dev \
freerdp3-dev \
libssh2-1-dev \
libtelnet-dev \
libwebsockets-dev \
libpulse-dev \
libvorbis-dev \
libwebp-dev \
libssl-dev \
libpango1.0-dev \
libswscale-dev \
libavcodec-dev \
libavutil-dev \
libavformat-dev
msg_info "Updating Guacamole Server (guacd)"
fetch_and_deploy_gh_tag "guacd" "apache/guacamole-server" "${CHECK_UPDATE_RELEASE}" "/opt/guacamole-server"
cd /opt/guacamole-server
export CPPFLAGS="-Wno-error=deprecated-declarations"
$STD autoreconf -fi
$STD ./configure --with-init-dir=/etc/init.d --enable-allow-freerdp-snapshots
$STD make
$STD make install
$STD ldconfig
cd /opt
rm -rf /opt/guacamole-server
msg_ok "Updated Guacamole Server (guacd) to ${CHECK_UPDATE_RELEASE}"
if [[ ! -f /etc/guacamole/guacd.conf ]]; then
mkdir -p /etc/guacamole
cat <<EOF >/etc/guacamole/guacd.conf
[server]
bind_host = 127.0.0.1
bind_port = 4822
EOF
fi
if [[ ! -f /etc/systemd/system/guacd.service ]] || grep -q "Type=forking" /etc/systemd/system/guacd.service 2>/dev/null; then
cat <<EOF >/etc/systemd/system/guacd.service
[Unit]
Description=Guacamole Proxy Daemon (guacd)
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/sbin/guacd -f -b 127.0.0.1 -l 4822
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
fi
if ! grep -q "guacd.service" /etc/systemd/system/termix.service 2>/dev/null; then
sed -i '/^After=network.target/s/$/ guacd.service/' /etc/systemd/system/termix.service
sed -i '/^\[Unit\]/a Wants=guacd.service' /etc/systemd/system/termix.service
fi
systemctl daemon-reload
systemctl enable -q --now guacd
fi
if check_for_gh_release "termix" "Termix-SSH/Termix"; then
msg_info "Stopping Service"
msg_info "Stopping Termix"
systemctl stop termix
msg_ok "Stopped Service"
msg_ok "Stopped Termix"
msg_info "Migrating Configuration"
if [[ ! -f /opt/termix/.env ]]; then
cat <<EOF >/opt/termix/.env
NODE_ENV=production
DATA_DIR=/opt/termix/data
GUACD_HOST=127.0.0.1
GUACD_PORT=4822
EOF
fi
if ! grep -q "EnvironmentFile" /etc/systemd/system/termix.service 2>/dev/null; then
cat <<EOF >/etc/systemd/system/termix.service
[Unit]
Description=Termix Backend
After=network.target guacd.service
Wants=guacd.service
[Service]
Type=simple
User=root
WorkingDirectory=/opt/termix
EnvironmentFile=/opt/termix/.env
ExecStart=/usr/bin/node /opt/termix/dist/backend/backend/starter.js
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
fi
msg_ok "Migrated Configuration"
msg_info "Backing up Data"
cp -r /opt/termix/data /opt/termix_data_backup
@@ -95,16 +201,16 @@ function update_script() {
sed -i 's|/app/html|/opt/termix/html|g' /etc/nginx/nginx.conf
sed -i 's|/app/nginx|/opt/termix/nginx|g' /etc/nginx/nginx.conf
sed -i 's|listen ${PORT};|listen 80;|g' /etc/nginx/nginx.conf
nginx -t && systemctl reload nginx
msg_ok "Updated Nginx Configuration"
else
msg_warn "Nginx configuration not updated. If Termix doesn't work, restore from backup or update manually."
fi
msg_info "Starting Service"
msg_info "Starting Termix"
systemctl start termix
msg_ok "Started Service"
msg_ok "Started Termix"
msg_ok "Updated successfully!"
fi
exit

View File

@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: cobalt (cobaltgit)
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
# Source: https://ntfy.sh/
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Installing ntfy"
$STD apk add --no-cache ntfy ntfy-openrc libcap
sed -i '/^listen-http/s/^\(.*\)$/#\1\n/' /etc/ntfy/server.yml
setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy
$STD rc-update add ntfy default
$STD service ntfy start
msg_ok "Installed ntfy"
motd_ssh
customize

View File

@@ -35,7 +35,6 @@ setup_hwaccel
msg_info "Installing Channels DVR Server (Patience)"
cd /opt
$STD bash <(curl -fsSL https://getchannels.com/dvr/setup.sh)
sed -i -e 's/^sgx:x:104:$/render:x:104:root/' -e 's/^render:x:106:root$/sgx:x:106:/' /etc/group
msg_ok "Installed Channels DVR Server"
motd_ssh

View File

@@ -66,7 +66,7 @@ CELERY_BROKER_URL=redis://localhost:6379/0
DJANGO_SECRET_KEY=$DJANGO_SECRET
EOF
cd /opt/dispatcharr/frontend
$STD npm install --legacy-peer-deps
$STD npm install --no-audit --progress=false
$STD npm run build
msg_ok "Configured Dispatcharr"

View File

@@ -13,17 +13,9 @@ setting_up_container
network_check
update_os
setup_hwaccel
fetch_and_deploy_gh_release "emby" "MediaBrowser/Emby.Releases" "binary"
msg_info "Configuring Emby"
if [[ "$CTTYPE" == "0" ]]; then
sed -i -e 's/^ssl-cert:x:104:$/render:x:104:root,emby/' -e 's/^render:x:108:root,emby$/ssl-cert:x:108:/' /etc/group
else
sed -i -e 's/^ssl-cert:x:104:$/render:x:104:emby/' -e 's/^render:x:108:emby$/ssl-cert:x:108:/' /etc/group
fi
msg_ok "Configured Emby"
setup_hwaccel "emby"
motd_ssh
customize

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 tteck
# Author: tteck (tteckster)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://jellyfin.org/
@@ -14,31 +14,31 @@ network_check
update_os
msg_custom "" "${GN}" "If NVIDIA GPU passthrough is detected, you'll be asked whether to install drivers in the container"
setup_hwaccel
msg_info "Installing Jellyfin"
VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)"
if ! dpkg -s libjemalloc2 >/dev/null 2>&1; then
$STD apt install -y libjemalloc2
fi
msg_info "Installing Dependencies"
ensure_dependencies libjemalloc2
if [[ ! -f /usr/lib/libjemalloc.so ]]; then
ln -sf /usr/lib/x86_64-linux-gnu/libjemalloc.so.2 /usr/lib/libjemalloc.so
fi
if [[ ! -d /etc/apt/keyrings ]]; then
mkdir -p /etc/apt/keyrings
fi
curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key | gpg --dearmor --yes --output /etc/apt/keyrings/jellyfin.gpg
cat <<EOF >/etc/apt/sources.list.d/jellyfin.sources
Types: deb
URIs: https://repo.jellyfin.org/${PCT_OSTYPE}
Suites: ${VERSION}
Components: main
Architectures: amd64
Signed-By: /etc/apt/keyrings/jellyfin.gpg
EOF
msg_ok "Installed Dependencies"
$STD apt update
$STD apt install -y jellyfin
msg_info "Setting up Jellyfin Repository"
setup_deb822_repo \
"jellyfin" \
"https://repo.jellyfin.org/jellyfin_team.gpg.key" \
"https://repo.jellyfin.org/$(get_os_info id)" \
"$(get_os_info codename)"
msg_ok "Set up Jellyfin Repository"
msg_info "Installing Jellyfin"
$STD apt install -y jellyfin jellyfin-ffmpeg7
ln -sf /usr/lib/jellyfin-ffmpeg/ffmpeg /usr/bin/ffmpeg
ln -sf /usr/lib/jellyfin-ffmpeg/ffprobe /usr/bin/ffprobe
msg_ok "Installed Jellyfin"
setup_hwaccel "jellyfin"
msg_info "Configuring Jellyfin"
# Configure log rotation to prevent disk fill (keeps fail2ban compatibility) (PR: #1690 / Issue: #11224)
cat <<EOF >/etc/logrotate.d/jellyfin
/var/log/jellyfin/*.log {
@@ -55,12 +55,7 @@ EOF
chown -R jellyfin:adm /etc/jellyfin
sleep 10
systemctl restart jellyfin
if [[ "$CTTYPE" == "0" ]]; then
sed -i -e 's/^ssl-cert:x:104:$/render:x:104:root,jellyfin/' -e 's/^render:x:108:root,jellyfin$/ssl-cert:x:108:/' /etc/group
else
sed -i -e 's/^ssl-cert:x:104:$/render:x:104:jellyfin/' -e 's/^render:x:108:jellyfin$/ssl-cert:x:108:/' /etc/group
fi
msg_ok "Installed Jellyfin"
msg_ok "Configured Jellyfin"
motd_ssh
customize

View File

@@ -42,8 +42,6 @@ EOF
$STD apt update
msg_ok "Set up Intel® Repositories"
setup_hwaccel
msg_info "Installing Intel® Level Zero"
# Debian 13+ has newer Level Zero packages in system repos that conflict with Intel repo packages
if is_debian && [[ "$(get_os_version_major)" -ge 13 ]]; then
@@ -89,11 +87,11 @@ msg_info "Creating ollama User and Group"
if ! id ollama >/dev/null 2>&1; then
useradd -r -s /usr/sbin/nologin -U -m -d /usr/share/ollama ollama
fi
$STD usermod -aG render ollama || true
$STD usermod -aG video ollama || true
$STD usermod -aG ollama $(id -u -n)
msg_ok "Created ollama User and adjusted Groups"
setup_hwaccel "ollama"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/ollama.service
[Unit]

View File

@@ -13,8 +13,6 @@ setting_up_container
network_check
update_os
setup_hwaccel
msg_info "Setting Up Plex Media Server Repository"
setup_deb822_repo \
"plexmediaserver" \
@@ -26,13 +24,10 @@ msg_ok "Set Up Plex Media Server Repository"
msg_info "Installing Plex Media Server"
$STD apt install -y plexmediaserver
if [[ "$CTTYPE" == "0" ]]; then
sed -i -e 's/^ssl-cert:x:104:plex$/render:x:104:root,plex/' -e 's/^render:x:108:root$/ssl-cert:x:108:plex/' /etc/group
else
sed -i -e 's/^ssl-cert:x:104:plex$/render:x:104:plex/' -e 's/^render:x:108:$/ssl-cert:x:108:/' /etc/group
fi
msg_ok "Installed Plex Media Server"
setup_hwaccel "plex"
motd_ssh
customize
cleanup_lxc

View File

@@ -45,32 +45,58 @@ systemctl enable -q --now podman.socket
echo -e 'unqualified-search-registries=["docker.io"]' >>/etc/containers/registries.conf
msg_ok "Installed Podman"
mkdir -p /etc/containers/systemd
read -r -p "${TAB3}Would you like to add Portainer? <y/N> " prompt
if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then
msg_info "Installing Portainer $PORTAINER_LATEST_VERSION"
podman volume create portainer_data >/dev/null
$STD podman run -d \
-p 8000:8000 \
-p 9443:9443 \
--name=portainer \
--restart=always \
-v /run/podman/podman.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:latest
cat <<EOF >/etc/containers/systemd/portainer.container
[Unit]
Description=Portainer Container
After=network-online.target
[Container]
Image=docker.io/portainer/portainer-ce:latest
ContainerName=portainer
PublishPort=8000:8000
PublishPort=9443:9443
Volume=/run/podman/podman.sock:/var/run/docker.sock
Volume=portainer_data:/data
[Service]
Restart=always
[Install]
WantedBy=default.target multi-user.target
EOF
systemctl daemon-reload
$STD systemctl start portainer
msg_ok "Installed Portainer $PORTAINER_LATEST_VERSION"
else
read -r -p "${TAB3}Would you like to add the Portainer Agent? <y/N> " prompt
if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then
msg_info "Installing Portainer agent $PORTAINER_AGENT_LATEST_VERSION"
podman volume create temp >/dev/null
podman volume remove temp >/dev/null
$STD podman run -d \
-p 9001:9001 \
--name portainer_agent \
--restart=always \
-v /run/podman/podman.sock:/var/run/docker.sock \
-v /var/lib/containers/storage/volumes:/var/lib/docker/volumes \
portainer/agent
cat <<EOF >/etc/containers/systemd/portainer-agent.container
[Unit]
Description=Portainer Agent Container
After=network-online.target
[Container]
Image=docker.io/portainer/agent:latest
ContainerName=portainer_agent
PublishPort=9001:9001
Volume=/run/podman/podman.sock:/var/run/docker.sock
Volume=/var/lib/containers/storage/volumes:/var/lib/docker/volumes
[Service]
Restart=always
[Install]
WantedBy=default.target multi-user.target
EOF
systemctl daemon-reload
$STD systemctl start portainer-agent
msg_ok "Installed Portainer Agent $PORTAINER_AGENT_LATEST_VERSION"
fi
fi
@@ -81,19 +107,29 @@ msg_ok "Pulled Home Assistant Image"
msg_info "Installing Home Assistant"
$STD podman volume create hass_config
$STD podman run -d \
--name homeassistant \
--restart unless-stopped \
-v /dev:/dev \
-v hass_config:/config \
-v /etc/localtime:/etc/localtime:ro \
-v /etc/timezone:/etc/timezone:ro \
--net=host \
homeassistant/home-assistant:stable
podman generate systemd \
--new --name homeassistant \
>/etc/systemd/system/homeassistant.service
systemctl enable -q --now homeassistant
cat <<EOF >/etc/containers/systemd/homeassistant.container
[Unit]
Description=Home Assistant Container
After=network-online.target
[Container]
Image=docker.io/homeassistant/home-assistant:stable
ContainerName=homeassistant
Volume=/dev:/dev
Volume=hass_config:/config
Volume=/etc/localtime:/etc/localtime:ro
Volume=/etc/timezone:/etc/timezone:ro
Network=host
[Service]
Restart=always
TimeoutStartSec=300
[Install]
WantedBy=default.target multi-user.target
EOF
systemctl daemon-reload
$STD systemctl start homeassistant
msg_ok "Installed Home Assistant"
motd_ssh

View File

@@ -45,32 +45,58 @@ systemctl enable -q --now podman.socket
echo -e 'unqualified-search-registries=["docker.io"]' >>/etc/containers/registries.conf
msg_ok "Installed Podman"
mkdir -p /etc/containers/systemd
read -r -p "${TAB3}Would you like to add Portainer? <y/N> " prompt
if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then
msg_info "Installing Portainer $PORTAINER_LATEST_VERSION"
podman volume create portainer_data >/dev/null
$STD podman run -d \
-p 8000:8000 \
-p 9443:9443 \
--name=portainer \
--restart=always \
-v /run/podman/podman.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:latest
cat <<EOF >/etc/containers/systemd/portainer.container
[Unit]
Description=Portainer Container
After=network-online.target
[Container]
Image=docker.io/portainer/portainer-ce:latest
ContainerName=portainer
PublishPort=8000:8000
PublishPort=9443:9443
Volume=/run/podman/podman.sock:/var/run/docker.sock
Volume=portainer_data:/data
[Service]
Restart=always
[Install]
WantedBy=default.target multi-user.target
EOF
systemctl daemon-reload
$STD systemctl start portainer
msg_ok "Installed Portainer $PORTAINER_LATEST_VERSION"
else
read -r -p "${TAB3}Would you like to add the Portainer Agent? <y/N> " prompt
if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then
msg_info "Installing Portainer agent $PORTAINER_AGENT_LATEST_VERSION"
podman volume create temp >/dev/null
podman volume remove temp >/dev/null
$STD podman run -d \
-p 9001:9001 \
--name portainer_agent \
--restart=always \
-v /run/podman/podman.sock:/var/run/docker.sock \
-v /var/lib/containers/storage/volumes:/var/lib/docker/volumes \
portainer/agent
cat <<EOF >/etc/containers/systemd/portainer-agent.container
[Unit]
Description=Portainer Agent Container
After=network-online.target
[Container]
Image=docker.io/portainer/agent:latest
ContainerName=portainer_agent
PublishPort=9001:9001
Volume=/run/podman/podman.sock:/var/run/docker.sock
Volume=/var/lib/containers/storage/volumes:/var/lib/docker/volumes
[Service]
Restart=always
[Install]
WantedBy=default.target multi-user.target
EOF
systemctl daemon-reload
$STD systemctl start portainer-agent
msg_ok "Installed Portainer Agent $PORTAINER_AGENT_LATEST_VERSION"
fi
fi

View File

@@ -44,7 +44,7 @@ msg_ok "Configured RabbitMQ"
USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "reitti" "dedicatedcode/reitti" "singlefile" "latest" "/opt/reitti" "reitti-app.jar"
mv /opt/reitti/reitti-*.jar /opt/reitti/reitti.jar
USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "photon" "komoot/photon" "singlefile" "latest" "/opt/photon" "photon-0*.jar"
USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "photon" "komoot/photon" "singlefile" "latest" "/opt/photon" "photon-*.jar"
mv /opt/photon/photon-*.jar /opt/photon/photon.jar
msg_info "Installing Nginx Tile Cache"

View File

@@ -20,12 +20,16 @@ msg_ok "Installed Dependencies"
msg_info "Installing Tdarr"
mkdir -p /opt/tdarr
cd /opt/tdarr
RELEASE=$(curl -fsSL https://f000.backblazeb2.com/file/tdarrs/versions.json | grep -oP '(?<="Tdarr_Updater": ")[^"]+' | grep linux_x64 | head -n 1)
curl -fsSL "$RELEASE" -o Tdarr_Updater.zip
RELEASE=$(curl_with_retry "https://f000.backblazeb2.com/file/tdarrs/versions.json" "-" | grep -oP '(?<="Tdarr_Updater": ")[^"]+' | grep linux_x64 | head -n 1)
curl_with_retry "$RELEASE" "Tdarr_Updater.zip"
$STD unzip Tdarr_Updater.zip
chmod +x Tdarr_Updater
$STD ./Tdarr_Updater
rm -rf /opt/tdarr/Tdarr_Updater.zip
[[ -f /opt/tdarr/Tdarr_Server/Tdarr_Server ]] || {
msg_error "Tdarr_Updater failed — tdarr.io may be blocked by local DNS"
exit 250
}
msg_ok "Installed Tdarr"
setup_hwaccel

View File

@@ -19,9 +19,41 @@ $STD apt install -y \
python3 \
nginx \
openssl \
gettext-base
gettext-base \
libcairo2-dev \
libjpeg62-turbo-dev \
libpng-dev \
libtool-bin \
uuid-dev \
libvncserver-dev \
freerdp3-dev \
libssh2-1-dev \
libtelnet-dev \
libwebsockets-dev \
libpulse-dev \
libvorbis-dev \
libwebp-dev \
libssl-dev \
libpango1.0-dev \
libswscale-dev \
libavcodec-dev \
libavutil-dev \
libavformat-dev
msg_ok "Installed Dependencies"
msg_info "Building Guacamole Server (guacd)"
fetch_and_deploy_gh_tag "guacd" "apache/guacamole-server" "latest" "/opt/guacamole-server"
cd /opt/guacamole-server
export CPPFLAGS="-Wno-error=deprecated-declarations"
$STD autoreconf -fi
$STD ./configure --with-init-dir=/etc/init.d --enable-allow-freerdp-snapshots
$STD make
$STD make install
$STD ldconfig
cd /opt
rm -rf /opt/guacamole-server
msg_ok "Built Guacamole Server (guacd)"
NODE_VERSION="22" setup_nodejs
fetch_and_deploy_gh_release "termix" "Termix-SSH/Termix"
@@ -74,17 +106,46 @@ systemctl reload nginx
msg_ok "Configured Nginx"
msg_info "Creating Service"
mkdir -p /etc/guacamole
cat <<EOF >/etc/guacamole/guacd.conf
[server]
bind_host = 127.0.0.1
bind_port = 4822
EOF
cat <<EOF >/opt/termix/.env
NODE_ENV=production
DATA_DIR=/opt/termix/data
GUACD_HOST=127.0.0.1
GUACD_PORT=4822
EOF
cat <<EOF >/etc/systemd/system/guacd.service
[Unit]
Description=Guacamole Proxy Daemon (guacd)
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/sbin/guacd -f -b 127.0.0.1 -l 4822
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
cat <<EOF >/etc/systemd/system/termix.service
[Unit]
Description=Termix Backend
After=network.target
After=network.target guacd.service
Wants=guacd.service
[Service]
Type=simple
User=root
WorkingDirectory=/opt/termix
Environment=NODE_ENV=production
Environment=DATA_DIR=/opt/termix/data
EnvironmentFile=/opt/termix/.env
ExecStart=/usr/bin/node /opt/termix/dist/backend/backend/starter.js
Restart=on-failure
RestartSec=5
@@ -92,7 +153,7 @@ RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now termix
systemctl enable -q --now guacd termix
msg_ok "Created Service"
motd_ssh

View File

@@ -1979,6 +1979,47 @@ extract_version_from_json() {
fi
}
# ------------------------------------------------------------------------------
# Get latest GitHub tag (for repos that only publish tags, not releases).
#
# Usage:
# get_latest_gh_tag "owner/repo" [prefix]
#
# Arguments:
# $1 - GitHub repo (owner/repo)
# $2 - Optional prefix filter (e.g., "v" to only match tags starting with "v")
#
# Returns:
# Latest tag name (stdout), or returns 1 on failure
# ------------------------------------------------------------------------------
get_latest_gh_tag() {
local repo="$1"
local prefix="${2:-}"
local temp_file
temp_file=$(mktemp)
if ! github_api_call "https://api.github.com/repos/${repo}/tags?per_page=50" "$temp_file"; then
rm -f "$temp_file"
return 1
fi
local tag=""
if [[ -n "$prefix" ]]; then
tag=$(jq -r --arg p "$prefix" '[.[] | select(.name | startswith($p))][0].name // empty' "$temp_file")
else
tag=$(jq -r '.[0].name // empty' "$temp_file")
fi
rm -f "$temp_file"
if [[ -z "$tag" ]]; then
msg_error "No tags found for ${repo}"
return 1
fi
echo "$tag"
}
# ------------------------------------------------------------------------------
# Get latest GitHub release version with fallback to tags
# Usage: get_latest_github_release "owner/repo" [strip_v] [include_prerelease]
@@ -4215,6 +4256,8 @@ function setup_gs() {
# - NVIDIA requires matching host driver version
# ------------------------------------------------------------------------------
function setup_hwaccel() {
local service_user="${1:-}"
# Check if user explicitly disabled GPU in advanced settings
# ENABLE_GPU is exported from build.func
if [[ "${ENABLE_GPU:-no}" == "no" ]]; then
@@ -4466,7 +4509,7 @@ function setup_hwaccel() {
# ═══════════════════════════════════════════════════════════════════════════
# Device Permissions
# ═══════════════════════════════════════════════════════════════════════════
_setup_gpu_permissions "$in_ct"
_setup_gpu_permissions "$in_ct" "$service_user"
cache_installed_version "hwaccel" "1.0"
msg_ok "Setup Hardware Acceleration"
@@ -5100,6 +5143,7 @@ EOF
# ══════════════════════════════════════════════════════════════════════════════
_setup_gpu_permissions() {
local in_ct="$1"
local service_user="${2:-}"
# /dev/dri permissions (Intel/AMD)
if [[ "$in_ct" == "0" && -d /dev/dri ]]; then
@@ -5166,6 +5210,12 @@ _setup_gpu_permissions() {
chmod 666 /dev/kfd 2>/dev/null || true
msg_info "AMD ROCm compute device configured"
fi
# Add service user to render and video groups for GPU hardware acceleration
if [[ -n "$service_user" ]]; then
usermod -aG render "$service_user" 2>/dev/null || true
usermod -aG video "$service_user" 2>/dev/null || true
fi
}
# ------------------------------------------------------------------------------
@@ -6648,22 +6698,38 @@ EOF
# - Optionally uses official PGDG repository for specific versions
# - Detects existing PostgreSQL version
# - Dumps all databases before upgrade
# - Installs optional PG_MODULES (e.g. postgis, contrib)
# - Installs optional PG_MODULES (e.g. postgis, contrib, cron)
# - Restores dumped data post-upgrade
#
# Variables:
# USE_PGDG_REPO - Use official PGDG repository (default: true)
# Set to "false" to use distro packages instead
# PG_VERSION - Major PostgreSQL version (e.g. 15, 16) (default: 16)
# PG_MODULES - Comma-separated list of modules (e.g. "postgis,contrib")
# PG_MODULES - Comma-separated list of modules (e.g. "postgis,contrib,cron")
#
# Examples:
# setup_postgresql # Uses PGDG repo, PG 16
# PG_VERSION="17" setup_postgresql # Specific version from PGDG
# USE_PGDG_REPO=false setup_postgresql # Uses distro package instead
# setup_postgresql # Uses PGDG repo, PG 16
# PG_VERSION="17" setup_postgresql # Specific version from PGDG
# USE_PGDG_REPO=false setup_postgresql # Uses distro package instead
# PG_VERSION="17" PG_MODULES="cron" setup_postgresql # With pg_cron module
# ------------------------------------------------------------------------------
function setup_postgresql() {
# Internal helper: Configure shared_preload_libraries for pg_cron
_configure_pg_cron_preload() {
local modules="${1:-}"
[[ -z "$modules" ]] && return 0
if [[ ",$modules," == *",cron,"* ]]; then
local current_libs
current_libs=$(sudo -u postgres psql -tAc "SHOW shared_preload_libraries;" 2>/dev/null || echo "")
if [[ "$current_libs" != *"pg_cron"* ]]; then
local new_libs="${current_libs:+${current_libs},}pg_cron"
$STD sudo -u postgres psql -c "ALTER SYSTEM SET shared_preload_libraries = '${new_libs}';"
$STD systemctl restart postgresql
fi
fi
}
setup_postgresql() {
local PG_VERSION="${PG_VERSION:-16}"
local PG_MODULES="${PG_MODULES:-}"
local USE_PGDG_REPO="${USE_PGDG_REPO:-true}"
@@ -6701,6 +6767,7 @@ function setup_postgresql() {
$STD apt install -y "postgresql-${CURRENT_PG_VERSION}-${module}" 2>/dev/null || true
done
fi
_configure_pg_cron_preload "$PG_MODULES"
return 0
fi
@@ -6736,6 +6803,7 @@ function setup_postgresql() {
$STD apt install -y "postgresql-${INSTALLED_VERSION}-${module}" 2>/dev/null || true
done
fi
_configure_pg_cron_preload "$PG_MODULES"
return 0
fi
@@ -6757,6 +6825,7 @@ function setup_postgresql() {
$STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || true
done
fi
_configure_pg_cron_preload "$PG_MODULES"
return 0
fi
@@ -6784,13 +6853,16 @@ function setup_postgresql() {
local SUITE
case "$DISTRO_CODENAME" in
trixie | forky | sid)
if verify_repo_available "https://apt.postgresql.org/pub/repos/apt" "trixie-pgdg"; then
SUITE="trixie-pgdg"
else
msg_warn "PGDG repo not available for ${DISTRO_CODENAME}, falling back to distro packages"
USE_PGDG_REPO=false setup_postgresql
return $?
fi
;;
*)
SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt")
@@ -6874,6 +6946,7 @@ function setup_postgresql() {
}
done
fi
_configure_pg_cron_preload "$PG_MODULES"
}
# ------------------------------------------------------------------------------
@@ -6892,6 +6965,7 @@ function setup_postgresql() {
# PG_DB_NAME="immich" PG_DB_USER="immich" PG_DB_EXTENSIONS="pgvector" setup_postgresql_db
# PG_DB_NAME="ghostfolio" PG_DB_USER="ghostfolio" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db
# PG_DB_NAME="adventurelog" PG_DB_USER="adventurelog" PG_DB_EXTENSIONS="postgis" setup_postgresql_db
# PG_DB_NAME="splitpro" PG_DB_USER="splitpro" PG_DB_EXTENSIONS="pg_cron" setup_postgresql_db
#
# Variables:
# PG_DB_NAME - Database name (required)
@@ -6923,6 +6997,13 @@ function setup_postgresql_db() {
$STD sudo -u postgres psql -c "CREATE ROLE $PG_DB_USER WITH LOGIN PASSWORD '$PG_DB_PASS';"
$STD sudo -u postgres psql -c "CREATE DATABASE $PG_DB_NAME WITH OWNER $PG_DB_USER ENCODING 'UTF8' TEMPLATE template0;"
# Configure pg_cron database BEFORE creating the extension (must be set before pg_cron loads)
if [[ -n "${PG_DB_EXTENSIONS:-}" ]] && [[ ",${PG_DB_EXTENSIONS//[[:space:]]/}," == *",pg_cron,"* ]]; then
$STD sudo -u postgres psql -c "ALTER SYSTEM SET cron.database_name = '${PG_DB_NAME}';"
$STD sudo -u postgres psql -c "ALTER SYSTEM SET cron.timezone = 'UTC';"
$STD systemctl restart postgresql
fi
# Install extensions (comma-separated)
if [[ -n "${PG_DB_EXTENSIONS:-}" ]]; then
IFS=',' read -ra EXT_LIST <<<"${PG_DB_EXTENSIONS:-}"
@@ -6932,6 +7013,12 @@ function setup_postgresql_db() {
done
fi
# Grant pg_cron schema permissions to DB user
if [[ -n "${PG_DB_EXTENSIONS:-}" ]] && [[ ",${PG_DB_EXTENSIONS//[[:space:]]/}," == *",pg_cron,"* ]]; then
$STD sudo -u postgres psql -d "$PG_DB_NAME" -c "GRANT USAGE ON SCHEMA cron TO ${PG_DB_USER};"
$STD sudo -u postgres psql -d "$PG_DB_NAME" -c "GRANT ALL ON ALL TABLES IN SCHEMA cron TO ${PG_DB_USER};"
fi
# ALTER ROLE settings for Django/Rails compatibility (unless skipped)
if [[ "${PG_DB_SKIP_ALTER_ROLE:-}" != "true" ]]; then
$STD sudo -u postgres psql -c "ALTER ROLE $PG_DB_USER SET client_encoding TO 'utf8';"
@@ -6973,7 +7060,6 @@ function setup_postgresql_db() {
export PG_DB_USER
export PG_DB_PASS
}
# ------------------------------------------------------------------------------
# Installs rbenv and ruby-build, installs Ruby and optionally Rails.
#