Files
pgsql-jellyfin/docs/LINUX_PACKAGE_BUILD_GUIDE.md
T
wjones 8914f4dce9 Add SQL query patterns documentation and Linux package build scripts
- Created `TV_SHOWS_SQL_QUERY_PATTERNS.md` to document SQL query patterns for TV shows, including performance issues and missing indexes.
- Added `README.md` for Linux package building, detailing steps for creating Debian and Red Hat packages.
- Implemented build scripts for Debian and Red Hat, including service files and post-installation hooks.
- Added necessary scripts for managing Jellyfin service lifecycle on both Debian and Red Hat systems.
- Included package specifications and installation instructions for both distributions.
2026-07-14 10:32:56 -04:00

666 lines
16 KiB
Markdown

# Linux Package Build Guide
Complete documentation for building Debian (`.deb`) and Red Hat (`.rpm`) packages for Jellyfin using the current publishing parameters.
---
## Table of Contents
1. [Common Publishing Setup](#common-publishing-setup)
2. [Debian Package Build](#debian-package-build)
3. [Red Hat Package Build](#red-hat-package-build)
4. [Build Scripts](#build-scripts)
5. [Testing & Verification](#testing--verification)
---
## Common Publishing Setup
All package formats use the same .NET publishing parameters:
- **Runtime**: `linux-x64`
- **Configuration**: `Release`
- **Self-contained**: `true` (includes .NET runtime)
- **Output Directory**: `/opt/jellyfin` (standard FHS location)
### Prerequisites (All Distributions)
```bash
# Required for building
- .NET SDK (for publishing)
- Build tools (gcc, make, etc.)
# Package-specific requirements
Debian: ruby-dev, gem, fpm
Red Hat: rpm-build, ruby-devel, gem, fpm
```
### Step 1: Publish the Application
This step is identical for both Debian and Red Hat:
```bash
cd /home/wjones/projects/pgsql-jellyfin
# Restore dependencies
dotnet restore Jellyfin.sln -r linux-x64
# Build the solution
dotnet build Jellyfin.sln --configuration Release
# Publish for Linux x64 (self-contained)
dotnet publish Jellyfin.Server/Jellyfin.Server.csproj \
--configuration Release \
--self-contained true \
--runtime linux-x64 \
--output /tmp/jellyfin-publish
```
The published artifacts will be in `/tmp/jellyfin-publish/`.
---
## Debian Package Build
### Method 1: Using FPM (Recommended - Quickest)
FPM handles all the Debian metadata automatically.
#### Prerequisites
```bash
sudo apt-get install ruby-dev
sudo gem install fpm
```
#### Build the Package
```bash
#!/bin/bash
set -e
# Configuration
VERSION="11.0.0" # Get from SharedVersion.cs
PUBLISH_DIR="/tmp/jellyfin-publish"
PACKAGE_OUTPUT="jellyfin-${VERSION}-amd64.deb"
# Build FPM package
fpm \
-s dir \
-t deb \
-n jellyfin \
-v "$VERSION" \
--architecture x86_64 \
--description "Jellyfin Media Server - Free Software Media System" \
--url "https://jellyfin.org" \
--license "GPL-2.0" \
--maintainer "Jellyfin Team <jellyfin@jellyfin.org>" \
--depends "libssl3" \
--depends "libicu72" \
--depends "libfontconfig1" \
--after-install scripts/debian/postinst.sh \
--before-remove scripts/debian/prerm.sh \
--after-remove scripts/debian/postrm.sh \
-C "$PUBLISH_DIR" \
-p "$PACKAGE_OUTPUT" \
opt/=opt/jellyfin
echo "✓ Package created: $PACKAGE_OUTPUT"
```
### Method 2: Manual dpkg-deb (Complete Control)
For custom Debian configurations.
#### Create Package Structure
```bash
#!/bin/bash
set -e
VERSION="11.0.0"
PUBLISH_DIR="/tmp/jellyfin-publish"
PKG_DIR="jellyfin-${VERSION}-deb"
# Create directory structure
mkdir -p "$PKG_DIR/DEBIAN"
mkdir -p "$PKG_DIR/opt/jellyfin"
mkdir -p "$PKG_DIR/etc/systemd/system"
mkdir -p "$PKG_DIR/etc/jellyfin"
mkdir -p "$PKG_DIR/var/lib/jellyfin"
mkdir -p "$PKG_DIR/var/log/jellyfin"
# Copy application files
cp -r "$PUBLISH_DIR"/* "$PKG_DIR/opt/jellyfin/"
# Create control file
cat > "$PKG_DIR/DEBIAN/control" << 'EOF'
Package: jellyfin
Version: VERSION_PLACEHOLDER
Architecture: amd64
Installed-Size: $(du -s "$PKG_DIR" | cut -f1)
Depends: libssl3, libicu72, libfontconfig1
Recommends: ffmpeg
Maintainer: Jellyfin Team <jellyfin@jellyfin.org>
Homepage: https://jellyfin.org
Description: Jellyfin Media Server
Jellyfin is a Free Software Media System that puts you in control of managing
and streaming your media. Run the Jellyfin server and access it from a web
browser, mobile app, media player, or other client application.
EOF
sed -i "s/VERSION_PLACEHOLDER/$VERSION/g" "$PKG_DIR/DEBIAN/control"
# Create package
dpkg-deb --build "$PKG_DIR" "jellyfin-${VERSION}-amd64.deb"
```
#### Post-Install Script (`scripts/debian/postinst.sh`)
```bash
#!/bin/bash
set -e
# Create jellyfin user/group
if ! id "jellyfin" &>/dev/null; then
useradd -r -s /bin/false -d /var/lib/jellyfin jellyfin
fi
# Set permissions
chown -R jellyfin:jellyfin /opt/jellyfin
chown -R jellyfin:jellyfin /var/lib/jellyfin
chown -R jellyfin:jellyfin /var/log/jellyfin
# Set directory permissions
chmod 755 /opt/jellyfin
chmod 750 /var/lib/jellyfin
chmod 750 /var/log/jellyfin
# Enable and start service (if systemd is available)
if command -v systemctl &> /dev/null; then
systemctl daemon-reload || true
systemctl enable jellyfin || true
systemctl start jellyfin || true
fi
```
#### Pre-Remove Script (`scripts/debian/prerm.sh`)
```bash
#!/bin/bash
set -e
# Stop service
if command -v systemctl &> /dev/null; then
systemctl stop jellyfin || true
systemctl disable jellyfin || true
fi
```
#### Post-Remove Script (`scripts/debian/postrm.sh`)
```bash
#!/bin/bash
# Remove user/group
userdel jellyfin || true
```
#### Systemd Service File (`scripts/debian/jellyfin.service`)
Create `/etc/systemd/system/jellyfin.service`:
```ini
[Unit]
Description=Jellyfin Media Server
After=network.target
[Service]
Type=simple
User=jellyfin
Group=jellyfin
WorkingDirectory=/var/lib/jellyfin
ExecStart=/opt/jellyfin/jellyfin \
--datadir=/var/lib/jellyfin \
--logdir=/var/log/jellyfin
Restart=on-failure
RestartSec=5s
# Resource limits
LimitNOFILE=65536
MemoryMax=4G
[Install]
WantedBy=multi-user.target
EOF
```
### Install Debian Package
```bash
# Install
sudo dpkg -i jellyfin-11.0.0-amd64.deb
sudo apt-get install -f # Fix any dependency issues
# Verify
sudo systemctl status jellyfin
sudo journalctl -u jellyfin -n 50
```
---
## Red Hat Package Build
### Method 1: Using FPM (Recommended - Cross-Compatible)
FPM works on any Linux distribution to create RPM packages.
#### Prerequisites
```bash
# On Debian/Ubuntu (cross-build)
sudo apt-get install ruby-dev
sudo gem install fpm
# OR on Red Hat/CentOS/Fedora (native)
sudo dnf install ruby-devel
sudo gem install fpm
```
#### Build the Package
```bash
#!/bin/bash
set -e
# Configuration
VERSION="11.0.0"
PUBLISH_DIR="/tmp/jellyfin-publish"
PACKAGE_OUTPUT="jellyfin-${VERSION}-1.x86_64.rpm"
# Build FPM package
fpm \
-s dir \
-t rpm \
-n jellyfin \
-v "$VERSION" \
--architecture x86_64 \
--description "Jellyfin Media Server - Free Software Media System" \
--url "https://jellyfin.org" \
--license "GPL-2.0" \
--maintainer "Jellyfin Team <jellyfin@jellyfin.org>" \
--depends "openssl-libs" \
--depends "libicu" \
--depends "fontconfig" \
--depends "systemd" \
--after-install scripts/redhat/post.sh \
--before-remove scripts/redhat/preun.sh \
--after-remove scripts/redhat/postun.sh \
-C "$PUBLISH_DIR" \
-p "$PACKAGE_OUTPUT" \
opt/=opt/jellyfin
echo "✓ Package created: $PACKAGE_OUTPUT"
```
### Method 2: Using rpmbuild (Red Hat Native)
For building on Red Hat systems with native tooling.
#### Create SPEC File (`jellyfin.spec`)
```spec
Name: jellyfin
Version: 11.0.0
Release: 1%{?dist}
Summary: Jellyfin Media Server
License: GPL-2.0
URL: https://jellyfin.org
Source0: %{name}-%{version}.tar.gz
BuildRequires: rpm-build
Requires: openssl-libs libicu fontconfig systemd
%description
Jellyfin is a Free Software Media System that puts you in control of managing
and streaming your media. Run the Jellyfin server and access it from a web
browser, mobile app, media player, or other client application.
%prep
%setup -q
%build
# No build needed - we're packaging pre-built artifacts
%install
mkdir -p %{buildroot}/opt/jellyfin
cp -r * %{buildroot}/opt/jellyfin/
mkdir -p %{buildroot}/etc/systemd/system
mkdir -p %{buildroot}/var/lib/jellyfin
mkdir -p %{buildroot}/var/log/jellyfin
%pre
# Create jellyfin user/group
if ! id "jellyfin" &>/dev/null; then
useradd -r -s /bin/false -d /var/lib/jellyfin jellyfin
fi
%post
# Set permissions
chown -R jellyfin:jellyfin /opt/jellyfin
chown -R jellyfin:jellyfin /var/lib/jellyfin
chown -R jellyfin:jellyfin /var/log/jellyfin
chmod 755 /opt/jellyfin
chmod 750 /var/lib/jellyfin
chmod 750 /var/log/jellyfin
# Enable and start service
systemctl daemon-reload || true
systemctl enable jellyfin || true
systemctl start jellyfin || true
%preun
# Stop service before uninstall
systemctl stop jellyfin || true
systemctl disable jellyfin || true
%postun
# Clean up
userdel jellyfin || true
%files
/opt/jellyfin
%config(noreplace) /etc/jellyfin
%dir /var/lib/jellyfin
%dir /var/log/jellyfin
%changelog
* Sun Jul 13 2026 Jellyfin Team <jellyfin@jellyfin.org> - 11.0.0-1
- Initial release
```
#### Build RPM with rpmbuild
```bash
#!/bin/bash
set -e
VERSION="11.0.0"
PUBLISH_DIR="/tmp/jellyfin-publish"
# Create tarball of published artifacts
tar -czf /tmp/jellyfin-${VERSION}.tar.gz -C "$PUBLISH_DIR" .
# Set up rpmbuild structure
mkdir -p ~/rpmbuild/{SOURCES,SPECS,BUILD,RPMS,SRPMS}
cp /tmp/jellyfin-${VERSION}.tar.gz ~/rpmbuild/SOURCES/
cp jellyfin.spec ~/rpmbuild/SPECS/
# Build the RPM
rpmbuild -ba ~/rpmbuild/SPECS/jellyfin.spec
# Package location
echo "✓ Package created: ~/rpmbuild/RPMS/x86_64/jellyfin-${VERSION}-1.x86_64.rpm"
```
### Red Hat Service Files (`scripts/redhat/`)
#### Post-Install Script (`post.sh`)
```bash
#!/bin/bash
set -e
# Create jellyfin user/group
if ! id "jellyfin" &>/dev/null; then
useradd -r -s /bin/false -d /var/lib/jellyfin jellyfin
fi
# Set permissions
chown -R jellyfin:jellyfin /opt/jellyfin
chown -R jellyfin:jellyfin /var/lib/jellyfin
chown -R jellyfin:jellyfin /var/log/jellyfin
chmod 755 /opt/jellyfin
chmod 750 /var/lib/jellyfin
chmod 750 /var/log/jellyfin
# Enable and start service
systemctl daemon-reload || true
systemctl enable jellyfin || true
systemctl start jellyfin || true
```
#### Pre-Uninstall Script (`preun.sh`)
```bash
#!/bin/bash
set -e
# Stop service
systemctl stop jellyfin || true
systemctl disable jellyfin || true
```
#### Post-Uninstall Script (`postun.sh`)
```bash
#!/bin/bash
# Clean up user/group
userdel jellyfin || true
```
#### Systemd Service File (`jellyfin.service`)
```ini
[Unit]
Description=Jellyfin Media Server
After=network.target
[Service]
Type=simple
User=jellyfin
Group=jellyfin
WorkingDirectory=/var/lib/jellyfin
ExecStart=/opt/jellyfin/jellyfin \
--datadir=/var/lib/jellyfin \
--logdir=/var/log/jellyfin
Restart=on-failure
RestartSec=5s
# Resource limits
LimitNOFILE=65536
MemoryMax=4G
[Install]
WantedBy=multi-user.target
```
### Install Red Hat Package
```bash
# Install
sudo dnf install jellyfin-11.0.0-1.x86_64.rpm
# Verify
sudo systemctl status jellyfin
sudo journalctl -u jellyfin -n 50
```
---
## Build Scripts
### Complete Build Automation (`build-linux-packages.sh`)
Cross-distribution build script that creates both Debian and Red Hat packages:
```bash
#!/bin/bash
set -e
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
VERSION=$(grep -oP 'Version\s*=\s*"\K[^"]+' "$PROJECT_ROOT/SharedVersion.cs")
PUBLISH_DIR="/tmp/jellyfin-publish-$$"
BUILD_OUTPUT_DIR="${PROJECT_ROOT}/build/packages"
echo "╔═══════════════════════════════════════════════════════════════╗"
echo "║ Jellyfin Linux Package Builder ║"
echo "║ Version: $VERSION ║"
echo "╚═══════════════════════════════════════════════════════════════╝"
echo
# Step 1: Publish the application
echo "[1/4] Publishing application..."
cd "$PROJECT_ROOT"
dotnet restore Jellyfin.sln -r linux-x64 > /dev/null
dotnet build Jellyfin.sln --configuration Release > /dev/null
dotnet publish Jellyfin.Server/Jellyfin.Server.csproj \
--configuration Release \
--self-contained true \
--runtime linux-x64 \
--output "$PUBLISH_DIR" > /dev/null
echo " ✓ Published to $PUBLISH_DIR"
echo
# Step 2: Create build output directory
mkdir -p "$BUILD_OUTPUT_DIR"
# Step 3: Build Debian package
if command -v fpm &> /dev/null; then
echo "[2/4] Building Debian package..."
fpm \
-s dir \
-t deb \
-n jellyfin \
-v "$VERSION" \
--architecture x86_64 \
--description "Jellyfin Media Server" \
--url "https://jellyfin.org" \
--license "GPL-2.0" \
--maintainer "Jellyfin Team" \
--depends "libssl3" \
--depends "libicu72" \
--depends "libfontconfig1" \
--after-install "${PROJECT_ROOT}/scripts/debian/postinst.sh" \
--before-remove "${PROJECT_ROOT}/scripts/debian/prerm.sh" \
--after-remove "${PROJECT_ROOT}/scripts/debian/postrm.sh" \
-C "$PUBLISH_DIR" \
-p "$BUILD_OUTPUT_DIR/jellyfin-${VERSION}-amd64.deb" \
opt/=opt/jellyfin
echo " ✓ Created: jellyfin-${VERSION}-amd64.deb"
else
echo " ⚠ fpm not found - skipping Debian package"
fi
echo
# Step 4: Build Red Hat package
echo "[3/4] Building Red Hat package..."
fpm \
-s dir \
-t rpm \
-n jellyfin \
-v "$VERSION" \
--architecture x86_64 \
--description "Jellyfin Media Server" \
--url "https://jellyfin.org" \
--license "GPL-2.0" \
--maintainer "Jellyfin Team" \
--depends "openssl-libs" \
--depends "libicu" \
--depends "fontconfig" \
--after-install "${PROJECT_ROOT}/scripts/redhat/post.sh" \
--before-remove "${PROJECT_ROOT}/scripts/redhat/preun.sh" \
--after-remove "${PROJECT_ROOT}/scripts/redhat/postun.sh" \
-C "$PUBLISH_DIR" \
-p "$BUILD_OUTPUT_DIR/jellyfin-${VERSION}-1.x86_64.rpm" \
opt/=opt/jellyfin
echo " ✓ Created: jellyfin-${VERSION}-1.x86_64.rpm"
echo
# Cleanup
rm -rf "$PUBLISH_DIR"
# Summary
echo "[4/4] Build Complete!"
echo "╔═══════════════════════════════════════════════════════════════╗"
echo "║ Packages available in: $BUILD_OUTPUT_DIR"
echo "╠═══════════════════════════════════════════════════════════════╣"
echo "║ Debian: jellyfin-${VERSION}-amd64.deb"
echo "║ Red Hat: jellyfin-${VERSION}-1.x86_64.rpm"
echo "╚═══════════════════════════════════════════════════════════════╝"
```
---
## Testing & Verification
### Debian Testing
```bash
# Inspect package contents
dpkg -c jellyfin-11.0.0-amd64.deb
# Extract to temporary location
dpkg-deb -x jellyfin-11.0.0-amd64.deb /tmp/jellyfin-test
# Verify install scripts
dpkg-deb -e jellyfin-11.0.0-amd64.deb /tmp/jellyfin-control
# Install and test
sudo dpkg -i jellyfin-11.0.0-amd64.deb
sudo systemctl status jellyfin
```
### Red Hat Testing
```bash
# Inspect package contents
rpm -qpl jellyfin-11.0.0-1.x86_64.rpm
# Extract to temporary location
rpm2cpio jellyfin-11.0.0-1.x86_64.rpm | cpio -idmv
# Verify scripts
rpm -qp --scripts jellyfin-11.0.0-1.x86_64.rpm
# Install and test
sudo dnf install ./jellyfin-11.0.0-1.x86_64.rpm
sudo systemctl status jellyfin
```
### Cross-Distribution Validation
```bash
# Create Docker test environments
docker run --rm -it debian:bookworm dpkg -i jellyfin-11.0.0-amd64.deb
docker run --rm -it fedora:latest dnf install -y jellyfin-11.0.0-1.x86_64.rpm
docker run --rm -it rockylinux:9 dnf install -y jellyfin-11.0.0-1.x86_64.rpm
```
---
## Summary
| Aspect | Debian | Red Hat |
|--------|--------|---------|
| Package Format | `.deb` | `.rpm` |
| Install Command | `dpkg -i` / `apt install` | `dnf install` / `rpm -i` |
| Service File | `/etc/systemd/system/` | `/etc/systemd/system/` |
| User Dir | `/var/lib/jellyfin` | `/var/lib/jellyfin` |
| Log Dir | `/var/log/jellyfin` | `/var/log/jellyfin` |
| Publish Runtime | `linux-x64` (both) | `linux-x64` (both) |
Both distribution families use the same .NET publishing parameters, so you can maintain a single publish configuration and generate packages for both using the provided scripts.