Proyecto LCX Dispatcharr multicuenta
Base Image Build / prepare (push) Has been cancelled
Build and Push Multi-Arch Docker Image / build-and-push (push) Has been cancelled
Frontend Tests / test (push) Has been cancelled
Base Image Build / docker (amd64, ubuntu-24.04) (push) Has been cancelled
Base Image Build / docker (arm64, ubuntu-24.04-arm) (push) Has been cancelled
Base Image Build / create-manifest (push) Has been cancelled
Base Image Build / prepare (push) Has been cancelled
Build and Push Multi-Arch Docker Image / build-and-push (push) Has been cancelled
Frontend Tests / test (push) Has been cancelled
Base Image Build / docker (amd64, ubuntu-24.04) (push) Has been cancelled
Base Image Build / docker (arm64, ubuntu-24.04-arm) (push) Has been cancelled
Base Image Build / create-manifest (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Fix TLS client key permissions and ownership for PostgreSQL.
|
||||
# libpq requires the client key to be 0600 or stricter.
|
||||
#
|
||||
# Triggers on:
|
||||
# - Permissions too open (Docker Desktop mounts files as 0777)
|
||||
# - Wrong ownership (Kubernetes secrets / Docker volumes mount as root;
|
||||
# the application user can't read a root-owned 0600 key)
|
||||
# - Read-only source (volume mounted :ro — can't chmod in place)
|
||||
#
|
||||
# Usage: source this script with FIXED_KEY_PATH set to the destination.
|
||||
# FIXED_KEY_PATH="/data/.pg-client.key"
|
||||
# . /app/docker/init/00-fix-pg-ssl-key.sh
|
||||
#
|
||||
# After sourcing, POSTGRES_SSL_KEY is updated to the fixed path if a copy
|
||||
# was needed. The caller is responsible for propagating the new value to
|
||||
# /etc/environment or profile.d if required.
|
||||
|
||||
: "${FIXED_KEY_PATH:?FIXED_KEY_PATH must be set before sourcing fix-pg-ssl-key.sh}"
|
||||
|
||||
if [ -n "${POSTGRES_SSL_KEY:-}" ] && [ -f "$POSTGRES_SSL_KEY" ]; then
|
||||
_key_perms=$(stat -c '%a' "$POSTGRES_SSL_KEY" 2>/dev/null)
|
||||
_key_owner=$(stat -c '%u' "$POSTGRES_SSL_KEY" 2>/dev/null)
|
||||
_needs_fix=false
|
||||
|
||||
if [ "$_key_perms" != "600" ] && [ "$_key_perms" != "640" ]; then
|
||||
_needs_fix=true
|
||||
elif [ "$(id -u)" = "0" ] && [ -n "${PUID:-}" ] && [ "$_key_owner" != "$PUID" ]; then
|
||||
_needs_fix=true
|
||||
fi
|
||||
|
||||
if [ "$_needs_fix" = true ]; then
|
||||
cp "$POSTGRES_SSL_KEY" "$FIXED_KEY_PATH"
|
||||
chmod 600 "$FIXED_KEY_PATH"
|
||||
if [ "$(id -u)" = "0" ] && [ -n "${PUID:-}" ]; then
|
||||
chown "${PUID}:${PGID:-$PUID}" "$FIXED_KEY_PATH"
|
||||
fi
|
||||
export POSTGRES_SSL_KEY="$FIXED_KEY_PATH"
|
||||
echo "Fixed PostgreSQL client key (perms: ${_key_perms}, owner: ${_key_owner} → ${PUID:-root}:600)"
|
||||
fi
|
||||
|
||||
unset _key_perms _key_owner _needs_fix
|
||||
fi
|
||||
@@ -0,0 +1,127 @@
|
||||
#!/bin/bash
|
||||
|
||||
# NOTE: PUID/PGID values matching internal system UIDs (e.g. 102 for the
|
||||
# postgres package user) will cause that OS user/group to be renamed to
|
||||
# $POSTGRES_USER inside the container. This is cosmetic and does not affect
|
||||
# runtime behavior since all postgres operations run as $POSTGRES_USER
|
||||
# rather than the postgres system user.
|
||||
|
||||
# Default PUID/PGID to 1000 when not explicitly set.
|
||||
# The old image ran Django as UID 1000 and PostgreSQL as UID 102. Since
|
||||
# DATA_DIRS and host-side files are owned by 1000, defaulting to 1000
|
||||
# preserves access for upgrading users without requiring configuration.
|
||||
# The DB ownership migration (102 → 1000) is handled by 02-postgres.sh.
|
||||
export PUID=${PUID:-1000}
|
||||
export PGID=${PGID:-1000}
|
||||
|
||||
# Validate PUID/PGID are positive integers before any user/group operations.
|
||||
# Non-numeric values would cause useradd/groupadd to fail with confusing errors.
|
||||
if ! [[ "$PUID" =~ ^[0-9]+$ ]] || ! [[ "$PGID" =~ ^[0-9]+$ ]]; then
|
||||
echo ""
|
||||
echo "================================================================"
|
||||
echo "ERROR: PUID and PGID must be positive integers."
|
||||
echo " PUID=$PUID PGID=$PGID"
|
||||
echo " Please set valid numeric values (default: 1000)."
|
||||
echo "================================================================"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# PostgreSQL refuses to run as root (UID 0). Block early — before any
|
||||
# user/group manipulation — to prevent renaming the root user/group,
|
||||
# which would break the container.
|
||||
if [ "$PUID" = "0" ] || [ "$PGID" = "0" ]; then
|
||||
echo ""
|
||||
echo "================================================================"
|
||||
echo "ERROR: PUID=0 or PGID=0 is not supported."
|
||||
echo " PostgreSQL cannot run as root (UID 0)."
|
||||
echo " Please set PUID and PGID to a non-zero value (default: 1000)."
|
||||
echo "================================================================"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if group with PGID exists
|
||||
if getent group "$PGID" >/dev/null 2>&1; then
|
||||
# Group exists, check if it's named correctly (should match POSTGRES_USER)
|
||||
existing_group=$(getent group "$PGID" | cut -d: -f1)
|
||||
if [ "$existing_group" != "$POSTGRES_USER" ]; then
|
||||
# Rename the existing group to match POSTGRES_USER
|
||||
groupmod -n "$POSTGRES_USER" "$existing_group"
|
||||
echo "Group $existing_group with GID $PGID renamed to $POSTGRES_USER"
|
||||
fi
|
||||
else
|
||||
# Group doesn't exist, create it with same name as POSTGRES_USER
|
||||
groupadd -g "$PGID" "$POSTGRES_USER"
|
||||
echo "Group $POSTGRES_USER with GID $PGID created"
|
||||
fi
|
||||
|
||||
# Create user if it doesn't exist
|
||||
if ! getent passwd "$PUID" > /dev/null 2>&1; then
|
||||
useradd -u "$PUID" -g "$PGID" -m "$POSTGRES_USER"
|
||||
else
|
||||
existing_user=$(getent passwd "$PUID" | cut -d: -f1)
|
||||
if [ "$existing_user" != "$POSTGRES_USER" ]; then
|
||||
usermod -l "$POSTGRES_USER" -g "$PGID" "$existing_user"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get the GID of /dev/dri/renderD128 on the host (must be mounted into container)
|
||||
if [ -e "/dev/dri/renderD128" ]; then
|
||||
HOST_RENDER_GID=$(stat -c '%g' /dev/dri/renderD128)
|
||||
|
||||
# Check if this GID belongs to the video group
|
||||
VIDEO_GID=$(getent group video 2>/dev/null | cut -d: -f3)
|
||||
|
||||
if [ "$HOST_RENDER_GID" = "$VIDEO_GID" ]; then
|
||||
echo "RenderD128 GID ($HOST_RENDER_GID) matches video group GID. Using video group for GPU access."
|
||||
# Make sure POSTGRES_USER is in video group
|
||||
if ! id -nG "$POSTGRES_USER" | grep -qw "video"; then
|
||||
usermod -a -G video "$POSTGRES_USER"
|
||||
echo "Added user $POSTGRES_USER to video group for GPU access"
|
||||
fi
|
||||
else
|
||||
# We need to ensure render group exists with correct GID
|
||||
if getent group render >/dev/null; then
|
||||
CURRENT_RENDER_GID=$(getent group render | cut -d: -f3)
|
||||
if [ "$CURRENT_RENDER_GID" != "$HOST_RENDER_GID" ]; then
|
||||
# Check if another group already has the target GID
|
||||
if getent group "$HOST_RENDER_GID" >/dev/null 2>&1; then
|
||||
EXISTING_GROUP=$(getent group "$HOST_RENDER_GID" | cut -d: -f1)
|
||||
echo "Warning: Cannot change render group GID to $HOST_RENDER_GID as it's already used by group '$EXISTING_GROUP'"
|
||||
# Add user to the existing group with the target GID to ensure device access
|
||||
if ! id -nG "$POSTGRES_USER" | grep -qw "$EXISTING_GROUP"; then
|
||||
usermod -a -G "$EXISTING_GROUP" "$POSTGRES_USER" || echo "Warning: Failed to add user to $EXISTING_GROUP group"
|
||||
echo "Added user $POSTGRES_USER to $EXISTING_GROUP group for GPU access"
|
||||
fi
|
||||
else
|
||||
echo "Changing render group GID from $CURRENT_RENDER_GID to $HOST_RENDER_GID"
|
||||
groupmod -g "$HOST_RENDER_GID" render || echo "Warning: Failed to change render group GID. Continuing anyway..."
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Creating render group with GID $HOST_RENDER_GID"
|
||||
groupadd -g "$HOST_RENDER_GID" render
|
||||
fi
|
||||
|
||||
# Make sure POSTGRES_USER is in render group
|
||||
if ! id -nG "$POSTGRES_USER" | grep -qw "render"; then
|
||||
usermod -a -G render "$POSTGRES_USER"
|
||||
echo "Added user $POSTGRES_USER to render group for GPU access"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Warning: /dev/dri/renderD128 not found. GPU acceleration may not be available."
|
||||
fi
|
||||
|
||||
# Always add user to video group for hardware acceleration if it exists
|
||||
# (some systems use video group for general GPU access)
|
||||
if getent group video >/dev/null 2>&1; then
|
||||
if ! id -nG "$POSTGRES_USER" | grep -qw "video"; then
|
||||
usermod -a -G video "$POSTGRES_USER"
|
||||
echo "Added user $POSTGRES_USER to video group for hardware acceleration access"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Run nginx as specified user (replace any existing user directive on line 1)
|
||||
sed -i "1s/^user .*/user $POSTGRES_USER;/" /etc/nginx/nginx.conf
|
||||
@@ -0,0 +1,507 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Skip internal PostgreSQL setup in modular mode (using external database)
|
||||
if [[ "$DISPATCHARR_ENV" != "modular" ]]; then
|
||||
|
||||
# Record PUID:PGID in a sentinel file so subsequent startups can skip
|
||||
# the expensive recursive chown when ownership is already correct.
|
||||
write_ownership_sentinel() {
|
||||
echo "$PUID:$PGID" > "${POSTGRES_DIR}/.owner_puid"
|
||||
chown "$PUID:$PGID" "${POSTGRES_DIR}/.owner_puid"
|
||||
}
|
||||
|
||||
# Ensure the PostgreSQL socket directory exists, is owned by PUID:PGID,
|
||||
# and has no stale lock/socket files from an unclean previous shutdown.
|
||||
# Called immediately before every pg_ctl start so it runs after any apt
|
||||
# post-remove scripts that might reset the directory's ownership.
|
||||
prepare_pg_socket_dir() {
|
||||
mkdir -p /var/run/postgresql
|
||||
chown "$PUID:$PGID" /var/run/postgresql
|
||||
chmod 755 /var/run/postgresql
|
||||
rm -f "/var/run/postgresql/.s.PGSQL.${POSTGRES_PORT}" \
|
||||
"/var/run/postgresql/.s.PGSQL.${POSTGRES_PORT}.lock" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Write standard pg_hba.conf and enable network listening.
|
||||
# Local (Unix socket): trust — safe for single-app containers where only
|
||||
# authorized processes connect. Network: password required via md5.
|
||||
# Idempotent: safe to call on every startup.
|
||||
configure_pg_network() {
|
||||
local datadir="$1"
|
||||
cat > "${datadir}/pg_hba.conf" <<HBAEOF
|
||||
local all all trust
|
||||
host all all 0.0.0.0/0 md5
|
||||
host all all ::1/128 md5
|
||||
HBAEOF
|
||||
chown "$PUID:$PGID" "${datadir}/pg_hba.conf"
|
||||
# Remove any active listen_addresses setting, then append the canonical
|
||||
# value. Avoids duplicate accumulation across restarts. Only targets
|
||||
# uncommented lines; leaves initdb's default comment intact.
|
||||
sed -Ei '/^[[:space:]]*listen_addresses[[:space:]]*=/d' "${datadir}/postgresql.conf"
|
||||
echo "listen_addresses='*'" >> "${datadir}/postgresql.conf"
|
||||
}
|
||||
|
||||
# Legacy migration: move data from /data root into $POSTGRES_DIR.
|
||||
# Safe to remove once all deployments have upgraded past this layout.
|
||||
if [ -e "/data/postgresql.conf" ]; then
|
||||
echo "Migrating PostgreSQL data from /data to $POSTGRES_DIR..."
|
||||
|
||||
# Create a temporary directory outside of /data
|
||||
mkdir -p /tmp/postgres_migration
|
||||
|
||||
# Move the PostgreSQL files to the temporary directory
|
||||
mv /data/* /tmp/postgres_migration/
|
||||
|
||||
# Create the target directory
|
||||
mkdir -p "$POSTGRES_DIR"
|
||||
|
||||
# Move the files from temporary directory to the final location
|
||||
mv /tmp/postgres_migration/* "$POSTGRES_DIR/"
|
||||
|
||||
# Clean up the temporary directory
|
||||
rmdir /tmp/postgres_migration
|
||||
|
||||
# Set proper ownership and permissions for PostgreSQL data directory
|
||||
chown -R "$PUID:$PGID" "$POSTGRES_DIR"
|
||||
chmod 700 "$POSTGRES_DIR"
|
||||
|
||||
echo "Migration completed successfully."
|
||||
fi
|
||||
|
||||
PG_VERSION_FILE="${POSTGRES_DIR}/PG_VERSION"
|
||||
|
||||
# Detect current version from data directory, if present
|
||||
if [ -f "$PG_VERSION_FILE" ]; then
|
||||
CURRENT_VERSION=$(cat "$PG_VERSION_FILE")
|
||||
else
|
||||
CURRENT_VERSION=""
|
||||
fi
|
||||
|
||||
# =========================================================================
|
||||
# Existing data: ensure ownership, auth, and permissions are correct.
|
||||
# These guarantees run on EVERY startup with existing data — not just
|
||||
# upgrades. This eliminates conditional edge cases and ensures the
|
||||
# container always reaches a known-good state regardless of how the
|
||||
# data was originally created.
|
||||
# =========================================================================
|
||||
if [ -n "$CURRENT_VERSION" ] && [ -d "$POSTGRES_DIR" ]; then
|
||||
|
||||
# --- 1. Ownership reconciliation (conditional — only when needed) ---
|
||||
# Two triggers cause a recursive chown:
|
||||
# a) PG_VERSION owner doesn't match PUID (obvious mismatch)
|
||||
# b) Sentinel file (.owner_puid) missing or stale — catches partial
|
||||
# chown from a previous interrupted startup where early files
|
||||
# (including PG_VERSION) got the new owner but deeper files didn't.
|
||||
# After a successful chown, the sentinel records PUID:PGID so
|
||||
# subsequent startups skip the expensive recursive operation.
|
||||
OWNERSHIP_SENTINEL="${POSTGRES_DIR}/.owner_puid"
|
||||
CURRENT_OWNER=$(stat -c '%u' "$PG_VERSION_FILE")
|
||||
_needs_chown=false
|
||||
if [ "$CURRENT_OWNER" != "$PUID" ]; then
|
||||
_needs_chown=true
|
||||
elif [ ! -f "$OWNERSHIP_SENTINEL" ] || [ "$(cat "$OWNERSHIP_SENTINEL" 2>/dev/null)" != "$PUID:$PGID" ]; then
|
||||
# Sentinel missing or stale. Could be:
|
||||
# a) First startup with sentinel code (pre-existing data) — benign
|
||||
# b) Interrupted chown from a previous startup — needs re-chown
|
||||
# Spot-check a deeper directory to distinguish: if base/ also
|
||||
# matches PUID:PGID, ownership is likely consistent (case a).
|
||||
_deeper_check=$(stat -c '%u:%g' "${POSTGRES_DIR}/base" 2>/dev/null)
|
||||
if [ "$_deeper_check" != "$PUID:$PGID" ]; then
|
||||
_needs_chown=true
|
||||
else
|
||||
# Spot-check passed — ownership is consistent, record sentinel
|
||||
# so future startups skip the spot-check entirely.
|
||||
write_ownership_sentinel
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$_needs_chown" = true ]; then
|
||||
echo "Migrating PostgreSQL data ownership from UID $CURRENT_OWNER to $PUID:$PGID..."
|
||||
echo " This may take several minutes for large databases. Do not stop the container."
|
||||
if ! chown -R "$PUID:$PGID" "$POSTGRES_DIR" 2>/dev/null; then
|
||||
echo ""
|
||||
echo "================================================================"
|
||||
echo "ERROR: Cannot update ownership of $POSTGRES_DIR"
|
||||
echo " Current owner: UID $CURRENT_OWNER"
|
||||
echo " Target owner: UID $PUID (GID $PGID)"
|
||||
echo ""
|
||||
echo " This typically occurs with rootless Docker or restricted"
|
||||
echo " filesystems (NFS with root_squash, CIFS/SMB)."
|
||||
echo ""
|
||||
echo " To fix:"
|
||||
echo " - Local/NFS: sudo chown -R $PUID:$PGID <host_path_to_data>/db"
|
||||
echo " - CIFS/SMB: set the mount uid=$PUID,gid=$PGID option instead"
|
||||
echo " Then restart the container."
|
||||
echo "================================================================"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
chmod 700 "$POSTGRES_DIR"
|
||||
# Write sentinel LAST — if chown was interrupted, the sentinel
|
||||
# won't exist and next startup will re-run the full chown.
|
||||
write_ownership_sentinel
|
||||
echo "Ownership migration complete."
|
||||
fi
|
||||
|
||||
# --- 2. Authentication guarantee (unconditional) ---
|
||||
# Always rewrite pg_hba.conf to the known-good state. This replaces
|
||||
# any auth method (peer, ident, md5, scram) left by previous images
|
||||
# or initdb defaults. Eliminates the class of bugs where the OS user
|
||||
# name doesn't match any PG role under peer/ident auth.
|
||||
configure_pg_network "${POSTGRES_DIR}"
|
||||
fi
|
||||
|
||||
# Only run upgrade if current version is set and not the target
|
||||
if [ -n "$CURRENT_VERSION" ] && [ "$CURRENT_VERSION" != "$PG_VERSION" ]; then
|
||||
echo "Detected PostgreSQL data directory version $CURRENT_VERSION, upgrading to $PG_VERSION..."
|
||||
# Set binary paths for upgrade if needed
|
||||
OLD_BINDIR="/usr/lib/postgresql/${CURRENT_VERSION}/bin"
|
||||
NEW_BINDIR="/usr/lib/postgresql/${PG_VERSION}/bin"
|
||||
PG_INSTALLED_BY_SCRIPT=0
|
||||
if [ ! -d "$OLD_BINDIR" ]; then
|
||||
echo "PostgreSQL binaries for version $CURRENT_VERSION not found. Installing..."
|
||||
apt update && apt install -y postgresql-$CURRENT_VERSION postgresql-contrib-$CURRENT_VERSION
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to install PostgreSQL version $CURRENT_VERSION. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
PG_INSTALLED_BY_SCRIPT=1
|
||||
fi
|
||||
|
||||
# Prepare the old cluster for pg_upgrade:
|
||||
# 1. Promote $POSTGRES_USER to superuser (needed for post-upgrade ops)
|
||||
# 2. Detect the bootstrap superuser (install user) — pg_upgrade
|
||||
# requires -U to match this role exactly.
|
||||
# The old cluster's install user is "postgres" (pre-PUID images)
|
||||
# or $POSTGRES_USER (post-PUID images, future upgrades).
|
||||
echo "Preparing old cluster for upgrade..."
|
||||
prepare_pg_socket_dir
|
||||
su - "$POSTGRES_USER" -c "$OLD_BINDIR/pg_ctl -D $POSTGRES_DIR start -w -o '-c port=${POSTGRES_PORT}'"
|
||||
_promoted=false
|
||||
for _role in "postgres" "$POSTGRES_USER"; do
|
||||
if su - "$POSTGRES_USER" -c "psql -U $_role -d template1 -p ${POSTGRES_PORT} -tAc 'SELECT 1;'" 2>/dev/null | grep -q 1; then
|
||||
if su - "$POSTGRES_USER" -c "psql -U $_role -d template1 -p ${POSTGRES_PORT} -v ON_ERROR_STOP=1" <<UPGEOF
|
||||
DO \$\$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '$POSTGRES_USER') THEN
|
||||
CREATE ROLE $POSTGRES_USER WITH SUPERUSER LOGIN;
|
||||
ELSE
|
||||
ALTER ROLE $POSTGRES_USER WITH SUPERUSER;
|
||||
END IF;
|
||||
END
|
||||
\$\$;
|
||||
UPGEOF
|
||||
then
|
||||
_promoted=true
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Detect the bootstrap superuser (OID 10 = the role that ran initdb).
|
||||
_install_user=$(su - "$POSTGRES_USER" -c "psql -d template1 -p ${POSTGRES_PORT} -tAc \
|
||||
\"SELECT rolname FROM pg_authid WHERE oid = 10;\"" 2>/dev/null | tr -d '[:space:]')
|
||||
if [ -z "$_install_user" ]; then
|
||||
_install_user="postgres"
|
||||
fi
|
||||
|
||||
su - "$POSTGRES_USER" -c "$OLD_BINDIR/pg_ctl -D $POSTGRES_DIR stop -w"
|
||||
if [ "$_promoted" != true ]; then
|
||||
echo "❌ Failed to prepare old cluster for upgrade."
|
||||
echo " Could not promote '$POSTGRES_USER' to superuser in PG $CURRENT_VERSION."
|
||||
exit 1
|
||||
fi
|
||||
echo "Old cluster install user: $_install_user"
|
||||
|
||||
# Prepare new data directory
|
||||
NEW_POSTGRES_DIR="${POSTGRES_DIR}_$PG_VERSION"
|
||||
|
||||
# Remove new data directory if it already exists (from a failed/partial upgrade)
|
||||
if [ -d "$NEW_POSTGRES_DIR" ]; then
|
||||
echo "Warning: $NEW_POSTGRES_DIR already exists. Removing it to avoid upgrade issues."
|
||||
rm -rf "$NEW_POSTGRES_DIR"
|
||||
fi
|
||||
|
||||
mkdir -p "$NEW_POSTGRES_DIR"
|
||||
chown -R "$PUID:$PGID" "$NEW_POSTGRES_DIR"
|
||||
chmod 700 "$NEW_POSTGRES_DIR"
|
||||
|
||||
# Initialize new data directory with the same install user as the old
|
||||
# cluster. pg_upgrade requires the -U user to match both clusters.
|
||||
echo "Initializing new PostgreSQL data directory at $NEW_POSTGRES_DIR..."
|
||||
su - "$POSTGRES_USER" -c "$NEW_BINDIR/initdb -U $_install_user -D $NEW_POSTGRES_DIR"
|
||||
echo "Running pg_upgrade from $OLD_BINDIR to $NEW_BINDIR..."
|
||||
su - "$POSTGRES_USER" -c "$NEW_BINDIR/pg_upgrade -U $_install_user -b $OLD_BINDIR -B $NEW_BINDIR -d $POSTGRES_DIR -D $NEW_POSTGRES_DIR"
|
||||
|
||||
# Move old data directory for backup, move new into place
|
||||
mv "$POSTGRES_DIR" "${POSTGRES_DIR}_backup_${CURRENT_VERSION}_$(date +%s)"
|
||||
mv "$NEW_POSTGRES_DIR" "$POSTGRES_DIR"
|
||||
|
||||
# Apply standard connection configuration to the upgraded data directory.
|
||||
configure_pg_network "${POSTGRES_DIR}"
|
||||
|
||||
# Record ownership sentinel for the newly upgraded data directory.
|
||||
write_ownership_sentinel
|
||||
|
||||
echo "Upgrade complete. Old data directory backed up."
|
||||
|
||||
# Uninstall PostgreSQL if we installed it just for upgrade
|
||||
if [ "$PG_INSTALLED_BY_SCRIPT" -eq 1 ]; then
|
||||
echo "Uninstalling temporary PostgreSQL $CURRENT_VERSION packages..."
|
||||
apt remove -y postgresql-$CURRENT_VERSION postgresql-contrib-$CURRENT_VERSION
|
||||
apt autoremove -y
|
||||
fi
|
||||
fi
|
||||
|
||||
# Initialize PostgreSQL data directory (fresh install only).
|
||||
# Only runs initdb + configure_pg_network here. Database creation,
|
||||
# role setup, and password configuration are handled by the
|
||||
# unconditional guarantees (promote_app_role, ensure_app_database)
|
||||
# after PostgreSQL starts in entrypoint.sh.
|
||||
if [ -z "$(ls -A "$POSTGRES_DIR")" ]; then
|
||||
echo "Initializing PostgreSQL database..."
|
||||
mkdir -p "$POSTGRES_DIR"
|
||||
chown -R "$PUID:$PGID" "$POSTGRES_DIR"
|
||||
chmod 700 "$POSTGRES_DIR"
|
||||
|
||||
# Initialize PostgreSQL as the application user.
|
||||
# The superuser role is automatically named $POSTGRES_USER.
|
||||
su - "$POSTGRES_USER" -c "$PG_BINDIR/initdb -D ${POSTGRES_DIR}"
|
||||
|
||||
# Configure authentication and network access.
|
||||
configure_pg_network "${POSTGRES_DIR}"
|
||||
|
||||
# Record ownership sentinel for the freshly initialized data directory.
|
||||
write_ownership_sentinel
|
||||
fi
|
||||
|
||||
fi # End of DISPATCHARR_ENV != modular check
|
||||
|
||||
# =========================================================================
|
||||
# 3. Role guarantee (unconditional — runs after PostgreSQL starts)
|
||||
#
|
||||
# Ensures the application role ($POSTGRES_USER) exists with superuser
|
||||
# privileges and the correct password. Called from entrypoint.sh after
|
||||
# PostgreSQL starts on every AIO startup.
|
||||
#
|
||||
# Idempotent: checks before altering. Handles all scenarios:
|
||||
# - Fresh install: role exists from initdb, just verifies
|
||||
# - Upgrade from postgres-user: creates dispatch role, promotes to superuser
|
||||
# - PUID change: verifies existing role, updates password
|
||||
# - Normal restart: no-op (role already correct)
|
||||
#
|
||||
# Tries multiple database/role combinations to handle incomplete data
|
||||
# (e.g., interrupted initialization from a previous image version).
|
||||
# =========================================================================
|
||||
promote_app_role() {
|
||||
if [[ "$DISPATCHARR_ENV" == "modular" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Ensuring application role is configured..."
|
||||
|
||||
# Find a connectable superuser role. Try multiple databases in case
|
||||
# the default 'postgres' database doesn't exist (e.g., incomplete
|
||||
# initialization from a crashed previous container).
|
||||
# Single query per candidate: if connection fails, output is empty;
|
||||
# if connected but not superuser, output is 'f'. Only 't' passes.
|
||||
local CONNECT_ROLE=""
|
||||
local CONNECT_DB=""
|
||||
for try_db in "postgres" "template1"; do
|
||||
for try_role in "postgres" "$POSTGRES_USER"; do
|
||||
local _super
|
||||
_super=$(su - "$POSTGRES_USER" -c "psql -U $try_role -d $try_db -p ${POSTGRES_PORT} -tAc \
|
||||
\"SELECT rolsuper FROM pg_roles WHERE rolname='$try_role';\"" 2>/dev/null | tr -d '[:space:]')
|
||||
if [ "$_super" = "t" ]; then
|
||||
CONNECT_ROLE="$try_role"
|
||||
CONNECT_DB="$try_db"
|
||||
break 2
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [ -z "$CONNECT_ROLE" ]; then
|
||||
echo "❌ Role setup failed: no connectable superuser role found."
|
||||
echo " To recover manually:"
|
||||
echo " su - "$POSTGRES_USER" -c \"psql -d template1 -p $POSTGRES_PORT\""
|
||||
echo " CREATE ROLE $POSTGRES_USER WITH SUPERUSER LOGIN PASSWORD '<your_password>';"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Escape single quotes for safe SQL interpolation
|
||||
local _sql_pw="${POSTGRES_PASSWORD//\'/\'\'}"
|
||||
|
||||
if ! su - "$POSTGRES_USER" -c "psql -U $CONNECT_ROLE -d $CONNECT_DB -p ${POSTGRES_PORT} -v ON_ERROR_STOP=1" <<EOSQL
|
||||
DO \$\$
|
||||
BEGIN
|
||||
-- Ensure the application role exists with superuser and login.
|
||||
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '$POSTGRES_USER') THEN
|
||||
CREATE ROLE $POSTGRES_USER WITH SUPERUSER LOGIN PASSWORD '${_sql_pw}';
|
||||
ELSE
|
||||
-- Only alter if not already superuser (idempotent).
|
||||
IF NOT (SELECT rolsuper FROM pg_roles WHERE rolname = '$POSTGRES_USER') THEN
|
||||
ALTER ROLE $POSTGRES_USER WITH SUPERUSER LOGIN;
|
||||
END IF;
|
||||
-- Ensure password is current regardless.
|
||||
ALTER ROLE $POSTGRES_USER WITH PASSWORD '${_sql_pw}';
|
||||
END IF;
|
||||
|
||||
-- Rollback compatibility: preserve the postgres role as superuser so
|
||||
-- older images (which connect as the postgres DB role) continue to work.
|
||||
-- This block can be removed once rollback to pre-PUID images is no
|
||||
-- longer expected.
|
||||
IF EXISTS (SELECT FROM pg_roles WHERE rolname = 'postgres') THEN
|
||||
IF NOT (SELECT rolsuper FROM pg_roles WHERE rolname = 'postgres') THEN
|
||||
ALTER ROLE postgres WITH SUPERUSER;
|
||||
END IF;
|
||||
END IF;
|
||||
END
|
||||
\$\$;
|
||||
EOSQL
|
||||
then
|
||||
echo "❌ Role setup failed. The application may not be able to connect."
|
||||
echo " Check PostgreSQL logs for details."
|
||||
echo " To recover manually:"
|
||||
echo " su - "$POSTGRES_USER" -c \"psql -d template1 -p $POSTGRES_PORT\""
|
||||
echo " ALTER ROLE $POSTGRES_USER WITH SUPERUSER LOGIN PASSWORD '<your_password>';"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Application role configured."
|
||||
}
|
||||
|
||||
# =========================================================================
|
||||
# 4. Database guarantee (unconditional — runs after role setup)
|
||||
#
|
||||
# Ensures the application database ($POSTGRES_DB) exists. Handles
|
||||
# incomplete data from interrupted previous initializations where
|
||||
# PG_VERSION exists but the application database was never created.
|
||||
# =========================================================================
|
||||
ensure_app_database() {
|
||||
if [[ "$DISPATCHARR_ENV" == "modular" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Connect to template1 (always exists) to check pg_database catalog.
|
||||
if su - "$POSTGRES_USER" -c "psql -d template1 -p ${POSTGRES_PORT} -tAc \
|
||||
\"SELECT 1 FROM pg_database WHERE datname = '$POSTGRES_DB';\"" 2>/dev/null | grep -q 1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Application database '$POSTGRES_DB' not found — creating..."
|
||||
if ! su - "$POSTGRES_USER" -c "createdb -p ${POSTGRES_PORT} --encoding=UTF8 ${POSTGRES_DB}" 2>/dev/null; then
|
||||
# Might already exist if the check failed for a transient reason.
|
||||
if su - "$POSTGRES_USER" -c "psql -d template1 -p ${POSTGRES_PORT} -tAc \
|
||||
\"SELECT 1 FROM pg_database WHERE datname = '$POSTGRES_DB';\"" 2>/dev/null | grep -q 1; then
|
||||
return 0
|
||||
fi
|
||||
echo "❌ Failed to create database '$POSTGRES_DB'"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Database '$POSTGRES_DB' created."
|
||||
}
|
||||
|
||||
ensure_utf8_encoding() {
|
||||
# Check encoding of existing database
|
||||
# Supports both internal (Unix socket) and external (TCP) PostgreSQL
|
||||
echo "Checking database encoding..."
|
||||
|
||||
if [[ "$DISPATCHARR_ENV" == "modular" ]]; then
|
||||
# External database: use TCP connection with password
|
||||
CURRENT_ENCODING=$(PGPASSWORD="$POSTGRES_PASSWORD" psql -w -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -tAc "SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database();" 2>/dev/null | tr -d ' ')
|
||||
else
|
||||
# Internal database: use Unix socket as application user
|
||||
CURRENT_ENCODING=$(su - "$POSTGRES_USER" -c "psql -p ${POSTGRES_PORT} -d ${POSTGRES_DB} -tAc \"SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database();\"" | tr -d ' ')
|
||||
fi
|
||||
|
||||
if [ "$CURRENT_ENCODING" != "UTF8" ]; then
|
||||
echo "Database $POSTGRES_DB encoding is $CURRENT_ENCODING, converting to UTF8..."
|
||||
DUMP_FILE="/tmp/${POSTGRES_DB}_utf8_dump_$(date +%s).sql"
|
||||
|
||||
if [[ "$DISPATCHARR_ENV" == "modular" ]]; then
|
||||
# External database: use TCP connection with password
|
||||
# Dump database (include permissions and ownership)
|
||||
PGPASSWORD="$POSTGRES_PASSWORD" pg_dump -w -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER" "$POSTGRES_DB" > "$DUMP_FILE" || { echo "Dump failed"; return 1; }
|
||||
# Drop and recreate database with UTF8 encoding using template0
|
||||
PGPASSWORD="$POSTGRES_PASSWORD" dropdb -w -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER" "$POSTGRES_DB" || { echo "Drop failed"; return 1; }
|
||||
# Recreate database with UTF8 encoding
|
||||
PGPASSWORD="$POSTGRES_PASSWORD" createdb -w -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER" --encoding=UTF8 --template=template0 "$POSTGRES_DB" || { echo "Create failed"; return 1; }
|
||||
# Restore data
|
||||
PGPASSWORD="$POSTGRES_PASSWORD" psql -w -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER" -d "$POSTGRES_DB" < "$DUMP_FILE" || { echo "Restore failed"; return 1; }
|
||||
else
|
||||
# Internal database: use Unix socket as application user
|
||||
# Dump database (include permissions and ownership)
|
||||
su - "$POSTGRES_USER" -c "pg_dump -p ${POSTGRES_PORT} ${POSTGRES_DB}" > "$DUMP_FILE" || { echo "Dump failed"; return 1; }
|
||||
# Drop and recreate database with UTF8 encoding using template0
|
||||
su - "$POSTGRES_USER" -c "dropdb -p ${POSTGRES_PORT} ${POSTGRES_DB}" || { echo "Drop failed"; return 1; }
|
||||
# Recreate database with UTF8 encoding and correct owner
|
||||
su - "$POSTGRES_USER" -c "createdb -p ${POSTGRES_PORT} --encoding=UTF8 --template=template0 --owner=${POSTGRES_USER} ${POSTGRES_DB}" || { echo "Create failed"; return 1; }
|
||||
# Restore data
|
||||
cat "$DUMP_FILE" | su - "$POSTGRES_USER" -c "psql -p ${POSTGRES_PORT} -d ${POSTGRES_DB}" || { echo "Restore failed"; return 1; }
|
||||
fi
|
||||
|
||||
rm -f "$DUMP_FILE"
|
||||
echo "✅ Database $POSTGRES_DB converted to UTF8."
|
||||
else
|
||||
echo "✅ Database encoding is UTF8"
|
||||
fi
|
||||
}
|
||||
|
||||
check_external_postgres_version() {
|
||||
# Only check for modular deployments
|
||||
if [[ "$DISPATCHARR_ENV" != "modular" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "🔍 Checking external PostgreSQL version compatibility..."
|
||||
|
||||
# Get minimum required version from base image (set in entrypoint.sh)
|
||||
# PG_VERSION is from DispatcharrBase
|
||||
MIN_REQUIRED_VERSION=$PG_VERSION
|
||||
|
||||
# Query external PostgreSQL version
|
||||
# Use $POSTGRES_DB — restricted users may not have access to the default 'postgres' database
|
||||
PG_VERSION_ERR=$(mktemp)
|
||||
EXTERNAL_VERSION=$(PGPASSWORD="$POSTGRES_PASSWORD" psql -w -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -tAc "SHOW server_version;" 2>"$PG_VERSION_ERR" | grep -oE '^[0-9]+')
|
||||
|
||||
if [ -z "$EXTERNAL_VERSION" ]; then
|
||||
echo "❌ ERROR: Unable to determine external PostgreSQL version"
|
||||
echo " Could not connect to database '$POSTGRES_DB' at ${POSTGRES_HOST}:${POSTGRES_PORT} as user '$POSTGRES_USER'"
|
||||
echo " Error: $(cat "$PG_VERSION_ERR")"
|
||||
echo " Please verify your database connection settings."
|
||||
rm -f "$PG_VERSION_ERR"
|
||||
return 1
|
||||
fi
|
||||
rm -f "$PG_VERSION_ERR"
|
||||
|
||||
# Compare versions
|
||||
if [[ "$EXTERNAL_VERSION" -lt "$MIN_REQUIRED_VERSION" ]]; then
|
||||
# FAIL: Version too old
|
||||
echo ""
|
||||
echo "❌ ERROR: PostgreSQL version mismatch"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " External Database: PostgreSQL $EXTERNAL_VERSION"
|
||||
echo " Required Version: PostgreSQL $MIN_REQUIRED_VERSION or higher"
|
||||
echo ""
|
||||
echo " Your external PostgreSQL database is too old for Dispatcharr."
|
||||
echo " Please upgrade to PostgreSQL $MIN_REQUIRED_VERSION or higher."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
return 1
|
||||
|
||||
elif [[ "$EXTERNAL_VERSION" -eq "$MIN_REQUIRED_VERSION" ]]; then
|
||||
# MATCH: Exact version match
|
||||
echo "✅ PostgreSQL version check passed"
|
||||
echo " External Database: PostgreSQL $EXTERNAL_VERSION (matches target version)"
|
||||
|
||||
else
|
||||
# HIGHER: Newer version
|
||||
echo "✅ PostgreSQL version check passed"
|
||||
echo " External Database: PostgreSQL $EXTERNAL_VERSION"
|
||||
echo " Target Version: PostgreSQL $MIN_REQUIRED_VERSION"
|
||||
echo " ℹ️ Your database is newer than the target version."
|
||||
echo " PostgreSQL version should be compatible with Dispatcharr."
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Define directories that need to exist and be owned by PUID:PGID.
|
||||
# DATA_DIRS may reside on external mounts (NFS, SMB/CIFS, FUSE) where
|
||||
# mkdir and chown can fail. Failures are collected and reported as a
|
||||
# single consolidated warning so the container still starts.
|
||||
DATA_DIRS=(
|
||||
"/data/backups"
|
||||
"/data/logos"
|
||||
"/data/recordings"
|
||||
"/data/uploads/m3us"
|
||||
"/data/uploads/epgs"
|
||||
"/data/m3us"
|
||||
"/data/epgs"
|
||||
"/data/plugins"
|
||||
"/data/models"
|
||||
"/data/scripts"
|
||||
)
|
||||
|
||||
# APP_DIRS live on the image layer and are always locally writable.
|
||||
APP_DIRS=(
|
||||
"/app/logo_cache"
|
||||
"/app/media"
|
||||
"/app/static"
|
||||
)
|
||||
|
||||
# Create app directories (image layer — always writable)
|
||||
for dir in "${APP_DIRS[@]}"; do
|
||||
mkdir -p "$dir"
|
||||
done
|
||||
|
||||
# Create data directories, tolerating failures on external mounts
|
||||
_failed_mkdir=()
|
||||
_failed_chown=()
|
||||
for dir in "${DATA_DIRS[@]}"; do
|
||||
_mkdir_err=$(mkdir -p "$dir" 2>&1) || _failed_mkdir+=("$dir ($_mkdir_err)")
|
||||
done
|
||||
|
||||
# Ensure /app itself is owned by PUID:PGID (needed for uwsgi socket creation)
|
||||
if [ "$(id -u)" = "0" ] && [ -d "/app" ]; then
|
||||
if [ "$(stat -c '%u:%g' /app)" != "$PUID:$PGID" ]; then
|
||||
echo "Fixing ownership for /app (non-recursive)"
|
||||
chown "$PUID:$PGID" /app
|
||||
fi
|
||||
fi
|
||||
# Configure nginx port
|
||||
if ! [[ "$DISPATCHARR_PORT" =~ ^[0-9]+$ ]]; then
|
||||
echo "⚠️ Warning: DISPATCHARR_PORT is not a valid integer, using default port 9191"
|
||||
DISPATCHARR_PORT=9191
|
||||
fi
|
||||
sed -i "s/NGINX_PORT/${DISPATCHARR_PORT}/g" /etc/nginx/sites-enabled/default
|
||||
|
||||
# Configure nginx based on IPv6 availability
|
||||
if ip -6 addr show | grep -q "inet6"; then
|
||||
echo "✅ IPv6 is available, enabling IPv6 in nginx"
|
||||
else
|
||||
echo "⚠️ IPv6 not available, disabling IPv6 in nginx"
|
||||
sed -i '/listen \[::\]:/d' /etc/nginx/sites-enabled/default
|
||||
fi
|
||||
|
||||
# NOTE: mac doesn't run as root, so only manage permissions
|
||||
# if this script is running as root
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
# Fix data directories (non-recursive to avoid touching user files).
|
||||
# Failures are collected rather than fatal — directories may be on
|
||||
# external mounts (NFS, SMB/CIFS, FUSE) that reject chown.
|
||||
for dir in "${DATA_DIRS[@]}"; do
|
||||
if [ -d "$dir" ] && [ "$(stat -c '%u:%g' "$dir" 2>/dev/null)" != "$PUID:$PGID" ]; then
|
||||
_chown_err=$(chown "$PUID:$PGID" "$dir" 2>&1) || {
|
||||
_current_owner=$(stat -c '%u:%g' "$dir" 2>/dev/null || echo "unknown")
|
||||
_failed_chown+=("$dir (current: $_current_owner, error: $_chown_err)")
|
||||
}
|
||||
fi
|
||||
done
|
||||
|
||||
# Fix app directories (recursive since they're managed by the app)
|
||||
for dir in "${APP_DIRS[@]}"; do
|
||||
if [ -d "$dir" ] && [ "$(stat -c '%u:%g' "$dir")" != "$PUID:$PGID" ]; then
|
||||
echo "Fixing ownership for $dir (recursive)"
|
||||
chown -R "$PUID:$PGID" "$dir"
|
||||
fi
|
||||
done
|
||||
|
||||
# /data/db ownership is handled by 02-postgres.sh (sentinel-based reconciliation).
|
||||
# No secondary check needed here — duplicating it could chown without updating
|
||||
# the sentinel, creating inconsistent state.
|
||||
|
||||
# Fix /data directory ownership (non-recursive).
|
||||
# Tolerates failure for the same external-mount reasons as DATA_DIRS.
|
||||
if [ -d "/data" ] && [ "$(stat -c '%u:%g' /data 2>/dev/null)" != "$PUID:$PGID" ]; then
|
||||
_chown_err=$(chown "$PUID:$PGID" /data 2>&1) || {
|
||||
_current_owner=$(stat -c '%u:%g' /data 2>/dev/null || echo "unknown")
|
||||
_failed_chown+=("/data (current: $_current_owner, error: $_chown_err)")
|
||||
}
|
||||
fi
|
||||
|
||||
chmod +x /data 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Consolidated warning for all mkdir/chown failures.
|
||||
# Emitted outside the root guard so non-root mkdir failures are also reported.
|
||||
if [ ${#_failed_mkdir[@]} -gt 0 ] || [ ${#_failed_chown[@]} -gt 0 ]; then
|
||||
echo ""
|
||||
echo "================================================================"
|
||||
echo "WARNING: Some data directories could not be created or updated."
|
||||
echo " This typically occurs with NFS, SMB/CIFS, or other external"
|
||||
echo " mounts that restrict ownership changes."
|
||||
echo ""
|
||||
if [ ${#_failed_mkdir[@]} -gt 0 ]; then
|
||||
echo " Could not create:"
|
||||
for entry in "${_failed_mkdir[@]}"; do
|
||||
echo " - $entry"
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
if [ ${#_failed_chown[@]} -gt 0 ]; then
|
||||
echo " Could not set ownership to $PUID:$PGID:"
|
||||
for entry in "${_failed_chown[@]}"; do
|
||||
echo " - $entry"
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
echo " To fix, either:"
|
||||
echo " 1. Set PUID/PGID to match your mount's owner"
|
||||
echo " 2. Fix ownership on the host/NAS:"
|
||||
echo " sudo chown $PUID:$PGID <path>"
|
||||
echo " 3. For SMB/CIFS: set uid=$PUID,gid=$PGID in mount options"
|
||||
echo "================================================================"
|
||||
echo ""
|
||||
fi
|
||||
@@ -0,0 +1,715 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "🔍 Checking for GPU acceleration devices..."
|
||||
|
||||
# Helper function for device access checks
|
||||
check_dev() {
|
||||
local dev=$1
|
||||
if [ -e "$dev" ]; then
|
||||
if [ -r "$dev" ] && [ -w "$dev" ]; then
|
||||
echo "✅ Device $dev is accessible."
|
||||
else
|
||||
echo "⚠️ Device $dev exists but is not accessible. Check permissions or container runtime options."
|
||||
fi
|
||||
else
|
||||
echo "ℹ️ Device $dev does not exist."
|
||||
fi
|
||||
}
|
||||
|
||||
# Initialize device detection flags
|
||||
ANY_GPU_DEVICES_FOUND=false
|
||||
DRI_DEVICES_FOUND=false
|
||||
NVIDIA_FOUND=false
|
||||
NVIDIA_GPU_IN_LSPCI=false
|
||||
INTEL_GPU_IN_LSPCI=false
|
||||
AMD_GPU_IN_LSPCI=false
|
||||
|
||||
# Check for all GPU types in hardware via lspci
|
||||
if command -v lspci >/dev/null 2>&1; then
|
||||
# Check for NVIDIA GPUs
|
||||
if lspci | grep -i "NVIDIA" | grep -i "VGA\|3D\|Display" >/dev/null; then
|
||||
NVIDIA_GPU_IN_LSPCI=true
|
||||
NVIDIA_MODEL=$(lspci | grep -i "NVIDIA" | grep -i "VGA\|3D\|Display" | head -1 | sed -E 's/.*: (.*) \[.*/\1/' | sed 's/Corporation //')
|
||||
fi
|
||||
|
||||
# Check for Intel GPUs - making sure it's not already detected as NVIDIA
|
||||
if lspci | grep -i "Intel" | grep -v "NVIDIA" | grep -i "VGA\|3D\|Display" >/dev/null; then
|
||||
INTEL_GPU_IN_LSPCI=true
|
||||
INTEL_MODEL=$(lspci | grep -i "Intel" | grep -v "NVIDIA" | grep -i "VGA\|3D\|Display" | head -1 | sed -E 's/.*: (.*) \[.*/\1/' | sed 's/Corporation //')
|
||||
fi
|
||||
|
||||
# Check for AMD GPUs - making sure it's not already detected as NVIDIA or Intel
|
||||
if lspci | grep -i "AMD\|ATI\|Advanced Micro Devices" | grep -v "NVIDIA\|Intel" | grep -i "VGA\|3D\|Display" >/dev/null; then
|
||||
AMD_GPU_IN_LSPCI=true
|
||||
AMD_MODEL=$(lspci | grep -i "AMD\|ATI\|Advanced Micro Devices" | grep -v "NVIDIA\|Intel" | grep -i "VGA\|3D\|Display" | head -1 | sed -E 's/.*: (.*) \[.*/\1/' | sed 's/Corporation //' | sed 's/Technologies //')
|
||||
fi
|
||||
|
||||
# Display detected GPU hardware
|
||||
if [ "$NVIDIA_GPU_IN_LSPCI" = true ]; then
|
||||
echo "🔍 Hardware detection: NVIDIA GPU ($NVIDIA_MODEL)"
|
||||
fi
|
||||
if [ "$INTEL_GPU_IN_LSPCI" = true ]; then
|
||||
echo "🔍 Hardware detection: Intel GPU ($INTEL_MODEL)"
|
||||
fi
|
||||
if [ "$AMD_GPU_IN_LSPCI" = true ]; then
|
||||
echo "🔍 Hardware detection: AMD GPU ($AMD_MODEL)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Silently check for any GPU devices first
|
||||
for dev in /dev/dri/renderD* /dev/dri/card* /dev/nvidia*; do
|
||||
if [ -e "$dev" ]; then
|
||||
ANY_GPU_DEVICES_FOUND=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Only if devices might exist, show detailed checks
|
||||
if [ "$ANY_GPU_DEVICES_FOUND" = true ]; then
|
||||
# Check Intel/AMD VAAPI devices
|
||||
echo "🔍 Checking for VAAPI device nodes (Intel/AMD)..."
|
||||
for dev in /dev/dri/renderD* /dev/dri/card*; do
|
||||
if [ -e "$dev" ]; then
|
||||
DRI_DEVICES_FOUND=true
|
||||
check_dev "$dev"
|
||||
fi
|
||||
done
|
||||
|
||||
# Check NVIDIA device nodes
|
||||
echo "🔍 Checking for NVIDIA device nodes..."
|
||||
for dev in /dev/nvidia*; do
|
||||
if [ -e "$dev" ]; then
|
||||
NVIDIA_FOUND=true
|
||||
check_dev "$dev"
|
||||
fi
|
||||
done
|
||||
|
||||
# Show GPU device availability messages
|
||||
if [ "$NVIDIA_FOUND" = false ] && [ "$NVIDIA_GPU_IN_LSPCI" = true ]; then
|
||||
echo "⚠️ No NVIDIA device nodes available despite hardware detection."
|
||||
echo " You may be able to use VAAPI for hardware acceleration, but NVENC/CUDA won't be available."
|
||||
echo " For optimal performance, configure proper NVIDIA container runtime."
|
||||
elif [ "$NVIDIA_FOUND" = false ]; then
|
||||
echo "ℹ️ No NVIDIA device nodes found under /dev."
|
||||
fi
|
||||
|
||||
# Check for Intel/AMD GPUs that might not be fully accessible
|
||||
if [ "$DRI_DEVICES_FOUND" = false ] && [ "$INTEL_GPU_IN_LSPCI" = true ]; then
|
||||
echo "⚠️ Intel GPU detected in hardware but no DRI devices found."
|
||||
echo " Hardware acceleration will not be available."
|
||||
echo " Make sure /dev/dri/ devices are properly mapped to the container."
|
||||
elif [ "$DRI_DEVICES_FOUND" = false ] && [ "$AMD_GPU_IN_LSPCI" = true ]; then
|
||||
echo "⚠️ AMD GPU detected in hardware but no DRI devices found."
|
||||
echo " Hardware acceleration will not be available."
|
||||
echo " Make sure /dev/dri/ devices are properly mapped to the container."
|
||||
fi
|
||||
else
|
||||
# No GPU devices found, skip the detailed checks
|
||||
echo "❌ No GPU acceleration devices detected in this container."
|
||||
echo "ℹ️ Checking for potential configuration issues..."
|
||||
|
||||
# Check if the host might have GPUs that aren't passed to the container
|
||||
if command -v lspci >/dev/null 2>&1; then
|
||||
if lspci | grep -i "VGA\|3D\|Display" | grep -i "NVIDIA\|Intel\|AMD" >/dev/null; then
|
||||
echo "⚠️ Host system appears to have GPU hardware, but no devices are accessible to the container."
|
||||
echo " - For NVIDIA GPUs: Ensure NVIDIA Container Runtime is configured properly"
|
||||
echo " - For Intel/AMD GPUs: Verify that /dev/dri/ devices are passed to the container"
|
||||
echo " - Check your Docker run command or docker-compose.yml for proper device mapping"
|
||||
else
|
||||
echo "ℹ️ No GPU hardware detected on the host system. CPU-only transcoding will be used."
|
||||
fi
|
||||
else
|
||||
echo "ℹ️ Unable to check host GPU hardware (lspci not available). CPU-only transcoding will be used."
|
||||
fi
|
||||
|
||||
echo "📋 =================================================="
|
||||
echo "✅ GPU detection script complete. No GPUs available for hardware acceleration."
|
||||
# Don't exit the container - just return from this script
|
||||
return 0 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Check group membership for GPU access - context-aware based on hardware
|
||||
echo "🔍 Checking user group memberships and device access..."
|
||||
VIDEO_GID=$(getent group video | cut -d: -f3)
|
||||
RENDER_GID=$(getent group render | cut -d: -f3)
|
||||
NVIDIA_CONTAINER_TOOLKIT_FOUND=false
|
||||
NVIDIA_ENV_MISMATCH=false
|
||||
|
||||
# Improved device access check function
|
||||
check_user_device_access() {
|
||||
local device=$1
|
||||
local user=$2
|
||||
if [ -e "$device" ];then
|
||||
if su -c "test -r '$device' && test -w '$device'" - "$user" 2>/dev/null; then
|
||||
echo "✅ User $user has full access to $device"
|
||||
return 0
|
||||
else
|
||||
echo "⚠️ User $user cannot access $device (permission denied)"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
# Device doesn't exist, no need to report here
|
||||
return 2
|
||||
fi
|
||||
}
|
||||
|
||||
# Direct device access verification for DRI (Intel/AMD)
|
||||
echo "🔍 Verifying if $POSTGRES_USER has direct access to GPU devices..."
|
||||
HAS_DRI_ACCESS=false
|
||||
DRI_ACCESS_COUNT=0
|
||||
DRI_DEVICE_COUNT=0
|
||||
|
||||
for dev in /dev/dri/renderD* /dev/dri/card*; do
|
||||
if [ -e "$dev" ]; then
|
||||
DRI_DEVICE_COUNT=$((DRI_DEVICE_COUNT + 1))
|
||||
if check_user_device_access "$dev" "$POSTGRES_USER"; then
|
||||
DRI_ACCESS_COUNT=$((DRI_ACCESS_COUNT + 1))
|
||||
HAS_DRI_ACCESS=true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Direct device access verification for NVIDIA
|
||||
HAS_NVIDIA_ACCESS=false
|
||||
NVIDIA_ACCESS_COUNT=0
|
||||
NVIDIA_DEVICE_COUNT=0
|
||||
|
||||
for dev in /dev/nvidia*; do
|
||||
if [ -e "$dev" ]; then
|
||||
NVIDIA_DEVICE_COUNT=$((NVIDIA_DEVICE_COUNT + 1))
|
||||
if check_user_device_access "$dev" "$POSTGRES_USER"; then
|
||||
NVIDIA_ACCESS_COUNT=$((NVIDIA_ACCESS_COUNT + 1))
|
||||
HAS_NVIDIA_ACCESS=true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Summary of device access
|
||||
if [ $DRI_DEVICE_COUNT -gt 0 ]; then
|
||||
if [ $DRI_ACCESS_COUNT -eq $DRI_DEVICE_COUNT ]; then
|
||||
echo "✅ User $POSTGRES_USER has access to all DRI devices ($DRI_ACCESS_COUNT/$DRI_DEVICE_COUNT)"
|
||||
echo " VAAPI hardware acceleration should work properly."
|
||||
else
|
||||
echo "⚠️ User $POSTGRES_USER has limited access to DRI devices ($DRI_ACCESS_COUNT/$DRI_DEVICE_COUNT)"
|
||||
echo " VAAPI hardware acceleration may not work properly."
|
||||
echo " Consider adding $POSTGRES_USER to the 'video' and/or 'render' groups."
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $NVIDIA_DEVICE_COUNT -gt 0 ]; then
|
||||
if [ $NVIDIA_ACCESS_COUNT -eq $NVIDIA_DEVICE_COUNT ]; then
|
||||
echo "✅ User $POSTGRES_USER has access to all NVIDIA devices ($NVIDIA_ACCESS_COUNT/$NVIDIA_DEVICE_COUNT)"
|
||||
echo " NVIDIA hardware acceleration should work properly."
|
||||
else
|
||||
echo "⚠️ User $POSTGRES_USER has limited access to NVIDIA devices ($NVIDIA_ACCESS_COUNT/$NVIDIA_DEVICE_COUNT)"
|
||||
echo " NVIDIA hardware acceleration may not work properly."
|
||||
if [ "$NVIDIA_CONTAINER_TOOLKIT_FOUND" = false ]; then
|
||||
echo " Consider adding $POSTGRES_USER to the 'video' group or use NVIDIA Container Toolkit."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for traditional group memberships (as additional information)
|
||||
USER_IN_VIDEO_GROUP=false
|
||||
USER_IN_RENDER_GROUP=false
|
||||
|
||||
if [ -n "$VIDEO_GID" ]; then
|
||||
if id -nG "$POSTGRES_USER" 2>/dev/null | grep -qw "video"; then
|
||||
USER_IN_VIDEO_GROUP=true
|
||||
echo "ℹ️ User $POSTGRES_USER is in the 'video' group (GID $VIDEO_GID)."
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$RENDER_GID" ]; then
|
||||
if id -nG "$POSTGRES_USER" 2>/dev/null | grep -qw "render"; then
|
||||
USER_IN_RENDER_GROUP=true
|
||||
echo "ℹ️ User $POSTGRES_USER is in the 'render' group (GID $RENDER_GID)."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if NVIDIA Container Toolkit is present through environment or CLI tool
|
||||
# IMPORTANT: Only mark as found if both env vars AND actual NVIDIA devices exist
|
||||
if [ "$NVIDIA_FOUND" = true ] && command -v nvidia-container-cli >/dev/null 2>&1; then
|
||||
NVIDIA_CONTAINER_TOOLKIT_FOUND=true
|
||||
# Check for environment variables set by NVIDIA Container Runtime, but only if NVIDIA hardware exists
|
||||
elif [ "$NVIDIA_FOUND" = true ] && [ -n "$NVIDIA_VISIBLE_DEVICES" ] && [ -n "$NVIDIA_DRIVER_CAPABILITIES" ]; then
|
||||
NVIDIA_CONTAINER_TOOLKIT_FOUND=true
|
||||
echo "✅ NVIDIA Container Toolkit detected (via environment variables)."
|
||||
echo " The container is properly configured with Docker Compose's 'driver: nvidia' syntax."
|
||||
elif [ -n "$NVIDIA_VISIBLE_DEVICES" ] && [ -n "$NVIDIA_DRIVER_CAPABILITIES" ] && [ "$NVIDIA_FOUND" = false ]; then
|
||||
NVIDIA_ENV_MISMATCH=true
|
||||
fi
|
||||
|
||||
# Removed duplicate video group checks here - consolidated into the earlier checks that include GID
|
||||
|
||||
# Check NVIDIA Container Toolkit support
|
||||
echo "🔍 Checking NVIDIA container runtime support..."
|
||||
|
||||
# More reliable detection of NVIDIA Container Runtime
|
||||
NVIDIA_RUNTIME_ACTIVE=false
|
||||
|
||||
# Method 1: Check for nvidia-container-cli tool
|
||||
if command -v nvidia-container-cli >/dev/null 2>&1; then
|
||||
NVIDIA_RUNTIME_ACTIVE=true
|
||||
echo "✅ NVIDIA Container Runtime detected (nvidia-container-cli found)."
|
||||
|
||||
if nvidia-container-cli info >/dev/null 2>&1; then
|
||||
echo "✅ NVIDIA container runtime is functional."
|
||||
else
|
||||
echo "⚠️ nvidia-container-cli found, but 'info' command failed. Runtime may be misconfigured."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Method 2: Check for NVIDIA Container Runtime specific files
|
||||
if [ -e "/dev/.nv" ] || [ -e "/.nv" ] || [ -e "/.nvidia-container-runtime" ]; then
|
||||
NVIDIA_RUNTIME_ACTIVE=true
|
||||
echo "✅ NVIDIA Container Runtime files detected."
|
||||
fi
|
||||
|
||||
# Method 3: Check cgroup information for NVIDIA
|
||||
if grep -q "nvidia" /proc/self/cgroup 2>/dev/null; then
|
||||
NVIDIA_RUNTIME_ACTIVE=true
|
||||
echo "✅ NVIDIA Container Runtime cgroups detected."
|
||||
fi
|
||||
|
||||
# Final verdict based on hardware AND runtime with improved messaging
|
||||
if [ "$NVIDIA_FOUND" = true ] && ([ "$NVIDIA_RUNTIME_ACTIVE" = true ] || [ "$NVIDIA_CONTAINER_TOOLKIT_FOUND" = true ]); then
|
||||
echo "✅ NVIDIA Container Runtime is properly configured with hardware access."
|
||||
elif [ "$NVIDIA_FOUND" = true ] && [ "$NVIDIA_RUNTIME_ACTIVE" = false ] && [ "$NVIDIA_CONTAINER_TOOLKIT_FOUND" = false ]; then
|
||||
echo "ℹ️ NVIDIA devices accessible via direct passthrough instead of Container Runtime."
|
||||
echo " This works but consider using the 'deploy: resources: reservations: devices:' method in docker-compose."
|
||||
elif [ "$NVIDIA_FOUND" = false ] && [ "$NVIDIA_RUNTIME_ACTIVE" = true ]; then
|
||||
echo "⚠️ NVIDIA Container Runtime appears to be configured, but no NVIDIA devices found."
|
||||
echo " Check that your host has NVIDIA drivers installed and GPUs are properly passed to the container."
|
||||
elif [ "$DRI_DEVICES_FOUND" = true ] && [ "$NVIDIA_GPU_IN_LSPCI" = true ]; then
|
||||
echo "ℹ️ Limited GPU access: Only DRI devices available for NVIDIA hardware."
|
||||
echo " VAAPI acceleration may work but NVENC/CUDA won't be available."
|
||||
echo " For full NVIDIA capabilities, configure the NVIDIA Container Runtime."
|
||||
elif [ "$DRI_DEVICES_FOUND" = true ]; then
|
||||
echo "ℹ️ Using Intel/AMD GPU hardware for acceleration via VAAPI."
|
||||
else
|
||||
echo "⚠️ No GPU acceleration devices detected. CPU-only transcoding will be used."
|
||||
fi
|
||||
|
||||
# Run nvidia-smi if available
|
||||
if command -v nvidia-smi >/dev/null 2>&1; then
|
||||
echo "🔍 Running nvidia-smi to verify GPU visibility..."
|
||||
if nvidia-smi >/dev/null 2>&1; then
|
||||
echo "✅ nvidia-smi successful - GPU is accessible to container!"
|
||||
echo " This confirms hardware acceleration should be available to FFmpeg."
|
||||
else
|
||||
echo "⚠️ nvidia-smi command failed. GPU may not be properly mapped into container."
|
||||
fi
|
||||
else
|
||||
echo "ℹ️ nvidia-smi not installed or not in PATH."
|
||||
fi
|
||||
|
||||
# Show relevant environment variables with contextual suggestions
|
||||
echo "🔍 Checking GPU-related environment variables..."
|
||||
|
||||
# Set flags based on device detection
|
||||
DRI_DEVICES_FOUND=false
|
||||
for dev in /dev/dri/renderD* /dev/dri/card*; do
|
||||
if [ -e "$dev" ];then
|
||||
DRI_DEVICES_FOUND=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Give contextual suggestions based on detected hardware
|
||||
if [ "$DRI_DEVICES_FOUND" = true ]; then
|
||||
# Detect Intel/AMD GPU model - skip this if we already detected GPUs earlier
|
||||
if [ "$NVIDIA_GPU_IN_LSPCI" = false ] && [ "$INTEL_GPU_IN_LSPCI" = false ] && [ "$AMD_GPU_IN_LSPCI" = false ] && command -v lspci >/dev/null 2>&1; then
|
||||
GPU_INFO=$(lspci -nn | grep -i "VGA\|Display" | head -1)
|
||||
if [ -n "$GPU_INFO" ]; then
|
||||
echo "🔍 Detected GPU: $GPU_INFO"
|
||||
# Extract model for cleaner display in summary
|
||||
GPU_MODEL=$(echo "$GPU_INFO" | sed -E 's/.*: (.*) \[.*/\1/' | sed 's/Corporation //' | sed 's/Technologies //')
|
||||
fi
|
||||
else
|
||||
# Use already detected GPU model info
|
||||
if [ "$NVIDIA_GPU_IN_LSPCI" = true ]; then
|
||||
GPU_MODEL=$NVIDIA_MODEL
|
||||
elif [ "$INTEL_GPU_IN_LSPCI" = true ]; then
|
||||
GPU_MODEL=$INTEL_MODEL
|
||||
elif [ "$AMD_GPU_IN_LSPCI" = true ]; then
|
||||
GPU_MODEL=$AMD_MODEL
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$GPU_MODEL" ]; then
|
||||
echo "🔍 GPU model: $GPU_MODEL"
|
||||
fi
|
||||
# Check for LIBVA_DRIVER_NAME environment variable
|
||||
if [ -n "$LIBVA_DRIVER_NAME" ]; then
|
||||
echo "ℹ️ LIBVA_DRIVER_NAME is set to '$LIBVA_DRIVER_NAME'"
|
||||
echo " Note: If you experience issues with hardware acceleration, try removing this"
|
||||
echo " environment variable to let the system auto-detect the appropriate driver."
|
||||
else
|
||||
# Check if we can detect the GPU type
|
||||
if command -v lspci >/dev/null 2>&1; then
|
||||
echo "ℹ️ VAAPI driver auto-detection is usually reliable. Settings below only needed if you experience issues."
|
||||
|
||||
# Create variables to store recommended driver and supported methods
|
||||
INTEL_RECOMMENDED_DRIVER=""
|
||||
INTEL_SUPPORTS_QSV=false
|
||||
|
||||
# Use the Intel model information we already captured
|
||||
if [ "$INTEL_GPU_IN_LSPCI" = true ] && [ -n "$INTEL_MODEL" ]; then
|
||||
# Check for newer Intel generations that use iHD
|
||||
if echo "$INTEL_MODEL" | grep -q -E "Arc|Xe|Alchemist|Tiger|Alder|Raptor|Meteor|Gen1[2-9]"; then
|
||||
echo "💡 Detected Intel GPU that supports iHD (e.g. Gen12+/Arc/Xe)"
|
||||
echo " Recommended: LIBVA_DRIVER_NAME=iHD"
|
||||
echo " Note: Only set this environment variable if hardware acceleration doesn't work by default"
|
||||
INTEL_RECOMMENDED_DRIVER="iHD"
|
||||
INTEL_SUPPORTS_QSV=true
|
||||
elif echo "$INTEL_MODEL" | grep -q -E "Coffee|Whiskey|Comet|Gen11"; then
|
||||
echo "💡 Detected Intel GPU that supports both i965 and iHD (e.g. Gen9.5/Gen11)"
|
||||
echo " Preferred: LIBVA_DRIVER_NAME=iHD"
|
||||
echo " Recommended: Try i965 only if iHD has compatibility issues"
|
||||
echo " Note: Only set this environment variable if hardware acceleration doesn't work by default"
|
||||
INTEL_RECOMMENDED_DRIVER="iHD"
|
||||
INTEL_SUPPORTS_QSV=true
|
||||
elif echo "$INTEL_MODEL" | grep -q -E "Haswell|Broadwell|Skylake|Kaby"; then
|
||||
echo "💡 Detected Intel GPU that supports i965 (e.g. Gen9 and below)"
|
||||
echo " Recommended: Set LIBVA_DRIVER_NAME=i965"
|
||||
echo " Note: Only set this environment variable if hardware acceleration doesn't work by default"
|
||||
INTEL_RECOMMENDED_DRIVER="i965"
|
||||
# Older Intel GPUs support QSV through i965 driver but with more limitations
|
||||
INTEL_SUPPORTS_QSV=false
|
||||
else
|
||||
# Generic Intel case - we're not fully confident in our recommendation
|
||||
echo "💡 Unable to definitively identify Intel GPU generation"
|
||||
echo " Try auto-detection first (no environment variable)"
|
||||
echo " If issues occur: Try LIBVA_DRIVER_NAME=iHD first (newer GPUs)"
|
||||
echo " If that fails: Try LIBVA_DRIVER_NAME=i965 (older GPUs)"
|
||||
INTEL_RECOMMENDED_DRIVER="unknown" # Mark as unknown rather than assuming
|
||||
INTEL_SUPPORTS_QSV="maybe" # Mark as maybe instead of assuming true
|
||||
fi
|
||||
elif [ "$AMD_GPU_IN_LSPCI" = true ]; then
|
||||
echo "💡 If auto-detection fails: Set LIBVA_DRIVER_NAME=radeonsi for AMD GPUs"
|
||||
echo " Note: Only set this environment variable if hardware acceleration doesn't work by default"
|
||||
else
|
||||
echo "ℹ️ Common VAAPI driver options if auto-detection fails:"
|
||||
echo " - For modern Intel GPUs (Gen12+/Arc/Xe): LIBVA_DRIVER_NAME=iHD"
|
||||
echo " - For older Intel GPUs: LIBVA_DRIVER_NAME=i965"
|
||||
echo " - For AMD GPUs: LIBVA_DRIVER_NAME=radeonsi"
|
||||
echo " Note: Only set these environment variables if hardware acceleration doesn't work by default"
|
||||
fi
|
||||
else
|
||||
echo "ℹ️ Intel/AMD GPU detected. Auto-detection should work in most cases."
|
||||
echo " If VAAPI doesn't work, you may need to set LIBVA_DRIVER_NAME manually."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check FFmpeg hardware acceleration support
|
||||
echo "🔍 Checking FFmpeg hardware acceleration capabilities..."
|
||||
if command -v ffmpeg >/dev/null 2>&1; then
|
||||
HWACCEL=$(ffmpeg -hide_banner -hwaccels 2>/dev/null | grep -v "Hardware acceleration methods:" || echo "None found")
|
||||
|
||||
# Initialize variables to store compatible and missing methods
|
||||
COMPATIBLE_METHODS=""
|
||||
MISSING_METHODS=""
|
||||
|
||||
# Format the list of hardware acceleration methods in a more readable way
|
||||
echo "🔍 Available FFmpeg hardware acceleration methods:"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Process the list into a more readable format with relevance indicators
|
||||
if [ -n "$HWACCEL" ] && [ "$HWACCEL" != "None found" ]; then
|
||||
# First, show methods compatible with detected hardware
|
||||
echo " 📌 Compatible with your hardware:"
|
||||
COMPATIBLE_FOUND=false
|
||||
|
||||
for method in $HWACCEL; do
|
||||
# Skip if it's just the header line or empty
|
||||
if [ "$method" = "Hardware" ] || [ -z "$method" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check if this method is relevant to detected hardware
|
||||
IS_COMPATIBLE=false
|
||||
DESCRIPTION=""
|
||||
|
||||
if [ "$NVIDIA_FOUND" = true ] && [[ "$method" =~ ^(cuda|cuvid|nvenc|nvdec)$ ]]; then
|
||||
IS_COMPATIBLE=true
|
||||
DESCRIPTION="NVIDIA GPU acceleration"
|
||||
elif [ "$INTEL_GPU_IN_LSPCI" = true ] && [ "$method" = "qsv" ] && [ "$INTEL_SUPPORTS_QSV" = true ]; then
|
||||
IS_COMPATIBLE=true
|
||||
DESCRIPTION="Intel QuickSync acceleration"
|
||||
elif [ "$method" = "vaapi" ] && (([ "$INTEL_GPU_IN_LSPCI" = true ] || [ "$AMD_GPU_IN_LSPCI" = true ]) && [ "$DRI_DEVICES_FOUND" = true ]); then
|
||||
IS_COMPATIBLE=true
|
||||
if [ "$INTEL_GPU_IN_LSPCI" = true ]; then
|
||||
DESCRIPTION="Intel VAAPI acceleration"
|
||||
else
|
||||
DESCRIPTION="AMD VAAPI acceleration"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Display compatible methods and store for summary
|
||||
if [ "$IS_COMPATIBLE" = true ]; then
|
||||
COMPATIBLE_FOUND=true
|
||||
COMPATIBLE_METHODS="$COMPATIBLE_METHODS $method"
|
||||
echo " ✅ $method - $DESCRIPTION"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$COMPATIBLE_FOUND" = false ]; then
|
||||
echo " ❌ No compatible acceleration methods found for your hardware"
|
||||
fi
|
||||
|
||||
# Then show all other available methods
|
||||
echo " 📌 Other available methods (not compatible with detected hardware):"
|
||||
OTHER_FOUND=false
|
||||
|
||||
for method in $HWACCEL; do
|
||||
# Skip if it's just the header line or empty
|
||||
if [ "$method" = "Hardware" ] || [ -z "$method" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check if this method is relevant to detected hardware
|
||||
IS_COMPATIBLE=false
|
||||
|
||||
if [ "$NVIDIA_FOUND" = true ] && [[ "$method" =~ ^(cuda|cuvid|nvenc|nvdec)$ ]]; then
|
||||
IS_COMPATIBLE=true
|
||||
elif [ "$INTEL_GPU_IN_LSPCI" = true ] && [ "$method" = "qsv" ] && [ "$INTEL_SUPPORTS_QSV" = true ]; then
|
||||
IS_COMPATIBLE=true
|
||||
elif [ "$method" = "vaapi" ] && (([ "$INTEL_GPU_IN_LSPCI" = true ] || [ "$AMD_GPU_IN_LSPCI" = true ]) && [ "$DRI_DEVICES_FOUND" = true ]); then
|
||||
IS_COMPATIBLE=true
|
||||
fi
|
||||
|
||||
# Display other methods that aren't compatible
|
||||
if [ "$IS_COMPATIBLE" = false ]; then
|
||||
OTHER_FOUND=true
|
||||
echo " ℹ️ $method"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$OTHER_FOUND" = false ]; then
|
||||
echo " None"
|
||||
fi
|
||||
|
||||
# Show expected methods that are missing
|
||||
echo " 📌 Missing methods that should be available for your hardware:"
|
||||
MISSING_FOUND=false
|
||||
|
||||
# Check for NVIDIA methods if NVIDIA GPU is detected
|
||||
if [ "$NVIDIA_FOUND" = true ]; then
|
||||
EXPECTED_NVIDIA="cuda" # cuvid nvenc nvdec" keeping these in case future support is added
|
||||
for method in $EXPECTED_NVIDIA; do
|
||||
if ! echo "$HWACCEL" | grep -q "$method"; then
|
||||
MISSING_FOUND=true
|
||||
MISSING_METHODS="$MISSING_METHODS $method"
|
||||
echo " ⚠️ $method - NVIDIA acceleration (missing but should be available)"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Check for Intel methods if Intel GPU is detected
|
||||
if [ "$INTEL_GPU_IN_LSPCI" = true ] && [ "$DRI_DEVICES_FOUND" = true ]; then
|
||||
if [ "$INTEL_SUPPORTS_QSV" = true ] && ! echo "$HWACCEL" | grep -q "qsv"; then
|
||||
MISSING_FOUND=true
|
||||
MISSING_METHODS="$MISSING_METHODS qsv"
|
||||
echo " ⚠️ qsv - Intel QuickSync acceleration (missing but should be available)"
|
||||
fi
|
||||
|
||||
if ! echo "$HWACCEL" | grep -q "vaapi"; then
|
||||
MISSING_FOUND=true
|
||||
MISSING_METHODS="$MISSING_METHODS vaapi"
|
||||
echo " ⚠️ vaapi - Intel VAAPI acceleration (missing but should be available)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for AMD methods if AMD GPU is detected
|
||||
if [ "$AMD_GPU_IN_LSPCI" = true ] && [ "$DRI_DEVICES_FOUND" = true ]; then
|
||||
if ! echo "$HWACCEL" | grep -q "vaapi"; then
|
||||
MISSING_FOUND=true
|
||||
MISSING_METHODS="$MISSING_METHODS vaapi"
|
||||
echo " ⚠️ vaapi - AMD VAAPI acceleration (missing but should be available)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$MISSING_FOUND" = false ]; then
|
||||
echo " None - All expected methods are available"
|
||||
fi
|
||||
else
|
||||
echo " ❌ No hardware acceleration methods found"
|
||||
fi
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Show hardware-appropriate method summary using the already gathered information
|
||||
if [ -n "$COMPATIBLE_METHODS" ]; then
|
||||
echo "✅ Hardware-appropriate acceleration methods available:$COMPATIBLE_METHODS"
|
||||
fi
|
||||
|
||||
# Show missing expected methods
|
||||
if [ -n "$MISSING_METHODS" ]; then
|
||||
echo "⚠️ Expected acceleration methods not found:$MISSING_METHODS"
|
||||
echo " This might indicate missing libraries or improper driver configuration."
|
||||
fi
|
||||
|
||||
# Display specific cases of interest (simplify using previously captured information)
|
||||
if [ "$NVIDIA_FOUND" = true ] && ! echo "$COMPATIBLE_METHODS" | grep -q "cuda\|nvenc\|cuvid"; then
|
||||
echo "⚠️ NVIDIA GPU detected but no NVIDIA acceleration methods available."
|
||||
echo " Ensure ffmpeg is built with NVIDIA support and required libraries are installed."
|
||||
fi
|
||||
|
||||
if (([ "$INTEL_GPU_IN_LSPCI" = true ] || [ "$AMD_GPU_IN_LSPCI" = true ]) &&
|
||||
[ "$DRI_DEVICES_FOUND" = true ] && ! echo "$COMPATIBLE_METHODS" | grep -q "vaapi"); then
|
||||
echo "⚠️ Intel/AMD GPU detected but VAAPI acceleration not available."
|
||||
echo " Ensure ffmpeg is built with VAAPI support and proper drivers are installed."
|
||||
fi
|
||||
else
|
||||
echo "⚠️ FFmpeg not found in PATH."
|
||||
fi
|
||||
|
||||
# Provide a final summary of the hardware acceleration setup
|
||||
echo "📋 ===================== SUMMARY ====================="
|
||||
|
||||
# Identify which GPU type is active and working
|
||||
if [ "$NVIDIA_FOUND" = true ] && (nvidia-smi >/dev/null 2>&1 || [ -n "$NVIDIA_VISIBLE_DEVICES" ]); then
|
||||
if [ -n "$NVIDIA_MODEL" ]; then
|
||||
echo "🔰 NVIDIA GPU: $NVIDIA_MODEL"
|
||||
else
|
||||
echo "🔰 NVIDIA GPU: ACTIVE (model detection unavailable)"
|
||||
echo "ℹ️ Note: GPU model information couldn't be retrieved, but devices are present."
|
||||
echo " This may be due to missing nvidia-smi tool or container limitations."
|
||||
fi
|
||||
|
||||
if [ "$NVIDIA_CONTAINER_TOOLKIT_FOUND" = true ]; then
|
||||
echo "✅ NVIDIA Container Toolkit: CONFIGURED CORRECTLY"
|
||||
elif [ -n "$NVIDIA_VISIBLE_DEVICES" ] && [ -n "$NVIDIA_DRIVER_CAPABILITIES" ]; then
|
||||
echo "✅ NVIDIA Docker configuration: USING MODERN DEPLOYMENT"
|
||||
else
|
||||
echo "⚠️ NVIDIA setup method: DIRECT DEVICE MAPPING (functional but not optimal)"
|
||||
fi
|
||||
|
||||
# Add device accessibility status
|
||||
if [ $NVIDIA_DEVICE_COUNT -gt 0 ]; then
|
||||
if [ $NVIDIA_ACCESS_COUNT -eq $NVIDIA_DEVICE_COUNT ]; then
|
||||
echo "✅ Device access: ALL NVIDIA DEVICES ACCESSIBLE ($NVIDIA_ACCESS_COUNT/$NVIDIA_DEVICE_COUNT)"
|
||||
else
|
||||
echo "⚠️ Device access: LIMITED NVIDIA DEVICE ACCESS ($NVIDIA_ACCESS_COUNT/$NVIDIA_DEVICE_COUNT)"
|
||||
echo " Some hardware acceleration features may not work properly."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Display FFmpeg NVIDIA acceleration methods in more detail
|
||||
if echo "$COMPATIBLE_METHODS" | grep -q "cuda\|nvenc\|cuvid"; then
|
||||
echo "✅ FFmpeg NVIDIA acceleration: AVAILABLE"
|
||||
|
||||
# Show detailed breakdown of available NVIDIA methods
|
||||
NVIDIA_METHODS=$(echo "$COMPATIBLE_METHODS" | grep -o '\(cuda\|cuvid\|nvenc\|nvdec\)')
|
||||
echo " Available NVIDIA methods: $NVIDIA_METHODS"
|
||||
echo " Recommended for: Video transcoding with NVIDIA GPUs"
|
||||
else
|
||||
echo "⚠️ FFmpeg NVIDIA acceleration: NOT DETECTED"
|
||||
if [ -n "$MISSING_METHODS" ]; then
|
||||
echo " Missing methods that should be available: $MISSING_METHODS"
|
||||
fi
|
||||
fi
|
||||
elif [ "$NVIDIA_GPU_IN_LSPCI" = true ] && [ "$DRI_DEVICES_FOUND" = true ]; then
|
||||
# NVIDIA through DRI only (suboptimal but possible)
|
||||
if [ -n "$NVIDIA_MODEL" ]; then
|
||||
echo "🔰 NVIDIA GPU: $NVIDIA_MODEL (SUBOPTIMALLY CONFIGURED)"
|
||||
else
|
||||
echo "🔰 NVIDIA GPU: DETECTED BUT SUBOPTIMALLY CONFIGURED"
|
||||
fi
|
||||
echo "⚠️ Your NVIDIA GPU is only accessible through DRI devices"
|
||||
echo " - VAAPI acceleration may work for some tasks"
|
||||
echo " - NVENC/CUDA acceleration is NOT available"
|
||||
|
||||
# Add device accessibility status
|
||||
if [ $DRI_DEVICE_COUNT -gt 0 ]; then
|
||||
if [ $DRI_ACCESS_COUNT -eq $DRI_DEVICE_COUNT ]; then
|
||||
echo "✅ Device access: ALL DRI DEVICES ACCESSIBLE ($DRI_ACCESS_COUNT/$DRI_DEVICE_COUNT)"
|
||||
echo " VAAPI acceleration should work properly."
|
||||
else
|
||||
echo "⚠️ Device access: LIMITED DRI DEVICE ACCESS ($DRI_ACCESS_COUNT/$DRI_DEVICE_COUNT)"
|
||||
echo " VAAPI acceleration may not work properly."
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "💡 RECOMMENDATION: Use the proper NVIDIA container configuration:"
|
||||
echo " deploy:"
|
||||
echo " resources:"
|
||||
echo " reservations:"
|
||||
echo " devices:"
|
||||
echo " - driver: nvidia"
|
||||
echo " count: all"
|
||||
echo " capabilities: [gpu]"
|
||||
|
||||
if echo "$COMPATIBLE_METHODS" | grep -q "vaapi"; then
|
||||
echo "✅ FFmpeg VAAPI acceleration: AVAILABLE (limited without NVENC)"
|
||||
echo " VAAPI can be used for transcoding, but NVENC/CUDA would be more efficient"
|
||||
else
|
||||
echo "⚠️ FFmpeg VAAPI acceleration: NOT DETECTED"
|
||||
fi
|
||||
elif [ "$DRI_DEVICES_FOUND" = true ]; then
|
||||
# Intel/AMD detection with model if available
|
||||
if [ -n "$GPU_MODEL" ]; then
|
||||
echo "🔰 GPU: $GPU_MODEL"
|
||||
elif [ -n "$LIBVA_DRIVER_NAME" ]; then
|
||||
echo "🔰 ${LIBVA_DRIVER_NAME^^} GPU: ACTIVE"
|
||||
else
|
||||
echo "🔰 INTEL/AMD GPU: ACTIVE (model detection unavailable)"
|
||||
echo "ℹ️ Note: Basic GPU drivers appear to be loaded (device nodes exist), but"
|
||||
echo " couldn't identify specific model. This doesn't necessarily indicate a problem."
|
||||
fi
|
||||
|
||||
# Add device accessibility status
|
||||
if [ $DRI_DEVICE_COUNT -gt 0 ]; then
|
||||
if [ $DRI_ACCESS_COUNT -eq $DRI_DEVICE_COUNT ]; then
|
||||
echo "✅ Device access: ALL DRI DEVICES ACCESSIBLE ($DRI_ACCESS_COUNT/$DRI_DEVICE_COUNT)"
|
||||
echo " VAAPI hardware acceleration should work properly."
|
||||
else
|
||||
echo "⚠️ Device access: LIMITED DRI DEVICE ACCESS ($DRI_ACCESS_COUNT/$DRI_DEVICE_COUNT)"
|
||||
echo " VAAPI hardware acceleration may not work properly."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Display FFmpeg VAAPI acceleration method with more details
|
||||
if echo "$COMPATIBLE_METHODS" | grep -q "vaapi"; then
|
||||
echo "✅ FFmpeg VAAPI acceleration: AVAILABLE"
|
||||
|
||||
# Add recommended usage information
|
||||
echo " Recommended for: General video transcoding with Intel/AMD GPUs"
|
||||
|
||||
# Add recommended driver information for Intel GPUs
|
||||
if [ "$INTEL_GPU_IN_LSPCI" = true ] && [ -n "$INTEL_RECOMMENDED_DRIVER" ]; then
|
||||
if [ "$INTEL_RECOMMENDED_DRIVER" = "unknown" ]; then
|
||||
echo "ℹ️ Uncertain about recommended VAAPI driver for this Intel GPU"
|
||||
echo " Auto-detection should work, but if issues occur try iHD or i965"
|
||||
else
|
||||
echo "ℹ️ Recommended VAAPI driver for this Intel GPU: $INTEL_RECOMMENDED_DRIVER"
|
||||
fi
|
||||
|
||||
if [ "$INTEL_SUPPORTS_QSV" = true ] && echo "$COMPATIBLE_METHODS" | grep -q "qsv"; then
|
||||
echo "✅ QSV acceleration: AVAILABLE"
|
||||
echo " Recommended for: Intel-specific optimized transcoding"
|
||||
echo " Works best with: $INTEL_RECOMMENDED_DRIVER driver"
|
||||
elif [ "$INTEL_SUPPORTS_QSV" = true ]; then
|
||||
echo "ℹ️ QSV acceleration: NOT DETECTED (may be available with proper configuration)"
|
||||
echo " Your Intel GPU supports QSV but it's not available in FFmpeg"
|
||||
echo " Check if FFmpeg is built with QSV support"
|
||||
elif [ "$INTEL_SUPPORTS_QSV" = "maybe" ]; then
|
||||
echo "ℹ️ QSV acceleration: MAY BE AVAILABLE (depends on exact GPU model)"
|
||||
fi
|
||||
elif [ "$AMD_GPU_IN_LSPCI" = true ]; then
|
||||
echo "ℹ️ Recommended VAAPI driver for AMD GPUs: radeonsi"
|
||||
fi
|
||||
else
|
||||
echo "⚠️ FFmpeg VAAPI acceleration: NOT DETECTED"
|
||||
if [ -n "$MISSING_METHODS" ]; then
|
||||
echo " Missing methods that should be available: $MISSING_METHODS"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "❌ NO GPU ACCELERATION DETECTED"
|
||||
echo "⚠️ Hardware acceleration is unavailable or misconfigured"
|
||||
fi
|
||||
|
||||
echo "📋 =================================================="
|
||||
echo "✅ GPU detection script complete."
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -e "/tmp/init" ]; then
|
||||
echo "🚀 Development Mode - Setting up Frontend..."
|
||||
|
||||
# Install Node.js
|
||||
if ! command -v node 2>&1 >/dev/null
|
||||
then
|
||||
echo "=== setting up nodejs ==="
|
||||
curl -sL https://deb.nodesource.com/setup_23.x -o /tmp/nodesource_setup.sh
|
||||
bash /tmp/nodesource_setup.sh
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
nodejs
|
||||
fi
|
||||
|
||||
# Install frontend dependencies
|
||||
cd /app/frontend && npm install
|
||||
# Install Python dependencies using UV
|
||||
cd /app && uv sync --python $UV_PROJECT_ENVIRONMENT/bin/python --no-install-project --no-dev
|
||||
|
||||
# Install debugpy for remote debugging
|
||||
if [ "$DISPATCHARR_DEBUG" = "true" ]; then
|
||||
echo "=== setting up debugpy ==="
|
||||
uv pip install --python $UV_PROJECT_ENVIRONMENT/bin/python debugpy
|
||||
fi
|
||||
|
||||
if [[ "$DISPATCHARR_ENV" = "dev" ]]; then
|
||||
touch /tmp/init
|
||||
fi
|
||||
else
|
||||
echo "Development mode initialization already done. Skipping dev setup."
|
||||
fi
|
||||
Reference in New Issue
Block a user