From 64863fbd89e0dad884a553179cadc536bccbcc41 Mon Sep 17 00:00:00 2001 From: upmcplanetracker <219436948+upmcplanetracker@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:26:35 -0400 Subject: [PATCH 1/9] Update migrate_lib.sh to skip accounts check Removed accounts check from PostgreSQL data migration script. --- scripts/migrate_lib.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/migrate_lib.sh b/scripts/migrate_lib.sh index e4cf282e..2823d993 100644 --- a/scripts/migrate_lib.sh +++ b/scripts/migrate_lib.sh @@ -75,15 +75,15 @@ check_and_migrate() { # Check if PostgreSQL already has data (check multiple tables, not just metadata_items) local pg_count=$(psql -t -c "SELECT COUNT(*) FROM $PG_SCHEMA.metadata_items;" 2>/dev/null | tr -d ' ' || echo "0") local pg_sections=$(psql -t -c "SELECT COUNT(*) FROM $PG_SCHEMA.library_sections;" 2>/dev/null | tr -d ' ' || echo "0") - local pg_accounts=$(psql -t -c "SELECT COUNT(*) FROM $PG_SCHEMA.accounts;" 2>/dev/null | tr -d ' ' || echo "0") + # Do NOT check accounts – it's always created as bootstrap data. local pg_has_data=0 - if [[ "$pg_count" -gt 0 ]] || [[ "$pg_sections" -gt 0 ]] || [[ "$pg_accounts" -gt 0 ]]; then + if [[ "$pg_count" -gt 0 ]] || [[ "$pg_sections" -gt 0 ]]; then pg_has_data=1 fi if [[ "$pg_has_data" -eq 1 ]]; then - echo -e "${YELLOW}PostgreSQL already has data (metadata_items=$pg_count, library_sections=$pg_sections, accounts=$pg_accounts).${NC}" - + echo -e "${YELLOW}PostgreSQL already has data (metadata_items=$pg_count, library_sections=$pg_sections).${NC}" + if [[ "$MIGRATION_INTERACTIVE" == "1" ]]; then echo "" echo "Options:" From 039b3d26aa191e96dac81ef1b36fa549f3da30f1 Mon Sep 17 00:00:00 2001 From: upmcplanetracker <219436948+upmcplanetracker@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:33:41 -0400 Subject: [PATCH 2/9] Add GitHub Actions workflow for debug image build This workflow builds and pushes a debug image with optional sanitizer types and creates a multi-architecture manifest. --- .github/workflows/build-debug.yml | 95 +++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 .github/workflows/build-debug.yml diff --git a/.github/workflows/build-debug.yml b/.github/workflows/build-debug.yml new file mode 100644 index 00000000..efb9c3ee --- /dev/null +++ b/.github/workflows/build-debug.yml @@ -0,0 +1,95 @@ +name: Build Debug / Sanitizer Image + +on: + workflow_dispatch: + inputs: + sanitizer: + description: 'Sanitizer type (address, thread, or empty for debug symbols only)' + required: false + default: 'address' + type: choice + options: + - address + - thread + - '' + +jobs: + build-debug: + name: Build Debug / Sanitizer (${{ matrix.name }} / ${{ matrix.arch }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - name: linuxserver + dockerfile: Dockerfile + image: ghcr.io/${{ github.repository_owner }}/plex-postgresql-debug + arch: amd64 + platform: linux/amd64 + runner: ubuntu-latest + - name: linuxserver + dockerfile: Dockerfile + image: ghcr.io/${{ github.repository_owner }}/plex-postgresql-debug + arch: arm64 + platform: linux/arm64 + runner: ubuntu-24.04-arm + # Optionally add plexinc variant if you use it + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push debug image + uses: docker/build-push-action@v6 + with: + context: . + file: ${{ matrix.dockerfile }} + platforms: ${{ matrix.platform }} + push: true + provenance: false + tags: | + ${{ matrix.image }}:${{ github.sha }}-${{ matrix.arch }} + ${{ matrix.image }}:latest-${{ matrix.arch }} + build-args: | + PLEX_PG_SANITIZE=${{ inputs.sanitizer }} + # Optionally add other debug flags via environment variables if needed + + manifest-debug: + name: Create multi-arch manifest for debug + runs-on: ubuntu-latest + needs: build-debug + if: always() + steps: + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create and push manifest + run: | + set -eux + IMAGE="ghcr.io/${{ github.repository_owner }}/plex-postgresql-debug" + SHA="${{ github.sha }}" + docker manifest create "${IMAGE}:latest" \ + "${IMAGE}:${SHA}-amd64" \ + "${IMAGE}:${SHA}-arm64" + docker manifest push "${IMAGE}:latest" + # Also tag with sanitizer type if needed + if [ -n "${{ inputs.sanitizer }}" ]; then + docker manifest create "${IMAGE}:${{ inputs.sanitizer }}" \ + "${IMAGE}:${SHA}-amd64" \ + "${IMAGE}:${SHA}-arm64" + docker manifest push "${IMAGE}:${{ inputs.sanitizer }}" + fi From 14dc8298cd8eb063c1607438f638806adacf56df Mon Sep 17 00:00:00 2001 From: upmcplanetracker <219436948+upmcplanetracker@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:37:33 -0400 Subject: [PATCH 3/9] Remove ARM64 configuration from build-debug.yml Removed ARM64 build configuration from the workflow. --- .github/workflows/build-debug.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-debug.yml b/.github/workflows/build-debug.yml index efb9c3ee..3c26429b 100644 --- a/.github/workflows/build-debug.yml +++ b/.github/workflows/build-debug.yml @@ -27,12 +27,6 @@ jobs: arch: amd64 platform: linux/amd64 runner: ubuntu-latest - - name: linuxserver - dockerfile: Dockerfile - image: ghcr.io/${{ github.repository_owner }}/plex-postgresql-debug - arch: arm64 - platform: linux/arm64 - runner: ubuntu-24.04-arm # Optionally add plexinc variant if you use it steps: @@ -83,13 +77,11 @@ jobs: IMAGE="ghcr.io/${{ github.repository_owner }}/plex-postgresql-debug" SHA="${{ github.sha }}" docker manifest create "${IMAGE}:latest" \ - "${IMAGE}:${SHA}-amd64" \ - "${IMAGE}:${SHA}-arm64" + "${IMAGE}:${SHA}-amd64" docker manifest push "${IMAGE}:latest" # Also tag with sanitizer type if needed if [ -n "${{ inputs.sanitizer }}" ]; then docker manifest create "${IMAGE}:${{ inputs.sanitizer }}" \ - "${IMAGE}:${SHA}-amd64" \ - "${IMAGE}:${SHA}-arm64" + "${IMAGE}:${SHA}-amd64" docker manifest push "${IMAGE}:${{ inputs.sanitizer }}" fi From 4369d61f211b192582d032aab82a81db7a8f7488 Mon Sep 17 00:00:00 2001 From: upmcplanetracker <219436948+upmcplanetracker@users.noreply.github.com> Date: Sat, 29 Aug 2026 13:08:18 -0400 Subject: [PATCH 4/9] Implement conditional sanitizer library installation Add conditional installation of sanitizer libraries for GCC based on build argument. --- Dockerfile | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Dockerfile b/Dockerfile index a4216729..beb61762 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,6 +15,14 @@ RUN apk add --no-cache \ curl \ perl +# Conditionally install sanitizer libraries for GCC based on build arg +ARG PLEX_PG_SANITIZE +RUN if [ "$PLEX_PG_SANITIZE" = "address" ]; then \ + apk add --no-cache libasan; \ + elif [ "$PLEX_PG_SANITIZE" = "thread" ]; then \ + apk add --no-cache libtsan; \ + fi + # Verify musl version matches Plex (1.2.2) RUN /lib/ld-musl-*.so.1 --version 2>&1 | head -2 @@ -52,11 +60,15 @@ FROM linuxserver/plex:latest # Install PostgreSQL client for health checks, sqlite3 for schema fixes, # python3 for data migration, gdb for debugging +# Install runtime tools and conditional sanitizer runtime libraries +ARG PLEX_PG_SANITIZE RUN apt-get update && apt-get install -y --no-install-recommends \ postgresql-client \ sqlite3 \ python3 \ gdb \ + $(if [ "$PLEX_PG_SANITIZE" = "address" ]; then echo "libasan6"; fi) \ + $(if [ "$PLEX_PG_SANITIZE" = "thread" ]; then echo "libtsan0"; fi) \ && rm -rf /var/lib/apt/lists/* # NOTE: Do NOT set LANG/LC_ALL/CHARSET here — Plex's bundled musl+boost::locale From 92165eae54b5ae99a75f4627d3a3ccf82d8ce340 Mon Sep 17 00:00:00 2001 From: upmcplanetracker <219436948+upmcplanetracker@users.noreply.github.com> Date: Sat, 29 Aug 2026 13:12:17 -0400 Subject: [PATCH 5/9] Improve sanitizer library handling in Dockerfile Refactor sanitizer library installation and update runtime libraries for PostgreSQL client. --- Dockerfile | 55 ++++++++---------------------------------------------- 1 file changed, 8 insertions(+), 47 deletions(-) diff --git a/Dockerfile b/Dockerfile index beb61762..8f4de047 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,12 +15,9 @@ RUN apk add --no-cache \ curl \ perl -# Conditionally install sanitizer libraries for GCC based on build arg -ARG PLEX_PG_SANITIZE -RUN if [ "$PLEX_PG_SANITIZE" = "address" ]; then \ - apk add --no-cache libasan; \ - elif [ "$PLEX_PG_SANITIZE" = "thread" ]; then \ - apk add --no-cache libtsan; \ +# Conditionally install sanitizer static runtime support in Alpine +RUN if [ "$PLEX_PG_SANITIZE" = "address" ] || [ "$PLEX_PG_SANITIZE" = "thread" ]; then \ + apk add --no-cache compiler-rt-static || true; \ fi # Verify musl version matches Plex (1.2.2) @@ -59,16 +56,15 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \ FROM linuxserver/plex:latest # Install PostgreSQL client for health checks, sqlite3 for schema fixes, -# python3 for data migration, gdb for debugging -# Install runtime tools and conditional sanitizer runtime libraries +# python3 for data migration, gdb for debugging, and conditional sanitizer runtime libraries ARG PLEX_PG_SANITIZE RUN apt-get update && apt-get install -y --no-install-recommends \ postgresql-client \ sqlite3 \ python3 \ gdb \ - $(if [ "$PLEX_PG_SANITIZE" = "address" ]; then echo "libasan6"; fi) \ - $(if [ "$PLEX_PG_SANITIZE" = "thread" ]; then echo "libtsan0"; fi) \ + $(if [ "$PLEX_PG_SANITIZE" = "address" ]; then echo "libasan8"; fi) \ + $(if [ "$PLEX_PG_SANITIZE" = "thread" ]; then echo "libtsan2"; fi) \ && rm -rf /var/lib/apt/lists/* # NOTE: Do NOT set LANG/LC_ALL/CHARSET here — Plex's bundled musl+boost::locale @@ -130,54 +126,19 @@ RUN if [ -f /etc/s6-overlay/s6-rc.d/init-plex-claim/run ]; then \ echo "Patched init-plex-claim for PostgreSQL shim"; \ fi -# Keep upstream CrashUploader binary. -# With SIGCHLD forced to SIG_IGN, child exits should no longer destabilize Plex. - # s6 finish script — defense-in-depth for the BindAddrInUseException crash loop. -# -# PRIMARY FIX: PLEX_PG_SUPPRESS_DAEMON=1 injected below keeps PMS in the -# foreground so s6 never sees the run script exit during normal startup. -# This finish script is a safety net for the case where daemon suppression is -# disabled (PLEX_PG_SUPPRESS_DAEMON=0) or fails. -# -# HOW THE CRASH LOOP WORKS WITHOUT THE PRIMARY FIX: -# PMS calls daemon() → fork() → parent exits → s6 sees its watched PID exit -# → s6 runs finish + restarts → new PMS tries to bind 32400 → the re-exec'd -# child from the previous cycle still holds 32400 → BindAddrInUseException -# → SIGABRT → loop ~50 times. -# -# WHY THE OLD FINISH SCRIPT DID NOT WORK: -# s6-overlay v3 kills the finish script after S6_KILL_FINISH_MAXTIME ms -# (default 5000ms). The old script's `while pgrep ... do sleep 5; done` -# loop is killed on its first iteration. Also, `nc -z localhost 32400` -# races — the re-exec'd child may not have bound 32400 yet. -# -# THIS finish script sets a 30-second timeout file and polls at 1s intervals -# so s6 does not kill it before it can detect the child. It exits 125 to -# signal s6 that it should not restart immediately (s6-overlay v3: exit codes -# >= 125 in the finish script suppress the automatic restart). RUN printf '#!/bin/bash\n# Exit code 125 tells s6-supervise not to restart the service.\n# See: https://skarnet.org/software/s6/s6-supervise.html\nexit_code=${1:-0}\nif [ "${exit_code}" = "0" ]; then\n deadline=30\n elapsed=0\n while [ $elapsed -lt $deadline ]; do\n if pgrep -x "Plex Media Server" >/dev/null 2>&1; then\n echo "[plex-pg] PMS re-exec child still running (${elapsed}s), suppressing restart"\n sleep 1\n elapsed=$((elapsed+1))\n else\n break\n fi\n done\n if pgrep -x "Plex Media Server" >/dev/null 2>&1; then\n echo "[plex-pg] PMS child still alive after ${deadline}s — suppressing restart, s6 will retry"\n exit 125\n fi\nfi\n' \ > /etc/s6-overlay/s6-rc.d/svc-plex/finish && \ chmod +x /etc/s6-overlay/s6-rc.d/svc-plex/finish && \ printf '30000\n' > /etc/s6-overlay/s6-rc.d/svc-plex/finish-timeout -# Inject shim env into the upstream svc-plex run script and wrap PMS -# with subreaper to prevent the BindAddrInUseException crash loop. -# -# PMS does vfork+execve to re-exec itself during startup: the parent exits -# while the child takes over on port 32400. s6 watches the parent PID, sees -# it exit, and immediately restarts PMS — but the child still holds port -# 32400, causing BindAddrInUseException → SIGABRT in a crash loop. -# -# FIX: subreaper sets PR_SET_CHILD_SUBREAPER, so the re-exec'd child is -# reparented to subreaper (not PID 1). subreaper waits for ALL descendants -# before exiting — s6 never sees a premature death. +# Inject shim env into the upstream svc-plex run script and wrap PMS with subreaper RUN sed -i '/export PLEX_MEDIA_SERVER_INFO_PLATFORM_VERSION/a\ arch="$(uname -m)"\ \nif [[ "$arch" == "aarch64" || "$arch" == "arm64" ]]; then\ \n export OPENSSL_armcap="${PLEX_PG_OPENSSL_ARMCAP:-0}"\ \nfi\ -\nexport LD_LIBRARY_PATH="/usr/lib/plexmediaserver/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"\ +\nexport LD_LIBRARY_PATH="/usr/local/lib/plex-postgresql:/usr/lib/plexmediaserver/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"\ \nexport LD_PRELOAD="/usr/local/lib/plex-postgresql/db_interpose_pg.so"\ ' /etc/s6-overlay/s6-rc.d/svc-plex/run && \ sed -i 's|"/usr/lib/plexmediaserver/Plex Media Server"|/usr/local/bin/subreaper "/usr/lib/plexmediaserver/Plex Media Server"|g' \ From 9ae38ee7babd3e68da7ee9cac89b25ecebd0b806 Mon Sep 17 00:00:00 2001 From: upmcplanetracker <219436948+upmcplanetracker@users.noreply.github.com> Date: Sat, 29 Aug 2026 13:23:11 -0400 Subject: [PATCH 6/9] Add gcc-sanitizers to build dependencies in Dockerfile --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8f4de047..e8b7d2b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,7 @@ ENV PLEX_PG_SANITIZE=${PLEX_PG_SANITIZE} # Install build dependencies RUN apk add --no-cache \ build-base \ + gcc-sanitizers \ sqlite-dev \ linux-headers \ curl \ @@ -17,7 +18,7 @@ RUN apk add --no-cache \ # Conditionally install sanitizer static runtime support in Alpine RUN if [ "$PLEX_PG_SANITIZE" = "address" ] || [ "$PLEX_PG_SANITIZE" = "thread" ]; then \ - apk add --no-cache compiler-rt-static || true; \ + apk add --no-cache gcc-sanitizers compiler-rt-static || true; \ fi # Verify musl version matches Plex (1.2.2) From 320ff7651951bb9c300362c3e03ee946b3e1beaf Mon Sep 17 00:00:00 2001 From: upmcplanetracker <219436948+upmcplanetracker@users.noreply.github.com> Date: Sat, 29 Aug 2026 13:27:22 -0400 Subject: [PATCH 7/9] Refactor Dockerfile comments and sanitizer dependencies Updated Dockerfile to clarify comments and modify sanitizer installation. --- Dockerfile | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index e8b7d2b1..9f7e50ce 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,23 +7,21 @@ FROM alpine:3.15 AS builder ARG PLEX_PG_SANITIZE ENV PLEX_PG_SANITIZE=${PLEX_PG_SANITIZE} -# Install build dependencies +# Install build dependencies – includes sanitizer runtime libraries RUN apk add --no-cache \ build-base \ - gcc-sanitizers \ + libasan \ + libtsan \ sqlite-dev \ linux-headers \ curl \ perl -# Conditionally install sanitizer static runtime support in Alpine -RUN if [ "$PLEX_PG_SANITIZE" = "address" ] || [ "$PLEX_PG_SANITIZE" = "thread" ]; then \ - apk add --no-cache gcc-sanitizers compiler-rt-static || true; \ - fi - # Verify musl version matches Plex (1.2.2) RUN /lib/ld-musl-*.so.1 --version 2>&1 | head -2 +# ... rest of builder stage unchanged ... + WORKDIR /build # Install Rust toolchain From a91b3759851410a29bc81cfcbbd613eaf4a5a147 Mon Sep 17 00:00:00 2001 From: upmcplanetracker <219436948+upmcplanetracker@users.noreply.github.com> Date: Sat, 29 Aug 2026 13:29:43 -0400 Subject: [PATCH 8/9] Remove libasan and libtsan from Dockerfile Removed unnecessary sanitizer libraries from build dependencies. --- Dockerfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9f7e50ce..51f65e1a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,8 +10,6 @@ ENV PLEX_PG_SANITIZE=${PLEX_PG_SANITIZE} # Install build dependencies – includes sanitizer runtime libraries RUN apk add --no-cache \ build-base \ - libasan \ - libtsan \ sqlite-dev \ linux-headers \ curl \ From 874a17faadedae4966bb12107b7755e190eaba36 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 31 Aug 2026 22:43:11 +0000 Subject: [PATCH 9/9] chore: bump version to v1.3.15 for upstream updates --- .github/upstream-digests.json | 2 +- CHANGELOG.md | 5 +++++ VERSION | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/upstream-digests.json b/.github/upstream-digests.json index 6df9f78e..31dd952b 100644 --- a/.github/upstream-digests.json +++ b/.github/upstream-digests.json @@ -1,4 +1,4 @@ { - "linuxserver/plex:latest": "sha256:f6c58cb2f5e41cd1397bf2ed4e61ef63bd86e0736841b3ef426fabfe04606293", + "linuxserver/plex:latest": "sha256:7f9a1d574958fc2f177c14ca190d4b811a58c274477f5bae8fb44ee676fb96bf", "plexinc/pms-docker:latest": "sha256:83a425ae9e133b1cb2cc3b809556e01c61cd8ff65c582e41b4374bc2210bac9e" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 426b03bb..969d31b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.3.15] - 2026-08-31 + +### Changed +- Updated upstream base Docker images (linuxserver/plex:latest / plexinc/pms-docker:latest). + ## [1.3.14] - 2026-08-18 ### Changed diff --git a/VERSION b/VERSION index 085c0f26..5bdcf5c3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3.14 +1.3.15