# 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 " \ --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 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 " \ --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 - 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.