# Building a Debian Package This document describes how to build a Debian (`.deb`) package from the Jellyfin project using the current publishing parameters. ## Overview The Jellyfin project is configured to publish as a self-contained Linux application. These published files can be packaged into a Debian package for easy distribution and installation on Debian-based systems. ### Current Publishing Parameters The project is configured with the following publishing settings (from [rebuild-solution.sh](rebuild-solution.sh)): | Parameter | Value | Purpose | |-----------|-------|---------| | **Runtime** | `linux-x64` | Self-contained binary for 64-bit Linux | | **Configuration** | `Release` | Optimized production build | | **Self-contained** | `true` | Includes all .NET runtime dependencies | | **Output Directory** | `/opt/jellyfin` | Standard Linux application directory | ## Prerequisites Install the required tools on your system: ```bash # Update package list sudo apt-get update # Install build tools sudo apt-get install -y \ dotnet-sdk-11.0 \ ruby-dev \ build-essential # Install FPM (Effing Package Manager) for easy package creation sudo gem install fpm --no-document ``` ## Method 1: Using FPM (Recommended) FPM automates the package creation process and handles dependencies, scripts, and metadata. ### Step 1: Publish the Application ```bash cd /home/wjones/projects/pgsql-jellyfin # Restore dependencies dotnet restore Jellyfin.sln -r linux-x64 # Build for Release dotnet build Jellyfin.sln --configuration Release # Publish as self-contained dotnet publish Jellyfin.Server/Jellyfin.Server.csproj \ --configuration Release \ --self-contained true \ --runtime linux-x64 \ --output /tmp/jellyfin-build ``` **Expected output location**: `/tmp/jellyfin-build/` ### Step 2: Extract Version Get the version from the SharedVersion.cs file: ```bash # Extract version number VERSION=$(grep -oP 'Version\s*=\s*"\K[^"]+' SharedVersion.cs) echo "Building Jellyfin version: $VERSION" ``` ### Step 3: Create the Debian Package ```bash # Navigate to project root cd /home/wjones/projects/pgsql-jellyfin # Build the .deb package fpm -s dir \ -t deb \ -n jellyfin \ -v "$VERSION" \ -C /tmp/jellyfin-build \ -p "jellyfin-${VERSION}_amd64.deb" \ --license "LICENSE" \ --vendor "Jellyfin Contributors" \ --maintainer "Your Name " \ --description "Jellyfin Media Server - a free software media server" \ --url "https://jellyfin.org" \ --architecture x86_64 \ --depends "libssl3" \ --depends "libicu72" \ --depends "libfontconfig1" \ --depends "libc6 (>= 2.31)" \ opt/=opt/jellyfin \ etc/=etc/jellyfin ``` **Output**: `jellyfin-{VERSION}_amd64.deb` ### Step 4 (Optional): Add Service Files To include systemd integration, create service files: ```bash # Create directories for package contents mkdir -p fpm-package/etc/jellyfin mkdir -p fpm-package/usr/lib/systemd/system mkdir -p fpm-package/opt/jellyfin # Copy published files cp -r /tmp/jellyfin-build/* fpm-package/opt/jellyfin/ # Create systemd service file cat > fpm-package/usr/lib/systemd/system/jellyfin.service << 'EOF' [Unit] Description=Jellyfin Media Server After=network.target [Service] Type=simple User=jellyfin Group=jellyfin WorkingDirectory=/opt/jellyfin ExecStart=/opt/jellyfin/jellyfin Restart=on-failure RestartSec=10 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF # Update FPM command to include systemd service fpm -s dir \ -t deb \ -n jellyfin \ -v "$VERSION" \ -C fpm-package \ -p "jellyfin-${VERSION}_amd64.deb" \ --after-install ./scripts/debian/postinst \ --before-remove ./scripts/debian/prerm \ --license "LICENSE" \ --description "Jellyfin Media Server" \ --url "https://jellyfin.org" \ --architecture x86_64 \ --depends "libssl3" \ --depends "libicu72" \ opt/ \ usr/ ``` ## Method 2: Using dpkg-deb (Manual) For more control over the package structure, use `dpkg-deb` directly. ### Step 1-2: Same as Method 1 Publish the application (Steps 1-2 above). ### Step 3: Create Package Directory Structure ```bash # Create control directory mkdir -p jellyfin-pkg/DEBIAN mkdir -p jellyfin-pkg/opt/jellyfin mkdir -p jellyfin-pkg/etc/jellyfin mkdir -p jellyfin-pkg/usr/lib/systemd/system # Copy published application files cp -r /tmp/jellyfin-build/* jellyfin-pkg/opt/jellyfin/ # Copy configuration template cp jellyfin-setup.iss jellyfin-pkg/etc/jellyfin/jellyfin.conf.example ``` ### Step 4: Create DEBIAN Control Files Create `jellyfin-pkg/DEBIAN/control`: ```ini Package: jellyfin Version: 10.x.x Architecture: amd64 Maintainer: Jellyfin Contributors Depends: libssl3, libicu72, libfontconfig1, libc6 (>= 2.31) Homepage: https://jellyfin.org Description: Jellyfin Media Server Jellyfin is a free software media server application that allows you to collect, manage, and share your digital media (video, music, photos) anywhere. ``` Create `jellyfin-pkg/DEBIAN/postinst` (post-install script): ```bash #!/bin/bash set -e # Create jellyfin user if it doesn't exist if ! getent passwd jellyfin > /dev/null; then useradd --system --home /var/lib/jellyfin --shell /bin/false jellyfin fi # Create necessary directories mkdir -p /var/lib/jellyfin /var/log/jellyfin /etc/jellyfin chown -R jellyfin:jellyfin /var/lib/jellyfin /var/log/jellyfin /etc/jellyfin # Reload systemd systemctl daemon-reload # Start the service systemctl enable jellyfin.service systemctl start jellyfin.service exit 0 ``` Create `jellyfin-pkg/DEBIAN/prerm` (pre-remove script): ```bash #!/bin/bash set -e # Stop the service systemctl stop jellyfin.service || true systemctl disable jellyfin.service || true exit 0 ``` Create `jellyfin-pkg/DEBIAN/postrm` (post-remove script): ```bash #!/bin/bash set -e # Clean up user and directories (optional) # userdel jellyfin || true # rm -rf /var/lib/jellyfin exit 0 ``` Make scripts executable: ```bash chmod 0755 jellyfin-pkg/DEBIAN/postinst chmod 0755 jellyfin-pkg/DEBIAN/prerm chmod 0755 jellyfin-pkg/DEBIAN/postrm ``` ### Step 5: Build the Package ```bash # Get version VERSION=$(grep -oP 'Version\s*=\s*"\K[^"]+' SharedVersion.cs) # Create the .deb package dpkg-deb --build jellyfin-pkg "jellyfin-${VERSION}_amd64.deb" # Verify the package dpkg-deb --info "jellyfin-${VERSION}_amd64.deb" ``` **Output**: `jellyfin-{VERSION}_amd64.deb` ## Installation Once you have created the `.deb` package: ```bash # Install the package sudo dpkg -i jellyfin-10.x.x_amd64.deb # If dependencies are missing, install them sudo apt-get install -f # Check service status sudo systemctl status jellyfin # View logs sudo journalctl -u jellyfin -f ``` ## Verification ### Check Package Contents ```bash # List files in the package dpkg-deb -c jellyfin-10.x.x_amd64.deb # View package metadata dpkg-deb -I jellyfin-10.x.x_amd64.deb ``` ### Test Installation ```bash # Create a test environment mkdir -p /tmp/jellyfin-test cd /tmp/jellyfin-test # Extract package contents dpkg -x /path/to/jellyfin-10.x.x_amd64.deb . dpkg -e /path/to/jellyfin-10.x.x_amd64.deb DEBIAN # Review extracted files ls -la cat DEBIAN/control ``` ## Distribution ### Options for Distribution 1. **Local Repository**: Host `.deb` files on a private apt repository 2. **GitHub Releases**: Upload to GitHub releases for easy download 3. **Package Repository**: Submit to Ubuntu/Debian repositories 4. **Direct Download**: Provide `.deb` file download link ### Example: Create Local APT Repository ```bash # Create repository directory mkdir -p /var/www/jellyfin-repo/pool/main # Copy package cp jellyfin-10.x.x_amd64.deb /var/www/jellyfin-repo/pool/main/ # Generate package index (requires apt-utils) cd /var/www/jellyfin-repo dpkg-scanpackages pool/main /dev/null | gzip > Packages.gz # Users can then add to their apt sources: # echo "deb file:///var/www/jellyfin-repo /" | sudo tee /etc/apt/sources.list.d/jellyfin.list ``` ## Troubleshooting ### Common Issues **Issue**: `dpkg-deb: error: unable to open jellyfin-pkg/DEBIAN/control (No such file or directory)` **Solution**: Ensure the DEBIAN directory exists and control file is properly created: ```bash mkdir -p jellyfin-pkg/DEBIAN # Recreate control file with proper permissions ``` **Issue**: Package installs but service won't start **Solution**: Check if jellyfin user exists and has proper permissions: ```bash sudo systemctl status jellyfin sudo journalctl -u jellyfin -n 50 sudo ls -la /opt/jellyfin sudo ls -la /var/lib/jellyfin ``` **Issue**: Missing dependencies warning **Solution**: Ensure all required libraries are listed in the `Depends:` field: ```bash ldd /opt/jellyfin/jellyfin | grep "not found" ``` ## Automation Script Create `scripts/linux/build-deb-package.sh`: ```bash #!/bin/bash set -e SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")" VERSION=$(grep -oP 'Version\s*=\s*"\K[^"]+' "$PROJECT_ROOT/SharedVersion.cs") BUILD_DIR="/tmp/jellyfin-build-$$" OUTPUT_DIR="$PROJECT_ROOT/dist" echo "Building Jellyfin Debian package v$VERSION..." # Create output directory mkdir -p "$OUTPUT_DIR" # Build and publish cd "$PROJECT_ROOT" dotnet restore Jellyfin.sln -r linux-x64 dotnet build Jellyfin.sln --configuration Release dotnet publish Jellyfin.Server/Jellyfin.Server.csproj \ --configuration Release \ --self-contained true \ --runtime linux-x64 \ --output "$BUILD_DIR" # Create package fpm -s dir \ -t deb \ -n jellyfin \ -v "$VERSION" \ -C "$BUILD_DIR" \ -p "$OUTPUT_DIR/jellyfin-${VERSION}_amd64.deb" \ --license "LICENSE" \ --description "Jellyfin Media Server" \ --url "https://jellyfin.org" \ --architecture x86_64 \ --depends "libssl3" \ --depends "libicu72" \ opt/=opt/jellyfin # Cleanup rm -rf "$BUILD_DIR" echo "Package created: $OUTPUT_DIR/jellyfin-${VERSION}_amd64.deb" dpkg-deb -I "$OUTPUT_DIR/jellyfin-${VERSION}_amd64.deb" ``` Make it executable: ```bash chmod +x scripts/linux/build-deb-package.sh ``` Run it: ```bash ./scripts/linux/build-deb-package.sh ``` ## References - [Debian New Maintainers' Guide](https://www.debian.org/doc/manuals/maint-guide/) - [Packaging with fpm](https://github.com/jordansissel/fpm) - [dpkg-deb Manual](https://manpages.debian.org/dpkg-deb) - [systemd Unit Files](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)