# How startup.json is Loaded and Used on Startup ## YES - The code DOES check startup.json on startup! ✅ Here's the complete flow: --- ## Startup Flow Diagram ``` ┌─────────────────────────────────────────────┐ │ 1. Jellyfin Starts │ │ Program.Main() → StartApp() │ └──────────────────┬──────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ 2. CreateApplicationPaths() │ │ (in StartupHelpers.cs) │ └──────────────────┬──────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ 3. LoadStartupConfiguration() │ │ Searches for startup.json in: │ │ • Current directory │ │ • AppContext.BaseDirectory │ │ • AppContext.BaseDirectory/config │ └──────────────────┬──────────────────────────┘ │ ┌─────────┴─────────┐ │ │ Found? Yes No │ │ ▼ ▼ ┌──────────────────┐ ┌───────────────────────┐ │ 4a. Load JSON │ │ 4b. Create Default │ │ Return config│ │ (OS-specific) │ └────────┬─────────┘ └───────────┬───────────┘ │ │ └────────────┬───────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ 5. Resolve Paths (Priority Order): │ │ 1. Command-line args │ │ 2. Environment variables │ │ 3. startup.json values ← HERE! │ │ 4. Built-in defaults │ └──────────────────┬──────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ 6. Create ServerApplicationPaths object │ │ with resolved paths │ └─────────────────────────────────────────────┘ ``` --- ## Code Flow Details ### Step 1: Startup Entry Point **File:** `Jellyfin.Server/Program.cs` ```csharp // Line 88-90 private static async Task StartApp(StartupOptions options) { ServerApplicationPaths appPaths = StartupHelpers.CreateApplicationPaths(options); // ... } ``` ### Step 2: Load Configuration **File:** `Jellyfin.Server/Helpers/StartupHelpers.cs` ```csharp // Line 222-223 public static ServerApplicationPaths CreateApplicationPaths(StartupOptions options) { // Try to load startup configuration from file var startupConfig = LoadStartupConfiguration(); // ... } ``` ### Step 3: Search for startup.json **File:** `Jellyfin.Server/Helpers/StartupHelpers.cs` ```csharp // Lines 78-112 private static IConfigurationRoot? LoadStartupConfiguration() { const string ConfigFileName = "startup.json"; // Search locations in priority order var searchPaths = new[] { Path.Combine(Directory.GetCurrentDirectory(), ConfigFileName), Path.Combine(AppContext.BaseDirectory, ConfigFileName), Path.Combine(AppContext.BaseDirectory, "config", ConfigFileName) }; foreach (var configPath in searchPaths) { if (File.Exists(configPath)) { try { var config = new ConfigurationBuilder() .AddJsonFile(configPath, optional: false, reloadOnChange: false) .Build(); Console.WriteLine($"Loaded startup configuration from: {configPath}"); return config; } catch (Exception ex) { Console.WriteLine($"Warning: Failed to load startup configuration from {configPath}: {ex.Message}"); } } } // No configuration file found - create a default one CreateDefaultStartupConfiguration(); return null; } ``` ### Step 4: Use startup.json Values **File:** `Jellyfin.Server/Helpers/StartupHelpers.cs` Each path is resolved with this priority chain: ```csharp // DataDir example (lines 230-234) var dataDir = options.DataDir // 1. Command-line ?? Environment.GetEnvironmentVariable("JELLYFIN_DATA_DIR") // 2. Environment ?? startupConfig?.GetValue("Paths:DataDir") // 3. startup.json ← HERE! ?? Path.Join( // 4. Built-in default Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "jellyfin"); // ConfigDir (lines 236-248) var configDir = options.ConfigDir ?? Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR") ?? startupConfig?.GetValue("Paths:ConfigDir"); // ← startup.json checked! // CacheDir (lines 250-260) var cacheDir = options.CacheDir ?? Environment.GetEnvironmentVariable("JELLYFIN_CACHE_DIR") ?? startupConfig?.GetValue("Paths:CacheDir"); // ← startup.json checked! // LogDir (lines 289-293) var logDir = options.LogDir ?? Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR") ?? startupConfig?.GetValue("Paths:LogDir"); // ← startup.json checked! // TempDir (lines 295-301) var tempDir = options.TempDir ?? Environment.GetEnvironmentVariable("JELLYFIN_TEMP_DIR") ?? startupConfig?.GetValue("Paths:TempDir"); // ← startup.json checked! // WebDir (lines 262-287) var webDir = options.WebDir ?? Environment.GetEnvironmentVariable("JELLYFIN_WEB_DIR") ?? startupConfig?.GetValue("Paths:WebDir"); // ← startup.json checked! ``` --- ## Search Locations (In Order) When looking for `startup.json`, the code checks these locations: 1. **Current working directory** - Example: `E:\Projects\pgsql-jellyfin\startup.json` - This is where you run `jellyfin` from 2. **Application base directory** - Example: `E:\Projects\pgsql-jellyfin\Jellyfin.Server\bin\Release\net11.0\startup.json` - Where the jellyfin.exe/dll is located 3. **Config subdirectory** - Example: `E:\Projects\pgsql-jellyfin\Jellyfin.Server\bin\Release\net11.0\config\startup.json` - Allows organizing config files --- ## Priority Order for Path Resolution For **each path** (DataDir, ConfigDir, etc.), the code checks in this order: ``` ┌──────────────────────────────────────┐ │ 1. Command-line argument │ Highest Priority │ --datadir /custom/path │ (Always wins) └──────────────────┬───────────────────┘ │ If not provided ▼ ┌──────────────────────────────────────┐ │ 2. Environment variable │ │ JELLYFIN_DATA_DIR=/custom/path │ └──────────────────┬───────────────────┘ │ If not set ▼ ┌──────────────────────────────────────┐ │ 3. startup.json value │ ← YOUR QUESTION! │ "DataDir": "/custom/path" │ YES, IT CHECKS HERE! └──────────────────┬───────────────────┘ │ If null or missing ▼ ┌──────────────────────────────────────┐ │ 4. Built-in OS-specific default │ Lowest Priority │ (code determines based on OS) │ (Fallback) └──────────────────────────────────────┘ ``` --- ## Console Output ### When startup.json is Found ``` Loaded startup configuration from: E:\Projects\pgsql-jellyfin\startup.json ``` ### When startup.json is Not Found ``` Created default startup configuration at: E:\Projects\pgsql-jellyfin\startup.json Using Windows defaults - using C:/ProgramData/jellyfin You can customize this file to set different paths for Jellyfin. ``` --- ## Real-World Examples ### Example 1: Using startup.json **Your startup.json:** ```json { "Paths": { "DataDir": "D:/Media/Jellyfin/Data", "ConfigDir": "D:/Media/Jellyfin/Config", "CacheDir": "E:/Cache/Jellyfin", "LogDir": "D:/Logs/Jellyfin" } } ``` **What happens on startup:** ```csharp // For DataDir: options.DataDir // null (not provided) ?? Environment.GetEnvironmentVariable(...) // null (not set) ?? startupConfig?.GetValue("Paths:DataDir") // "D:/Media/Jellyfin/Data" ← USED! // Result: DataDir = "D:/Media/Jellyfin/Data" ``` **Console output:** ``` Loaded startup configuration from: E:\Projects\pgsql-jellyfin\startup.json [INF] Data directory: D:/Media/Jellyfin/Data [INF] Config directory: D:/Media/Jellyfin/Config [INF] Cache directory: E:/Cache/Jellyfin [INF] Log directory: D:/Logs/Jellyfin ``` ### Example 2: Override with Command-Line **Your startup.json:** ```json { "Paths": { "DataDir": "D:/Media/Jellyfin/Data" } } ``` **Command:** ```powershell jellyfin --datadir "F:/CustomData" ``` **What happens:** ```csharp options.DataDir // "F:/CustomData" ← USED! (highest priority) ?? Environment.GetEnvironmentVariable(...) // (not checked) ?? startupConfig?.GetValue("Paths:DataDir") // (not checked) // Result: DataDir = "F:/CustomData" ``` ### Example 3: Override with Environment Variable **Your startup.json:** ```json { "Paths": { "DataDir": "D:/Media/Jellyfin/Data" } } ``` **Environment:** ```powershell $env:JELLYFIN_DATA_DIR = "G:/EnvData" jellyfin ``` **What happens:** ```csharp options.DataDir // null ?? Environment.GetEnvironmentVariable(...) // "G:/EnvData" ← USED! ?? startupConfig?.GetValue("Paths:DataDir") // (not checked) // Result: DataDir = "G:/EnvData" ``` --- ## Testing the Behavior ### Test 1: Verify startup.json is Loaded ```powershell # Create a test startup.json @" { "Paths": { "DataDir": "E:/TEST_JELLYFIN_DATA", "LogDir": "E:/TEST_JELLYFIN_LOGS" } } "@ | Out-File startup.json # Run Jellyfin (it will create these directories and use them) .\jellyfin.exe # Check console output - should see: # "Loaded startup configuration from: E:\Projects\pgsql-jellyfin\startup.json" ``` ### Test 2: Verify Priority Order ```powershell # Set in startup.json echo '{"Paths":{"DataDir":"E:/FROM_JSON"}}' > startup.json # Override with environment variable $env:JELLYFIN_DATA_DIR = "E:/FROM_ENV" # Run Jellyfin .\jellyfin.exe # Result: Will use E:/FROM_ENV (environment wins over startup.json) ``` ### Test 3: Verify Search Locations ```powershell # Put startup.json in current directory echo '{"Paths":{"DataDir":"E:/CURRENT_DIR"}}' > startup.json # Also put one in base directory echo '{"Paths":{"DataDir":"E:/BASE_DIR"}}' > .\bin\Release\net11.0\startup.json # Run from current directory .\jellyfin.exe # Result: Uses E:/CURRENT_DIR (current directory checked first) ``` --- ## Summary ### ✅ YES - startup.json IS Checked on Startup! **When:** During application initialization in `CreateApplicationPaths()` **Where:** Searches 3 locations (current dir, base dir, config subdir) **Priority:** 3rd in the resolution chain: 1. Command-line args (highest) 2. Environment variables 3. **startup.json** ← HERE 4. Built-in defaults (lowest) **What's Read:** All `Paths:*` values: - `Paths:DataDir` - `Paths:ConfigDir` - `Paths:CacheDir` - `Paths:LogDir` - `Paths:TempDir` - `Paths:WebDir` **Confirmation:** Console output shows "Loaded startup configuration from: [path]" when found --- ## Code References | Function | File | Purpose | |----------|------|---------| | `StartApp()` | Program.cs:88 | Entry point | | `CreateApplicationPaths()` | StartupHelpers.cs:222 | Path resolution | | `LoadStartupConfiguration()` | StartupHelpers.cs:78 | Loads startup.json | | `CreateDefaultStartupConfiguration()` | StartupHelpers.cs:116 | Creates default if missing | --- **Verified:** The code absolutely checks and uses startup.json on every startup! ✅