- 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.
16 KiB
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
- Common Publishing Setup
- Debian Package Build
- Red Hat Package Build
- Build Scripts
- 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)
# 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:
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
sudo apt-get install ruby-dev
sudo gem install fpm
Build the Package
#!/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
#!/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)
#!/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)
#!/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)
#!/bin/bash
# Remove user/group
userdel jellyfin || true
Systemd Service File (scripts/debian/jellyfin.service)
Create /etc/systemd/system/jellyfin.service:
[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
# 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
# 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
#!/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)
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
#!/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)
#!/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)
#!/bin/bash
set -e
# Stop service
systemctl stop jellyfin || true
systemctl disable jellyfin || true
Post-Uninstall Script (postun.sh)
#!/bin/bash
# Clean up user/group
userdel jellyfin || true
Systemd Service File (jellyfin.service)
[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
# 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:
#!/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
# 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
# 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
# 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.