--- url: /plus/ai-assistant.md description: >- Using the AI assistant to control music playback, manage playlists, and explore your library with natural language. --- # AI Assistant Koel Plus comes with an AI-powered assistant that lets you interact with your music library using natural language. Instead of navigating through menus, you can simply type what you want in plain English. :::danger Disclaimer The AI assistant and its tools are provided as-is for personal use. Koel and its developers assume no legal responsibility for how these tools are used or for the content they generate, fetch, or modify. It is the user's sole responsibility to ensure that their usage complies with applicable laws and regulations in their jurisdiction. ::: :::warning Important As with any AI-powered feature, the assistant may occasionally misunderstand your request, produce unexpected results, or fail to find what you're looking for. Natural language is inherently ambiguous, and the assistant's responses depend on the underlying AI model. Always verify important actions (e.g., deleting playlists) and don't hesitate to rephrase your request if the result isn't what you expected. ::: ## Setup An admin turns the assistant on under **Settings → AI**: 1. Tick **Use AI assistant**. 2. Pick a provider. 3. Paste the provider's API key and save. Koel stores the key encrypted and never shows it again; to change it, paste a new one. Switching providers needs a new key. :::tip Upgrading from an earlier version If you had set up the assistant with `AI_ENABLED`, `AI_PROVIDER` and a provider key in `.env`, upgrading moves that setup into Settings, as long as Settings offers that provider. Afterwards those `.env` variables are no longer read and can be removed. ::: The AI assistant is powered by [Laravel's AI SDK](https://laravel.com/docs/12.x/ai-sdk). ## Using the Assistant Open the AI assistant by pressing / or clicking the sparkle button in the interface. Type your request in natural language and press Enter. The assistant understands a wide range of requests. Here are some examples to get you started: ### Playing Music * "Play the album Abbey Road" * "Play some jazz" * "Play Pink Floyd" * "Play my favorite songs" * "Play my most played tracks" * "Replay what I listened to recently" * "Play my Chill Vibes playlist" * "Add some jazz to the queue" * "Play the song that goes 'Is this the real life, is this just fantasy'" * "Play songs similar to this" * "Play my most listened-to album" * "Play my top artist" ### Library Information * "What song is playing right now?" * "Tell me about Radiohead" * "Tell me about the album Dark Side of the Moon" * "Show me the lyrics of this song" * "Find the lyrics for this song if there are none" ### Managing Playlists * "Create a smart playlist with Dance songs from the 90s" * "Add this to my Road Trip playlist" * "Remove this song from my Workout playlist" * "Rename my playlist to Summer Hits" * "Delete my old workout playlist" ### Favorites * "Add this song to my favorites" * "Remove this song from my favorites" * "Add this album to my favorites" * "Favorite the artist Radiohead" ### Radio Stations * "Stream some classical music" (if you have a matching radio station) :::tip The assistant is context-aware. If a song is currently playing, you can refer to it with "this song" or "this" without having to specify the exact title. ::: ## Known Limitations ### Lyrics Display When asked to show lyrics, the assistant retrieves them from your library and attempts to relay them in the response. However, some AI providers may refuse to display lyrics due to copyright restrictions built into their models. If this happens, try a different AI provider — behavior varies between providers and models. The lyrics are still stored in your library and can always be viewed via the side sheet's Lyrics tab. ## Conversation Memory The AI assistant remembers the context of your current conversation session. This means you can have follow-up requests like: * "Play some jazz" → "Actually, add those to the queue instead" * "Tell me about Radiohead" → "Play their most popular songs" The conversation resets when you close and reopen the assistant. --- --- url: /usage/artist-album-playlist-arts.md description: >- How Koel fetches and displays artwork from Spotify, Last.fm, and MusicBrainz, with support for custom uploads. --- # Artist, Album, and Playlist Arts If [integrated](../service-integrations) Koel will attempt to fetch artists' images and album arts from Spotify, Last.fm, or MusicBrainz/Wikipedia whenever a song is played. You can upload custom images for artists, albums, and playlists via the corresponding edit form. If there is no image available for an artist or album, Koel will use a default image (the Koel bird logo). For a playlist, a random "thumbnail stack" will be generated and used based on the tracks. Newer versions of Koel also provide a [command](../cli-commands.md#koel-fetch-artwork) to fetch missing artist and album arts. --- --- url: /cli-commands.md description: >- Reference for all Koel artisan CLI commands including scanning, syncing, pruning, search indexing, and storage setup. --- # CLI Commands Koel comes with a set of handy CLI commands to help you manage your installation. These commands are available via Laravel’s `artisan` command line interface. ::: tip Standalone Binary users If you installed Koel via the [Standalone Binary](/guide/standalone-binary), replace `php artisan` with `./artisan` (run from the bundle directory) in every command below. `./artisan` is a thin shortcut for `./koel php-cli artisan` that re-execs the launcher so the bundle's PHP runtime stays in scope. Otherwise the commands below are identical. ::: You can run `php artisan list` from your Koel installation directory and pipe the output to `grep` to filter out those under the `koel` namespace: ```bash php artisan list | grep koel koel koel:admin:change-password Change a user's password koel:doctor Check Koel setup koel:init Install or upgrade Koel koel:license:activate Activate a Koel Plus license koel:license:deactivate Deactivate the currently active Koel Plus license koel:license:status Check the current Koel Plus license status koel:podcasts:sync Synchronize podcasts. koel:prune Remove empty artists and albums ... ``` In order to get help on a specific command, run `php artisan -h`. ## Available Commands :::warning Warning Always run commands as your web server user (e.g. `www-data` or `nginx`), **never** as `root`. Otherwise, Koel might encounter issues with file permissions (e.g. with cache and storage files) and you might end up with a broken installation. With the Docker installation, for example, run the command as the `www-data` user: ```bash docker exec --user www-data php artisan ``` ::: ### `koel:admin:change-password` Change a user's password. #### Usage ```bash php artisan koel:admin:change-password [] ``` #### Arguments | Name | Description | |---------|--------------------------------------------------------------| | `email` | The user's email. If empty, will get the default admin user. | ### `koel:admin:set-user-role` Set a user's role. #### Usage ```bash php artisan koel:admin:set-user-role ``` #### Arguments | Name | Description | |---------|-------------------| | `email` | The user's email. | ### `koel:clean-up-temp-files` Remove temporary files older than a certain age. Examples of temporary files include temporarily downloaded files, transcoded files, and so on. #### Usage ```bash php artisan koel:clean-up-temp-files ``` #### Options | Name | Description | |----------|-------------------------------------------------------------------------------| | `--age=` | The age of temporary files to remove in minutes. Defaults to 1440 (one day). | ### `koel:doctor` Check Koel setup. #### Usage ```bash php artisan koel:doctor ``` ### `koel:extract-folders` Extract the folder structure from the existing song paths and store it in the database. This command needs to be run only once. Subsequent scans will take care of this functionality automatically. #### Usage ```bash php artisan koel:extract-folders ``` ### `koel:fetch-artwork` Attempt to fetch missing album covers and artist images from the available sources (the Cover Art Archive, Spotify, Last.fm, and Wikipedia). You'll need to configure and enable the corresponding [3rd-party integrations](./service-integrations.md) for this command to work. The Cover Art Archive finds covers by MusicBrainz identifier, so run [`koel:fetch-mbids`](#koel-fetch-mbids) first to fill in the albums that do not have one yet. To avoid hitting rate limits, Koel pauses between requests. By default, this delay is 1 second, but you can customize it with the `--delay` option. #### Usage ```bash php artisan koel:fetch-artwork # Or with a custom delay (in seconds) between requests: php artisan koel:fetch-artwork --delay=2 ``` ### `koel:fetch-mbids` Attempt to fetch missing MusicBrainz identifiers for albums, artists and songs. Koel reads these identifiers from your files when it scans them, and looks up the rest when you open an album or an artist. Run this command to look up everything that is still missing in one go, so songs you have not browsed to also carry an identifier when Koel submits a listen to ListenBrainz or answers a Subsonic client. A song's identifier is found through its album, so an album is revisited when any of its songs is still missing one, even if the album itself already has one. MusicBrainz accepts one request per second, so a large library takes a while. You can stop the command at any time and run it again later: it only looks up what is still missing, and it never replaces an identifier your files already supplied. #### Usage ```bash php artisan koel:fetch-mbids ``` ### `koel:init` Install or upgrade Koel. Usage ```bash php artisan koel:init [options] ``` #### Options | Name | Description | |------------------|---------------------------------| | `--no-assets` | Do not compile front-end assets | | `--no-scheduler` | Do not install scheduler | :::tip Instead of running `php artisan koel:init` directly, you should run `composer koel:init`, which calls this command under the hood after installing dependencies. To pass options to `php artisan koel:init`, use argument forwarding, e.g., `composer koel:init -- --no-assets`. ::: ### `koel:license:activate` Activate a Koel Plus license. #### Usage ```bash php artisan koel:license:activate ``` #### Arguments | Name | Description | |-------|------------------------------| | `key` | The license key to activate. | ### `koel:license:deactivate` Deactivate the currently active Koel Plus license. #### Usage ```bash php artisan koel:license:deactivate ``` ### `koel:license:status` Check the current Koel Plus license status. #### Usage ```bash php artisan koel:license:status ``` ### `koel:podcasts:sync` Synchronize podcasts. #### Usage ```bash php artisan koel:podcasts:sync ``` ### `koel:prune` Remove empty artists and albums. #### Usage ```bash php artisan koel:prune ``` ### `koel:scan` Scan for songs in the configured directory. #### Usage ```bash php artisan koel:scan [options] php artisan koel:sync [options] # Alias, deprecated ``` #### Options: | Name | Description | |------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `O`, `--owner=` | The ID of the user who should own the newly scanned songs. Defaults to the first admin user. | | `P`, `--private` | Whether to make the newly scanned songs private to the user. | | `I`, `--ignore=` | The comma-separated tags to ignore (exclude) from scanning. Valid tags are `title`, `album`,`artist`, `albumartist`, `track`, `disc`, `year`, `genre`, `lyrics`, and `cover`. | | `F`, `--force` | Force re-scanning even unchanged files. | ### `koel:scheduler:install` Install the command scheduler. Refer to [Command Scheduling](#command-scheduling) for more information. ### `koel:search:import` Import all searchable entities with Scout. Refer to [Instant Search](./usage/search) for more information. #### Usage ```bash php artisan koel:search:import ``` ### `koel:storage` Set up and configure Koel’s storage. #### Usage ```bash php artisan koel:storage ``` ### `koel:storage:dropbox` Set up Dropbox as the storage driver for Koel. #### Usage ```bash php artisan koel:storage:dropbox ``` ### `koel:storage:local` Set up the local storage for Koel. A "local storage" is simply a directory on the server where Koel is installed. #### Usage ```bash php artisan koel:storage:local ``` ### `koel:storage:s3` Set up Amazon S3 or a compatible service (DigitalOcean Spaces, Cloudflare R2, etc.) as the storage driver for Koel. #### Usage ```bash php artisan koel:storage:s3 ``` ### `koel:storage:webdav` Set up WebDAV (NextCloud, ownCloud, or a plain WebDAV server) as the storage driver for Koel. #### Usage ```bash php artisan koel:storage:webdav ``` :::tip To set up the storage driver for Koel, simply use `koel:storage`. Internally, it calls `koel:storage:local`, `koel:storage:s3`, `koel:storage:dropbox`, or `koel:storage:webdav` based on your input. ::: ### `koel:tags:collect` Collect additional tags from existing songs. This is a legacy command and is no longer needed for new installations. #### Usage ```bash php artisan koel:tags:collect ``` ## Command Scheduling Some commands, such as `koel:scan`, `koel:prune`, and `koel:clean-up-temp-files` can be scheduled to run at regular intervals. Instead of setting up individual cron jobs, you can use Koel’s built-in scheduler to handle them automatically. Every install method sets the scheduler up for you, so most users have nothing more to do. `koel:init` runs `koel:scheduler:install` for the pre-compiled archive and build-from-source paths; the [Standalone Binary](/guide/standalone-binary) launcher writes its own cron entry on every `./koel php-server` start. If you ran `koel:init` with `--no-scheduler` and want to install it on its own, run as the web server user (e.g. `www-data` or `nginx`): ```bash php artisan koel:scheduler:install ``` If you'd rather edit the crontab directly (for example, with `sudo crontab -u www-data -e`), add this line: ```bash * * * * * cd /path-to-koel-installation && php artisan schedule:run >> /dev/null 2>&1 ``` Either way, the scheduler runs every minute and executes any scheduled commands as needed. By default, `koel:scan`, `koel:prune`, and `koel:podcasts:sync` run every day at midnight. --- --- url: /plus/cloud-storage-support.md description: >- Setting up SFTP, Amazon S3, DigitalOcean Spaces, Cloudflare R2, Dropbox, and WebDAV (NextCloud, Owncloud) as Koel storage drivers. --- # Cloud Storage Support In addition to storing your music on the same server as Koel’s installation via the `local` storage drivers, Koel Plus offers several different file storage options, including SFTP, Amazon S3, S3-compatible services, Dropbox, WebDAV, and likely more in the future. This page will guide you through the process of setting up these storage options. :::warning Warning Though possible, changing storage drivers *after* you've already stored files is not recommended, as it may break links to your existing media. ::: :::tip Service UI may change The screenshots and instructions on this page may not be 100% up-to-date as 3rd-party services' UI may change. The general idea, however, should remain the same. ::: ## Local Storage The default storage driver stores media files on the same server Koel is installed on. Most installations are configured this way automatically by `koel:init`, but you can re-run the setup at any time: ```bash php artisan koel:storage:local ``` You'll be prompted for the absolute path to your media files (the path must already exist and be readable and writable by the web server user). Koel saves the path to its settings, writes `STORAGE_DRIVER=local` to your `.env`, and then offers to run `koel:scan` so the directory is indexed right away. ## SFTP To use SFTP as your storage driver, you need to have an SFTP server set up and running. Many cloud hosting providers offer SFTP access to their storage services. To enable SFTP storage support in Koel, you need to provide the following configuration in your `.env` file: ``` STORAGE_DRIVER=sftp SFTP_HOST= SFTP_PORT= # The absolute path of the directory to store the media files on the SFTP server. # Make sure the directory exists and is writable by the SFTP user. SFTP_ROOT= # You can use either a username/password pair… SFTP_USERNAME= SFTP_PASSWORD= # …or private key authentication: SFTP_PRIVATE_KEY= SFTP_PASSPHRASE= ``` After reloading, Koel will start using SFTP as its storage driver. You can now upload your music files to your SFTP server directly from Koel’s web interface. ## Amazon S3 and Compatible Services Since Amazon S3 and S3-compatible services share the same API, you can use the same configuration (`AWS_*`) for both. Koel has been tested with Amazon S3, [DigitalOcean Spaces](https://www.digitalocean.com/products/spaces), and [Cloudflare R2](https://www.cloudflare.com/developer-platform/r2/), but it should work with any S3-compatible service given the right configuration. ### Interactive setup From the root folder of your Koel installation, run: ```bash php artisan koel:storage:s3 ``` You'll be prompted for the access key ID, secret access key, region, endpoint, and bucket name. Koel writes the values to your `.env` file and uploads a test file to confirm the connection. The secret access key is hidden as you type; leave it blank on a re-run to keep the existing value. If the test fails, Koel restores your previous `.env` and exits. Fix the credentials and run the command again. ### Amazon S3 Create a new S3 bucket and obtain your access key ID and secret key from the AWS console. Then, populate these values in your `.env` file: ``` STORAGE_DRIVER=s3 AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_REGION= AWS_ENDPOINT= AWS_BUCKET= ``` After reloading, Koel will start using S3 as its storage. You can now upload your music files to your S3 bucket directly from Koel’s web interface. In order to use S3 for streaming, you'll also need to set up a cross-origin resource sharing (CORS) configuration for your bucket by going to the Permissions tab. Here's a sample policy that allows streaming from any origin: ```json [ { "AllowedHeaders": [], "AllowedMethods": [ "GET" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [] } ] ``` #### Cleaning up unfinished uploads While a file is being uploaded, it's kept under the `pending/` prefix of your bucket and moves out once the upload finishes. An upload that never finishes (for example, because the browser tab was closed) stays under `pending/`. To clean those up, add a lifecycle rule that deletes objects under `pending/` after a day. In the AWS console, go to your bucket's Management tab → Create lifecycle rule, or apply this configuration: ```json { "Rules": [ { "ID": "delete-unfinished-uploads", "Status": "Enabled", "Filter": { "Prefix": "pending/" }, "Expiration": { "Days": 1 } } ] } ``` To apply it with the AWS CLI, save it as `lifecycle.json` and run the command below. The command replaces the bucket's existing lifecycle rules, so if your bucket already has some, add them to `lifecycle.json` first. You can list them with `aws s3api get-bucket-lifecycle-configuration --bucket `. ```bash aws s3api put-bucket-lifecycle-configuration --bucket --lifecycle-configuration file://lifecycle.json ``` DigitalOcean Spaces accepts the same configuration through its S3-compatible API. On Cloudflare R2, go to your bucket's Settings → Object lifecycle rules → Add rule, set the prefix to `pending/`, and delete objects 1 day after upload. ### DigitalOcean Spaces To use [DigitalOcean Spaces](https://www.digitalocean.com/products/spaces), you can follow the same steps as with Amazon S3. Below are some screenshots to help you get started. Create a DigitalOcean Spaces bucket DigitalOcean Spaces bucket settings with CORS Configurations The access key ID and secret key can be generated and obtained via the API -> Space Keys tab in the DigitalOcean control panel: ![DigitalOcean Spaces keys](../assets/img/plus/storage/do-keys.webp) A sample `.env` configuration for DigitalOcean Spaces may look like this: ``` STORAGE_DRIVER=s3 AWS_ACCESS_KEY_ID=DO000000000000000000 AWS_SECRET_ACCESS_KEY=TheSecretKeyObtainedFromDO AWS_REGION=fra1 AWS_ENDPOINT=https://fra1.digitaloceanspaces.com AWS_BUCKET=koel ``` ### Cloudflare R2 [Cloudflare R2](https://www.cloudflare.com/developer-platform/r2/) is yet another excellent choice for an S3-compatible service. The setup is pretty much similar to the above: Create a Cloudflare R2 bucket Cloudflare R2 bucket settings with CORS Policy A sample `.env` configuration for Cloudflare R2 may look like this: ``` STORAGE_DRIVER=s3 AWS_ACCESS_KEY_ID=c50000000000000000 AWS_SECRET_ACCESS_KEY=TheSecretKeyObtainedFromR2 AWS_REGION=auto # Cloudflare R2 explicitly uses the `auto` region AWS_ENDPOINT=https://fa37a667b0038bbb7054133627ce74b4.r2.cloudflarestorage.com AWS_BUCKET=koel ``` ## Dropbox Koel Plus also supports using Dropbox as a storage driver, although the setup is a bit different. 1. First, [create a new Dropbox app](https://www.dropbox.com/developers/apps/create) with the "App folder" access type and set up sufficient permissions to read and write files. Create a Dropbox app Set sufficient app permissions 2. Under your Dropbox app's Settings tab, make a note of the "App key" and "App secret" values. Now, from the root folder of your Koel installation, run the following command: ```bash php artisan koel:storage:dropbox ``` You'll be prompted to enter the "App key" and "App secret" values obtained earlier. 3. Koel will generate a URL for you to visit and authorize the Dropbox app. Afterward, you'll receive an access code. 4. Enter the access code back into the command line's prompt. Koel will then finalize the setup automatically and start using Dropbox as its storage. Now when you upload music files to Koel, they'll be stored in your Dropbox app's folder. :::warning Two-way sync not supported Koel does not support two-way sync with Dropbox — at least not yet. This means manual changes made to your Dropbox folder will not be reflected in Koel. ::: ## WebDAV (NextCloud, Owncloud, …) Koel Plus can use any WebDAV server as a storage backend, including [NextCloud](https://nextcloud.com/), [Owncloud](https://owncloud.com/), or a plain WebDAV server. ### Interactive setup From the root folder of your Koel installation, run: ```bash php artisan koel:storage:webdav ``` You'll be prompted for the base URL, username, password, and optional path prefix. Koel writes the values to your `.env` file and uploads a test file to confirm the connection. For NextCloud, the base URL looks like `https://your-nextcloud.example/remote.php/dav/files//`. If the test fails, Koel restores your previous `.env` and exits. Fix the credentials and run the command again. ### Manual setup Alternatively, add the following to your `.env` file: ```dotenv STORAGE_DRIVER=webdav # For NextCloud, the base URL looks like: # https://your-nextcloud.example/remote.php/dav/files// WEBDAV_BASE_URL= WEBDAV_USERNAME= WEBDAV_PASSWORD= # Optional path prefix beneath the base URL where Koel stores its media (no leading or trailing slash). # For example: Music WEBDAV_PATH_PREFIX= ``` :::warning Use an app password For NextCloud, consider creating a dedicated [app password](https://docs.nextcloud.com/server/latest/user_manual/en/session_management.html#managing-devices) instead of using your account password, so you can revoke Koel's access without affecting other clients. ::: --- --- url: /plus/collaboration.md description: >- Song visibility, sharing, collaborative playlists, and multi-user library management in Koel Plus. --- # Collaboration One of the distinctive features of Koel Plus is the ability for users to share and collaborate with others. This page will talk you through the process of setting song visibility and playlist collaboration. ## Setting Song Visibility With Koel Plus, there are two different visibility settings for songs: public and private. * *Private* songs are only visible to the uploader (the "owner") * *Public* songs are visible to all users (**not** the whole internet!) However, only the owner can edit or delete them. The default visibility for uploaded songs is Private. For other users to be able to see and listen to your music, you need to explicitly mark them as public. This can be done in two ways: * **On an individual basis**: To mark individual songs as public or private, right-click on the songs to bring up the context menu. From there, you can choose "Mark as Public" or "Mark as Private" depending on the current visibility. * **Set default upload visibility**: For all future uploads to be public or private by default, you can toggle the "Make uploaded songs public by default" option in your [Profile & Preferences](../usage/profile-preferences#preferences) page. Note that this only affects future uploads. ### Scanning Music When you scan for music with the `koel:scan` command, discovered songs will be marked as public and assigned to the first admin user. You can change this behavior by passing the `--private` option to the command and/or explicitly specify the owner of the songs with the `--owner` option. ```bash php artisan koel:scan --private --owner=2 ``` ### Migrating from Community Edition When you upgrade Koel from a previous, Community, edition to Koel Plus, all existing music will be marked as public and assigned to the first admin user. This is to ensure all existing music is still accessible to everyone, and the first admin user has full control over them. You can always change the visibility of the songs and/or reassign them to other users as you see fit. ```sql --- Mark all songs as private UPDATE songs SET is_public = 0; --- Assign all songs, albums, and artists to another user UPDATE songs SET owner_id = 2; UPDATE albums SET user_id = 2; UPDATE artists SET user_id = 2; ``` ## Playlist Collaboration A *collaborative playlist* allows multiple users to add, remove, and reorder songs in a playlist. :::tip Songs are marked as public When a song is added to a collaborative playlist, it will be marked as public if it's not already. In addition, you cannot mark a song as private if it's still part of a collaborative playlist. ::: To set up or manage collaboration on a playlist, you need to be the playlist's owner. From the sidebar, right-click on the playlist and choose "Collaborate…" from the context menu. The "Playlist Collaboration" modal should appear. ![Collaboration modal](../assets/img/plus/collab.webp) To invite more collaborators, click the "Invite" button. A link will be generated and copied to your clipboard. Share this link with the users you want to collaborate with. They can then paste the link in their browser to join the playlist. To remove a collaborator, click the "Remove" button next to their name. --- --- url: /usage/playlists.md description: >- Creating standard and smart playlists, organizing with nested folders, and managing collaborative playlists in Koel. --- # Creating and Managing Playlists Koel supports creating an unlimited number of playlists and organizing them into nested folders. Start by clicking the button next to the "Playlists" header in the navigation bar. You'll be provided with three options: * "New Playlist…" brings up a dialog to create a standard playlist * "New Smart Playlist…" brings up a dialog to create a smart playlist * "New Folder…" brings up a dialog to create a playlist folder To create a **standard playlist** or **playlist folder**, enter a name and save it. Once a playlist or playlist folder is created, you can: * Drag and drop songs into a standard playlist to add them * Drag and drop playlists into or out of playlist folders ## Organizing Playlist Folders To create a folder inside another folder, open the parent folder's context menu and choose **Add** > **New Folder…**. Drag a folder onto another folder to move it there. To move it back to the top level, drag it to the root of the Playlists list. To rename a folder or move it without dragging, open its context menu and choose **Edit…**. Change the folder name or select a **Parent Folder**, then save your changes. Select **Root** to move the folder to the top level. To remove a folder, open its context menu, choose **Delete**, and confirm the deletion. ::: info Deleting a folder Deleting a playlist folder keeps its contents. Playlists directly inside it become unfiled, and its subfolders move to the top level with their contents unchanged. ::: For a **smart playlist**, you can define the criteria that determine which songs are included in the playlist, and Koel will automatically update the playlist based on those criteria. For example, with the criteria shown in the following screenshot: Koel will include in the playlist: * all songs by Pink Floyd, and * songs by Iron Maiden that have been played more than 99 times by the current user There are a great variety of criteria to choose from, and you can combine them in any way you like, providing a powerful tool for creating dynamic playlists. ## Collaboration Users of Koel Plus can collaborate on playlists. See [Collaboration](../plus/collaboration.md) for more details. --- --- url: /plus/custom-themes.md description: 'Creating custom themes with colors, background images, and fonts in Koel Plus.' --- # Custom Themes Apart from the built-in themes, you can create your own themes as a Koel Plus user. To do so, go to the Profile & Preferences page and click on the "Themes" tab. Here you'll find a list of custom themes you've created in addition to the built-in ones. Clicking on the "New Theme" button will open the "New Theme" form: A theme in Koel consists of these simple properties, each customizable via the form: * Foreground color * Background color * Highlight color * Background image (optional) * Font family * Font size :::tip `local-fonts` Permission On a [supported browser](https://caniuse.com/wf-local-fonts), Koel will ask for permission to use your local fonts. In case of unsupported browsers or denied permission, a list of common fonts will be used instead. ::: Every change will be reflected immediately. Moreover, clicking the "Preview" button will temporarily hide the form, giving you a full preview of your theme. Clicking "Save" will save your theme, apply it, and add it to the list of custom themes, when clicking "Cancel" will discard your changes and revert to the previous state. --- --- url: /usage/embedding.md description: >- Embedding tracks, playlists, albums, and artists via iframe with layout and theme options. --- # Embedding Koel supports embedding tracks, playlists (including smart playlists), albums, and artists. To create a new embed, right-click on the track/playlist/album/artist to bring up the context menu and select "Embed…". The Create Embed dialog will appear. Here you can pick the layout of the embed: either showing both the banner and the tracklist or showing only the banner. Users with a [Koel Plus](/plus/what-is-koel-plus) license can additionally choose a theme and toggle the Preview mode, which plays a 30-second sample instead of the full track. Once you're happy with the embed, click the "Copy Code" button to copy the embed code to your clipboard. Now you can paste it anywhere you want, e.g., in a blog post, and visitors will see the embed and be able to play the included track(s) without having to log in. :::warning Legal Notice Embedded tracks are available publicly by nature, so make sure you have the rights to distribute them. Koel is not responsible for any copyright issues. ::: :::info Allow iframes Since the embed is an iframe, make sure your Koel's webserver sends the proper `X-Frame-Options` response header—or doesn't send one at all, which would allow the iframe to be embedded *anywhere*. If you're not familiar with the concept, read more about it [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options). --- --- url: /environment-variables.md description: >- Comprehensive reference of all Koel .env variables for storage, scanning, streaming, integrations, SSO, and more. --- # Environment Variables This page documents the environment variables specific to Koel. These variables are typically set in the `.env` file at the root of your Koel installation. :::info Laravel Variables Koel is built on Laravel, which has its own set of environment variables (e.g. `APP_ENV`, `APP_DEBUG`, `APP_KEY`, `APP_URL`, `DB_*`, `CACHE_DRIVER`, `QUEUE_CONNECTION`, `SESSION_DRIVER`, etc.). These are not documented here — please refer to the [Laravel documentation](https://laravel.com/docs/configuration) for details. ::: ## Storage | Variable | Description | Default | |---|---|---| | `STORAGE_DRIVER` | The storage driver for your media files. Valid values: `local`, `sftp`, `s3` (Koel Plus), `dropbox` (Koel Plus), `webdav` (Koel Plus). See [Cloud Storage Support](plus/cloud-storage-support). | `local` | | `MEDIA_PATH` | The absolute path to your media directory. Required when using `STORAGE_DRIVER=local`. Can also be changed via the web interface. | *(empty)* | | `ARTIFACTS_PATH` | The absolute path to store Koel artifacts (transcoded files, podcast episodes, temporary downloads, etc.). If empty, uses the system's temporary directory. | *(empty)* | | `LARAVEL_STORAGE_PATH` | Absolute path Koel uses for writable runtime data — album/artist images (under `app/public/images`), logs, search indexes, framework cache, sessions. Set this to keep mutable state outside the application directory (read-only deploys, multi-instance setups, etc.). After setting it, run `php artisan storage:link` (or `composer koel:init`) so `public/storage` symlinks to `LARAVEL_STORAGE_PATH/app/public`. | `/storage` | :::info Upgrading an older Koel install The image storage location moved from `public/img/storage/` to `storage/app/public/images/` (or `LARAVEL_STORAGE_PATH/app/public/images/` when set). `php artisan koel:init` handles the migration automatically on the first run after upgrading — no manual action needed. If `koel:init` reports `Some legacy images could not be migrated. Keeping public/img/storage for manual recovery.`, rerun `koel:init` after fixing the underlying cause (most often a permissions issue on `storage/`). As a last resort, perform the move manually: ```bash mkdir -p "${LARAVEL_STORAGE_PATH:-$(pwd)/storage}/app/public/images" rsync -a public/img/storage/ "${LARAVEL_STORAGE_PATH:-$(pwd)/storage}/app/public/images/" php artisan storage:link ``` After confirming images render, the old `public/img/storage/` directory can be removed. ::: ### S3 / S3-Compatible Required when `STORAGE_DRIVER=s3`. Remember to set your CORS policy to allow access from Koel's domain. | Variable | Description | Default | |---|---|---| | `AWS_ACCESS_KEY_ID` | Your AWS (or S3-compatible service) access key ID. | *(empty)* | | `AWS_SECRET_ACCESS_KEY` | Your AWS secret access key. | *(empty)* | | `AWS_REGION` | The region of your bucket. Set to `auto` for Cloudflare R2. | *(empty)* | | `AWS_ENDPOINT` | The endpoint URL for S3-compatible services. | *(empty)* | | `AWS_BUCKET` | The name of your S3 bucket. | *(empty)* | ### Dropbox Required when `STORAGE_DRIVER=dropbox`. Run `php artisan koel:setup-dropbox` to set all Dropbox-related variables. | Variable | Description | Default | |---|---|---| | `DROPBOX_APP_KEY` | Your Dropbox app key. | *(empty)* | | `DROPBOX_APP_SECRET` | Your Dropbox app secret. | *(empty)* | | `DROPBOX_REFRESH_TOKEN` | Your Dropbox refresh token. | *(empty)* | ### SFTP Required when `STORAGE_DRIVER=sftp`. | Variable | Description | Default | |---|---|---| | `SFTP_HOST` | The SFTP server hostname. | *(empty)* | | `SFTP_PORT` | The SFTP server port. | *(empty)* | | `SFTP_ROOT` | The absolute path on the SFTP server to store media files. | *(empty)* | | `SFTP_USERNAME` | The SFTP username. | *(empty)* | | `SFTP_PASSWORD` | The SFTP password. | *(empty)* | | `SFTP_PRIVATE_KEY` | Path to the private key for key-based authentication (alternative to password). | *(empty)* | | `SFTP_PASSPHRASE` | The passphrase for the private key. | *(empty)* | ### WebDAV Required when `STORAGE_DRIVER=webdav`. | Variable | Description | Default | |---|---|---| | `WEBDAV_BASE_URL` | The WebDAV base URL. For NextCloud, this looks like `https://your-nextcloud.example/remote.php/dav/files//`. | *(empty)* | | `WEBDAV_USERNAME` | The WebDAV username. For NextCloud, use a dedicated [app password](https://docs.nextcloud.com/server/latest/user_manual/en/session_management.html#managing-devices) rather than the account password. | *(empty)* | | `WEBDAV_PASSWORD` | The WebDAV password (or NextCloud app password). | *(empty)* | | `WEBDAV_PATH_PREFIX` | Optional path prefix beneath the base URL where Koel stores its media (no leading or trailing slash). For example: `Music`. | *(empty)* | ## Media Scanning | Variable | Description | Default | |---|---|---| | `APP_MAX_SCAN_TIME` | The maximum scan time in seconds when scanning via the browser. Does not affect `koel:sync`. | `600` | | `MEMORY_LIMIT` | The memory limit in MB for the scanning process. Example: `2048`. | *(empty)* | | `SCAN_JOBS` | The number of parallel worker processes for scanning. Set to `1` to disable parallel scanning. Can be overridden with `--jobs` flag. | `4` | | `IGNORE_DOT_FILES` | Whether to ignore dot files and folders when scanning. Greatly improves performance if your media root has folders like `.git` or `.cache`. | `true` | | `SYNC_LOG_LEVEL` | The verbosity of sync logs (found under `storage/logs/`). Options: `all`, `error`. | `error` | ## Streaming & Transcoding | Variable | Description | Default | |---|---|---| | `STREAMING_METHOD` | The streaming method. Options: `php`, `x-sendfile`, `x-accel-redirect`. See [Streaming Music](usage/streaming). Using `x-sendfile` or `x-accel-redirect` is highly recommended for better performance. | `php` | | `TRANSCODE_FLAC` | Whether to transcode FLAC to AAC on the fly. Set to `false` to stream FLAC as-is. | `true` | | `TRANSCODE_BIT_RATE` | The bit rate (in kbps) for transcoded audio. Higher values mean better quality but slower streaming. | `128` | | `TRANSCODE_AAC_FAST` | Whether to use FFmpeg's faster AAC coding algorithm. Set to `false` to use its default AAC coder. | `true` | | `FFMPEG_PATH` | The full path to the ffmpeg binary. Automatically detected if left empty. | *(auto-detected)* | | `TRANSCODE_TIMEOUT` | The maximum time in seconds allowed for transcoding a single file. Increase for very large files. `0` disables the timeout. | `300` | ## Downloading | Variable | Description | Default | |---|---|---| | `ALLOW_DOWNLOAD` | Whether to allow song downloading. | `true` | ## Service Integrations Also see [Service Integrations](service-integrations) for detailed setup instructions. | Variable | Description | Default | |---|---|---| | `USE_MUSICBRAINZ` | Whether to use MusicBrainz for metadata fetching. | `true` | | `MUSICBRAINZ_USER_AGENT` | The user agent for MusicBrainz API requests. Auto-generated if empty. | *(auto-generated)* | | `LASTFM_API_KEY` | Your Last.fm API key. Required for artist/album info and scrobbling. | *(empty)* | | `LASTFM_API_SECRET` | Your Last.fm API secret. | *(empty)* | | `LISTENBRAINZ_API_ENDPOINT` | The ListenBrainz API root. Change this only if you run your own ListenBrainz server. | `https://api.listenbrainz.org` | | `SPOTIFY_CLIENT_ID` | Your Spotify application client ID. Used for fetching artist and album images. | *(empty)* | | `SPOTIFY_CLIENT_SECRET` | Your Spotify application client secret. | *(empty)* | | `YOUTUBE_API_KEY` | Your YouTube API key. See [YouTube integration](service-integrations#youtube). | *(empty)* | | `TICKETMASTER_API_KEY` | Your Ticketmaster API key. See [Ticketmaster](plus/ticketmaster). | *(empty)* | | `TICKETMASTER_DEFAULT_COUNTRY_CODE` | Fallback country code for Ticketmaster when IP-based lookup fails. See [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). | `US` | | `IPINFO_TOKEN` | Your IPinfo token, used to look up the user's country for Ticketmaster. | *(empty)* | ## SSO (Single Sign-On) Koel Plus only. See [Single Sign-On](plus/sso). | Variable | Description | Default | |---|---|---| | `SSO_GOOGLE_CLIENT_ID` | Your Google OAuth client ID. | *(empty)* | | `SSO_GOOGLE_CLIENT_SECRET` | Your Google OAuth client secret. | *(empty)* | | `SSO_GOOGLE_HOSTED_DOMAIN` | The Google Workspace domain users must belong to. | *(empty)* | | `SSO_OIDC_ISSUER` | Issuer URL of an OpenID Connect IdP (Authentik, Authelia, Keycloak, Zitadel, …). Koel reads `/.well-known/openid-configuration` for endpoint discovery. | *(empty)* | | `SSO_OIDC_CLIENT_ID` | OAuth client ID registered with the IdP. | *(empty)* | | `SSO_OIDC_CLIENT_SECRET` | OAuth client secret. | *(empty)* | | `SSO_OIDC_BUTTON_LABEL` | Label shown on the OIDC login button. | `OpenID Connect` | | `SSO_DEFAULT_ROLE` | The role given to a user on their first SSO login: `user` or `guest`. Applies to OIDC, Google, and reverse-proxy auth. | `user` | ## Proxy Authentication Koel Plus only. See [Proxy Authentication](plus/proxy-auth). | Variable | Description | Default | |---|---|---| | `PROXY_AUTH_ENABLED` | Whether to enable proxy authentication. | `false` | | `PROXY_AUTH_USER_HEADER` | The header containing the unique user identifier. | `remote-user` | | `PROXY_AUTH_PREFERRED_NAME_HEADER` | The header containing the user's preferred display name. | `remote-preferred-name` | | `PROXY_AUTH_ALLOW_LIST` | A comma-separated list of allowed proxy IPs or CIDRs. If empty, no requests are allowed. | *(empty)* | ## AI Assistant The AI assistant is no longer set up here. An admin turns it on and adds the provider's API key under **Settings → AI Assistant**. See [AI Assistant](./plus/ai-assistant#setup). ## Miscellaneous | Variable | Description | Default | |---|---|---| | `APP_URL` | The address people use to reach Koel, e.g. `https://music.example.com`. Links in emails, such as password resets and invitations, point here, so make sure it is correct. | `http://localhost` | | `TRUSTED_HOSTS` | A comma-separated list of hostnames allowed to access Koel. Requests for any other hostname are rejected. Prefix a hostname with `*.` to allow any of its subdomains. Leave empty to allow any hostname. Example: `localhost,192.168.0.1,yourdomain.com,*.yourdomain.com` | *(empty)* | | `TRUSTED_PROXIES` | A comma-separated list of IP addresses or ranges of the reverse proxies in front of Koel. Koel trusts `X-Forwarded-*` headers only from these. Set it if your proxy is on a public IP, such as Cloudflare. | `PRIVATE_SUBNETS` | | `FORCE_HTTPS` | Force Koel to use HTTPS URLs. Set to `true` if automatic detection fails. | `false` | | `SENTRY_LARAVEL_DSN` | Report unhandled exceptions to [Sentry](https://sentry.io). Leave empty to disable reporting entirely. Events are tagged with `APP_ENV`. | *(empty)* | | `BACKUP_ON_DELETE` | Whether to create a backup of a song when deleting it from the filesystem. | `true` | | `CDN_URL` | A CDN URL mapped to Koel's home URL, used to serve media files. No trailing slash. | *(empty)* | | `IMAGE_STORAGE_DRIVER` | The filesystem driver for artwork — album covers, artist images and avatars. Use `s3` to keep them on S3 or an S3-compatible service such as Cloudflare R2, reusing the `AWS_*` settings. | `local` | | `IMAGE_STORAGE_DIR` | Where artwork is stored: a path under Koel's `public` directory for the `local` driver, or a key prefix inside the bucket otherwise. | `storage/images` | | `IMAGE_STORAGE_BUCKET` | A separate, public bucket for artwork, so media can stay private. | *(`AWS_BUCKET`)* | | `IMAGE_STORAGE_URL` | The public URL artwork is served from. Required when the driver is not `local`. No trailing slash. | *(empty)* | | `MEDIA_BROWSER_ENABLED` | Whether to enable the media browser (experimental Koel Plus feature). | `false` | | `CLEAN_URLS_ENABLED` | Whether to use plain URLs like `/albums` instead of `/#/albums`. | `false` | | `EMBED_ENABLED` | Whether to allow embedding songs, albums, artists, and playlists on external sites. Set to `false` to hide the "Embed…" menu entries and disable both creation and rendering of embed widgets. | `true` | | `PODCASTS_ENABLED` | Whether to enable podcasts. Set to `false` to hide podcasts from the interface and stop serving them over both Koel's own API and Subsonic. Existing subscriptions and episodes are left untouched. | `true` | | `RADIO_ENABLED` | Whether to enable radio stations. Set to `false` to hide radio from the interface and stop serving it over both Koel's own API and Subsonic. Existing stations are left untouched. | `true` | --- --- url: /guide/getting-started.md description: >- Requirements, installation methods (pre-compiled, source, Docker, Railway), configuration, running with FrankenPHP (binary + systemd), upgrading, and downgrading Koel. --- # Getting Started ## Requirements Koel consists of two parts: the server and the client. The server is a Laravel application acting as the API, and the client is a Vue.js application responsible for the user interface. The requirements for each part are as follows: ### Server * [All requirements by Laravel](https://laravel.com/docs/11.x/deployment#server-requirements) – PHP >= 8.2 with required extensions * One of the databases supported by Laravel. Koel has been tested with MySQL, MariaDB, PostgreSQL, and SQLite. * If you're [building Koel from source](#building-from-source), make sure to have [Composer](https://getcomposer.org/), Git, Node.js >= 20 with [pnpm](https://pnpm.io/), and [Vite+](https://viteplus.dev/) (`pnpm install` will set it up locally). ### Client * Koel has been tested on Chrome 47, Firefox 42, Safari 8, Opera 34, and Edge, but having the latest version is always recommended. ## Installation There are five methods to install and start using Koel: ### Using the Standalone Binary The single-archive distribution from [koel/franken](https://github.com/koel/franken/releases) packages [FrankenPHP](https://frankenphp.dev) (Caddy + the PHP runtime) with the compiled Koel application. No Composer, Node, or system PHP needed on the host. SQLite by default for zero-setup operation. ```bash tar -xzf koel-franken-v9.3.2-linux-x86_64.tar.gz cd koel-franken-v9.3.2-linux-x86_64 ./koel php-server --listen :8000 ``` See the [Standalone Binary](/guide/standalone-binary) page for the full setup, conventions, MySQL/Postgres override, and systemd service unit. ### Using a Pre-Compiled Archive Koel supports installing from a pre-compiled archive, which eliminates the need of manually compiling the front-end assets. First, go to the [Releases page](https://github.com/koel/koel/releases) on GitHub, download either the `.tar.gz` or `.zip` file found under "Assets," and unzip it into the destination web root directory. From there, run the two following commands: ```bash composer koel:init -- --no-assets # Follow the wizard to populate necessary configurations php artisan serve ``` ### Building from Source From your console, run the following commands: ```bash cd git clone https://github.com/koel/koel.git . git checkout latest # Check out the latest version at https://github.com/koel/koel/releases composer koel:init # Follow the wizard to populate necessary configurations php artisan serve ``` In both cases, you should now be able to visit http://localhost:8000 in your browser and start using Koel. ::: warning Use a proper webserver http://localhost:8000 is only the *development* server for Koel (or rather, Laravel). For production, point a proper server (Apache, nginx, Caddy, etc.) at Koel's `public/` directory — the process is no different from any standard PHP application. Koel ships starter configs at the project root: `nginx.conf.example` for nginx + PHP-FPM, and `Caddyfile.example` for [FrankenPHP](/guide/running-with-frankenphp). ::: ### Using Docker Koel has an official Docker image: [koel/docker](https://github.com/koel/docker). Please refer to the repository for detailed instructions and issue reporting. ### Using Railway (community) To deploy Koel with PostgreSQL on [Railway](https://railway.com), use this community-maintained template: [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/koel?utm_medium=integration\&utm_source=button\&utm_campaign=koel) This is not an official Koel offering — please report template issues to the maintainers at [osbytes/template-koel](https://github.com/osbytes/template-koel). After the first deploy, change the default admin password immediately (`admin@koel.dev` / `KoelIsCool`). ## Configuration Koel’s configuration is stored in the `.env` file at the root of the project, which is created during the installation process by copying the `.env.example` file and filling it with sensible values. You can always modify the values to suit your environment. For a full list of Koel-specific configuration options, refer to [Environment Variables](/environment-variables). ### Configure a Mailer Though Koel can work without a mailer, certain features like "forgot password" and user invitation require a mailer to be set up. To determine if that's the case, Koel relies on the `MAIL_MAILER` value in the `.env` file. Any non-empty value other than `log` or `array` is considered a proper mailer. As such, if you don't need email-required features, you can simply set `MAIL_MAILER` to `log` or `array` and leave the rest of the mailer-related values empty, and Koel will know to remove/disable these features. ## Running with FrankenPHP Koel works well with [FrankenPHP](https://frankenphp.dev), a modern PHP application server that bundles a webserver (Caddy) and the PHP runtime into a single binary, with automatic HTTPS out of the box. See [Running with FrankenPHP](/guide/running-with-frankenphp) for the full setup guide — install, Caddyfile, artisan commands, systemd service, and reverse-proxy mode. ## Upgrade :::danger Backup your database Remember to always back up your database before upgrading. ::: Check out [Releases](https://github.com/koel/koel/releases) for upgrade guides corresponding to your Koel version and installation method. In general, the upgrade process involves updating the source code and dependencies, running database migrations, and occasionally, adding/updating some configuration values. ### Upgrade an installation from source If you installed Koel from source, upgrading is as simple as running the following commands: ```bash cd git pull git checkout latest composer koel:init ``` ### Upgrade a pre-compiled archive installation For pre-compiled archived users, download the latest archive, extract it, and replace the existing files with the new ones. Make sure, however, that old application code (typically `app` and `config` folders) is removed. ```bash cd rm -rf app config # assuming we're upgrading to v7.0.0 wget -qO- https://github.com/koel/koel/releases/download/v7.0.0/koel-v7.0.0.tar.gz | tar -xvzC . --strip-components=1 composer koel:init -- --no-assets ``` ### Upgrade a Docker installation For Docker users, the upgrade process is as simple as pulling the latest image and restarting the container. :::tip Always read release notes Again, no matter which installation method, always read the [release notes](https://github.com/koel/koel/releases) for specific guides as well as other important changes you might be missing. ::: ## Downgrade Koel does not provide a built-in downgrade mechanism. In the unlikely event that you need to downgrade, simply restore your database from a backup and follow the installation guide for the version you want to downgrade to. --- --- url: /usage/search.md description: >- Full-text instant search powered by Laravel Scout with support for TNTSearch, Algolia, and Meilisearch. --- # Instant Search Powered by [Laravel Scout](https://github.com/laravel/scout), Koel provides an instant search feature that performs full-text, fuzzy searches against your music collection and returns the most relevant results as you type. ## Configuration Out of the box, Koel uses [TNTSearch](https://github.com/teamtnt/tntsearch), a powerful full-text search engine written entirely in PHP and requires no configuration. Switching to [Algolia](https://www.algolia.com/) is just a matter of changing the driver value to `algolia` and populating the credentials into `.env`: ``` SCOUT_DRIVER=algolia ALGOLIA_APP_ID= ALGOLIA_SECRET= ``` Similarly, you can use [Meilisearch](https://www.meilisearch.com/): ``` SCOUT_DRIVER=meilisearch MEILISEARCH_HOST= MEILISEARCH_KEY= ``` ## Update the Search Index If the configuration is done after you have already populated Koel with songs, you'll need to update the search index. You can do so by running this command: ```bash php artisan koel:search:import ``` Afterward and for future changes, the index will be updated automatically whenever you add, update, or delete songs, albums, or artists. There's literally nothing you need to do to keep the index up to date. ## Usage To use the instant search, simply start typing in the search box at the top left of the screen or press F. You'll see the results appear as you type. --- --- url: /development.md description: >- Setting up a local Koel development environment, running the dev server, testing, linting, and contributing. --- # Local Development ## Koel :::tip Code Wiki It's highly recommended to take a look at Koel's [Code Wiki](https://codewiki.google/github.com/koel/koel) to have a better understanding of the code. ::: Koel is built with Laravel and Vue.js, and as such, it requires a PHP environment to run. There are multiple ways to set up a PHP development environment, but if you're on macOS, the easiest way is probably to use [Laravel Herd](https://herd.laravel.com/). You will also need [Node.js](https://nodejs.org/) and [pnpm](https://pnpm.io/) to build the client application. For more requirements, refer to the [Requirements section](./guide/getting-started#requirements). ### Running the Local Webserver First, clone the repository and install the dependencies: ```bash git clone https://github.com/koel/koel.git cd koel composer install pnpm install ``` You can now start the development server with `composer dev`: ```bash composer dev ``` A development version of Koel should now be available at `http://localhost:8000` with full HMR support. Every change you make to the client application will be reflected in the browser immediately. ### Testing, Linting, Static Analysis, etc. ```bash # PHP-related code quality tasks composer test # Run the PHP test suite composer cs # Run code style checker composer cs:fix # Run code style fixer composer analyze # Run PHP static analysis # Client code quality tasks pnpm run build # Build a production version of the client application pnpm run test # Unit testing pnpm run check # Format + lint + type check ``` ## Koel Docs Of course, you are welcome to contribute to Koel Docs (this documentation) as well! Koel’s documentation is built with [VitePress](https://vitepress.dev/) and stored under the `docs` directory in the same repository as Koel. To start the VitePress instance, use the following command: ```bash pnpm docs:dev vitepress v1.0.0 ➜ Local: http://localhost:5173/ ➜ Network: use --host to expose ➜ press h to show help ``` The documentation should now be available at `http://localhost:5173`. For more information on how to work with VitePress, check out its [official website](https://vitepress.dev/). --- --- url: /markdown-examples.md description: >- VitePress markdown extension examples including syntax highlighting and custom containers. --- # Markdown Extension Examples This page demonstrates some of the built-in markdown extensions provided by VitePress. ## Syntax Highlighting VitePress provides Syntax Highlighting powered by [Shiki](https://github.com/shikijs/shiki), with additional features like line-highlighting: **Input** ````md ```js{4} export default { data () { return { msg: 'Highlighted!' } } } ``` ```` **Output** ```js{4} export default { data () { return { msg: 'Highlighted!' } } } ``` ## Custom Containers **Input** ```md ::: info This is an info box. ::: ::: tip This is a tip. ::: ::: warning This is a warning. ::: ::: danger This is a dangerous warning. ::: ::: details This is a details block. ::: ``` **Output** ::: info This is an info box. ::: ::: tip This is a tip. ::: ::: warning This is a warning. ::: ::: danger This is a dangerous warning. ::: ::: details This is a details block. ::: ## More Check out the documentation for the [full list of markdown extensions](https://vitepress.dev/guide/markdown). --- --- url: /plus/media-browser.md description: >- Browsing your music library at the file and folder level using Koel Plus's media browser. --- # Media Browser Apart from the standard ability to browse your music library via extracted metadata, Koel Plus also provides a media browser—an interface in Windows Explorer or Finder style—that allows you to browse your music files directly. This is particularly useful if you have a large collection of music files but without proper ID3 tagging, and prefer to navigate through them by a folder structure. ## Enabling the Media Browser Since this is not a common use case, the media browser is not enabled by default. To enable it, set `MEDIA_BROWSER_ENABLED` to `true` in your `.env` file: ```dotenv MEDIA_BROWSER_ENABLED=true ``` For the first run, you also need to run the following command to extract the folder structure from your music library. This may take a while depending on the library size. ```bash php artisan koel:extract-folders ``` This command only needs to be run once, as Koel will automatically update the folder structure when you add new music files or perform scans. Reload Koel, and you should see a new "Media Browser" link in the sidebar. Click it to open the media browser. ## Usage ![Media Browser](../assets/img/plus/media-browser.avif) The media browser interface is similar to a file explorer. Typically, you will see a list of folders and files, as well as a breadcrumb at the top for quick navigation. Doubling-clicking a folder will navigate into it, while double-clicking a song will play it. You can also perform multi-selection and bring up the context menu to perform actions like Play, Shuffle, Queue, and so on. Dragging and dropping songs or entire folders into the queue or a playlist is also supported. ## Limitations The media browser is not a full-fledged file manager. It does not support file operations like renaming, moving, or deleting files or folders. Also, to keep things simple, browsing another user's upload folder is not supported (though this limitation may be removed in the future). --- --- url: /mobile-apps.md description: >- The official Koel Player Flutter app for Android and iOS as a companion to the web interface. --- # Mobile Apps While Koel is accessible on mobile devices through its responsive design, the user experience may not be optimal compared to a native app due to inherent limitations of web apps on mobile devices. For a better mobile experience, it's strongly recommended to use [Koel Player](https://github.com/koel/player), the official mobile app for Koel, instead. Written in Flutter, Koel Player is available for both [Android](https://play.google.com/store/apps/details?id=phanan.koel.app) and [iOS](https://apps.apple.com/de/app/koel-player/id1576886982?l=en). By purchasing Koel Player, you will also be supporting the continuous development of Koel. Of course, you can always compile and install the app yourself, as the source code is licensed under MIT. --- --- url: /usage/music-discovery.md description: >- How Koel discovers media files via scanning, uploading, filesystem watching, and AWS Lambda integration. --- # Music Discovery There are several ways for Koel to discover your media files. In the most common scenario, you will have a directory on your server where you store your music files. You can let Koel know where this directory is (the "media path") via the [web interface](#scan-via-the-web-interface) or the [CLI](../cli-commands#koel-storage-local). :::danger Keep media out of Koel’s directory Do NOT place your media files inside Koel’s directory. Though technically possible, doing so will make upgrading, downgrading, and reinstalling Koel much more tedious. ::: Once the media path is set, you can scan for songs (either manually or [using a cron job](../cli-commands#command-scheduling)), configure a watcher, or upload files directly via the web interface. :::tip Cloud Storage With [Koel Plus](../plus/what-is-koel-plus), you can also use cloud storage services like Amazon S3 or Dropbox to store your media files. Refer to [Storage Support](../plus/cloud-storage-support) for more details. ::: ## Supported Audio Formats Koel supports a vast range of audio formats, both lossy and lossless. Under the hood, it uses [getID3](https://www.getid3.org/) and as such, it can read metadata from most audio formats supported by the library, including but not limited to MP3, Ogg Vorbis, AAC, FLAC, AIFF, Monkey's Audio, WavPack, etc. Do note that, to play audio formats that are not [natively supported by modern browsers](https://caniuse.com/?search=audio%20format), you'll need proper [setup and configuration](streaming.md#transcoding). ## Scan via the Web interface :::warning Not for large libraries Scanning via the web interface is vulnerable to HTTP timeouts and memory limit, so if you have a decent-sized library, opt for other methods instead. ::: Upload your songs into a readable directory on your server and configure Koel to scan and sync it by setting a "media path" under Manage → Settings → Media Path. ![Settings Screen](../assets/img/settings.webp) Once set, click the "Scan" button to start the process. Koel will scan the directory and its subdirectories for audio files and add them to the database. ## Scan Using the CLI You can also scan from the CLI with the artisan `koel:scan` command. This method is faster, without a time or library-size limit, and provides useful feedbacks. :::warning Warning Always run `koel:scan` as well as any other commands as your web server user (e.g. `www-data` or `nginx`), **never** as `root`. Otherwise, Koel might encounter issues with file permissions (e.g. with cache and storage files) and you might end up with a broken installation. With the Docker installation, for example, run the command as the `www-data` user: ```bash docker exec --user www-data php artisan koel:scan ``` ::: ```bash php artisan koel:scan INFO Scanning /var/www/media 1189/1189 [============================] 100% INFO Scanning completed! ⇂ 1150 new or updated song(s) ⇂ 39 unchanged song(s) ⇂ 0 invalid file(s) ``` Suffix the command with a `-v` flag for more details e.g. scanning errors. This command can be added as a cron job, for example to run every midnight: ```bash 0 0 * * * cd /home/user/webapps/koel/ && /usr/local/bin/php artisan koel:scan >/dev/null 2>&1 ``` A better approach is to use Laravel’s built-in scheduler. See [Command Scheduling](../cli-commands#command-scheduling) for more details. ## Upload via the Web Interface You can upload songs directly by clicking the "Upload" sidebar menu item or just drag and drop files and folders into the web interface. Note that if you’re not using a cloud storage (available with Koel Plus), you will need to set the media path first, as the files will be uploaded into the `%media_path%/__KOEL__UPLOADS__` directory. Koel automatically detects duplicate uploads by comparing file hashes (not song metadata). If a file you upload matches a song already in your library, it will be flagged and held in a duplicate uploads panel on the Upload screen. You can then choose to discard the upload or continue and keep the duplicate. Depending on how big your files are, you may want to set `upload_max_filesize` and `post_max_size` in your `php.ini` correspondingly, or uploading may fail with a `Payload too large` error. This applies even if you’re using a cloud storage, as the files will be uploaded to your server first before being sent to the cloud. ## Watch the Media Directory You can also watch your media directory and trigger *selective* synchronization every time there's a change to it with the help of `inotifywait`. In order to start using the feature, follow these steps: ### 1. Install `inotify` Tools `inotify` is a Linux API that provides a mechanism for monitoring file system events and can be installed via your package manager. For example, you can install it on Ubuntu with: ```bash sudo apt-get install inotify-tools ``` ### 2. Set Up a Watcher Script Now you need to set up a watcher script to run `inotifywait` and send the output to `koel:scan` artisan command. For example, you can create a sample `watch` file in Koel’s root directory with this content: ```bash #!/bin/bash MEDIA_PATH=/var/www/media/ PHP_BIN=/usr/local/bin/php inotifywait -rme move,close_write,delete --format "%e %w%f" $MEDIA_PATH | while read file; do $PHP_BIN artisan koel:scan "${file}" done ``` ### 3. Run the Watcher in the Background Following the above example: ```bash chmod +x watch ./watch [Ctrl+z] bg disown -h ``` You can now verify that it works by `tail -f storage/logs/laravel.log` while making changes to your media directory, for example by adding or removing applicable files via FTP. ## Integration with AWS Lambda :::warning Deprecated Though still functional, this method is deprecated in favor of configuring S3 as a [cloud storage](../plus/cloud-storage-support). ::: Starting from version v3.0.0, Koel can work seamlessly with Amazon S3 with the help of the [official Koel-AWS package](https://github.com/koel/koel-aws). This allows you to run Koel in your server and have all media files hosted on S3. ### How It Works 1. You upload media files to your S3 bucket 2. S3 sends events to a Lambda function 3. The Lambda function calls Koel’s API to sync the media into Koel’s database 4. You create a streaming request to Koel 5. Koel gets the media from S3 and streams it to you ### Supports and Requirements Only `mp3`, `ogg`, `m4a`, and `flac` files are supported. ### Step-by-Step Installation #### 1. Prepare S3 for Streaming 1. Create an IAM user, e.g. `koel-user` 2. Create a bucket, e.g. `koel-bucket` 3. Make sure `koel-user` can read `koel-bucket`'s content. You can simply attach the `AmazonS3ReadOnlyAccess` policy to `koel-user`. 4. Allow CORS on `koel-bucket` ```xml * GET 3000 Authorization ``` #### 2. Configure Lambda for Syncing 1. Clone Koel-AWS's repository: `git clone https://github.com/phanan/koel-aws` 2. Install necessary packages: `cd koel-aws && npm install --production` 3. Copy `.env.example` into `.env` and edit the variables there 4. Zip the whole directory's content into something like `archive.zip` 5. In AWS Lambda console, create a Lambda function with the following information: ``` Name: koel-lambda Runtime: Node.js Code entry type: Upload a .ZIP file (you'll upload the zip file created in step 4 here) Handler: index.handler Role: S3 execution role (a new window will appear, where you can just click next next and next) Memory (MB): 128 should be fine Timeout: 0min 10sec VPC: "No VPC" should be fine ``` :::info AWS region Make sure you're creating the function in the same region with `koel-bucket`. ::: #### 3. Configure S3 to send events to Lambda Under `koel-bucket` "Events" section, create an event with the following details: ``` Name: Events: ObjectCreated(All), ObjectRemoved(All) Prefix: Suffix: Send To: Lambda function Lambda function: koel-lambda ``` #### 4. Configure Koel to Stream from S3 If everything works properly, you can now upload media files to the bucket, and they should appear in Koel. Now after you populate `koel-user`'s `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_REGION` into your Koel's `.env` file, Koel will start streaming media from your S3. --- --- url: /usage/offline-playback.md description: >- Caching songs for offline playback using Service Workers, managing offline storage, and limitations. --- # Offline Playback Koel allows you to cache songs for offline playback directly from your browser. This is especially useful when you're on the go or have an unreliable internet connection. :::info Information Offline playback requires a modern browser with Service Worker support. The feature is not available in older browsers or when Service Workers are disabled. ::: ## Making Songs Available Offline To cache a song for offline playback, right-click on it and select "Make Available Offline" from the context menu. You can also select multiple songs, or right-click on an entire album or playlist to cache all songs at once. The caching progress will be displayed in real time. Once cached, an offline indicator will appear next to the song, signifying that it can be played without an internet connection. ## Viewing Offline Songs All songs that have been cached for offline playback can be found on the "Available Offline" screen, accessible from the sidebar. This screen lists all cached songs along with their metadata. ## Removing Offline Cache To remove a song from the offline cache, right-click on it and select "Remove Offline Version" from the context menu. As with caching, you can bulk-remove by selecting multiple songs or right-clicking on an album or playlist. ## Storage Management You can view and manage your offline storage usage from the [Preferences screen](./profile-preferences#preferences). A progress bar shows how much storage space is being used by cached songs. To clear all cached songs at once, click the "Clear All" button. ## Limitations * Offline playback is only available for songs, not for radio stations or podcast episodes. * The amount of storage available depends on your browser and device. Koel will display the current usage and quota in the Preferences screen. * Songs cached in one browser are not available in another browser or device. --- --- url: /usage/podcasts.md description: >- Subscribing to podcasts via RSS, playback behavior, resume position tracking, and automatic syncing. --- # Podcasts Aside from music, Koel also supports podcast streaming. This feature is still in its early stages, so expect more improvements in the future. ![Podcasts](../assets/img/interface/podcasts.avif) ## Subscribe to a Podcast To subscribe to a podcast, click the button on the top right of the "Podcasts" page and enter the podcast's RSS feed URL. You can find the RSS feed URL on the podcast's website or by searching for it with your favorite search engine. Note that Koel doesn't support proprietary podcast services like Spotify or Apple Podcasts. :::warning Podcast parsing can be time-consuming, and not all podcasts are created equal. If the process errors out, check [Troubleshooting](../troubleshooting#first-steps) for help. ::: After successful subscription, you will be able to browse the podcast's episodes and stream them directly from Koel. ## Listening to Podcasts Koel treats podcast episodes not much differently from songs. You can stream them, mark them as favorites, add them to playlists, search for them etc. using the same [interface elements](./web-interface.md). However, there are some main differences: * Koel maintains the last played position for each episode, so you can resume listening where you left off. * Smart playlists don't work with podcasts and will not contain podcast episodes. * You can't edit the metadata of podcasts and episodes like you can with songs. * Podcast episodes don't have "genres" (though they may have categories), so you can't browse them by genre. * [Playlist collaboration](../plus/collaboration) doesn't work with podcasts. * When streaming an episode, Koel will first try with its original source and only download it to your server if a CORS error occurs in the attempt. This means that you can stream podcasts without worrying about your server's storage space, but your streaming experience will depend on the podcast's hosting service. ## Updating Podcasts If you have Koel's [command scheduler](../cli-commands.md#command-scheduling) set up, the `koel:podcasts:sync` command will automatically run at midnight to keep your podcasts up to date. You can also update a podcast's episodes manually from the web interface by clicking the button on the podcast's page. --- --- url: /usage/profile-preferences.md description: >- Managing your profile, avatar, theme selection, playback preferences, and service integration settings. --- # Profile and Preferences To manage your profile and preferences, click on your avatar in the bottom-right corner of the screen. From here, you can manage a couple aspects of your account: ## Profile To update your profile, you must first authenticate yourself by entering your current password. After that, you can update your name and email, and set a new password. Leaving the New Password field blank will keep your current password intact. :::tip Pick a strong password Koel enforces a strong password policy. Make sure to pick a password that is at least 10 characters long and contains a mix of letters, numbers, and special characters. Your password will also be checked against a list of leaked passwords for extra security. ::: ## Custom Avatar By default, Koel uses [Gravatar](https://gravatar.com) to fetch your avatar based on your email address. By hovering over the avatar and clicking the icon, you can select an image file from your computer, crop it, and set it as your custom avatar. Remember to click Save for the change to take effect. To remove your custom avatar and revert to using Gravatar, click the icon. ## Themes Koel comes loaded with beautiful, handcrafted themes. You can activate a theme simply by clicking on it. The new theme will be applied immediately. ![Theme selection](../assets/img/themes.avif) More themes are to be added in the future, along with the ability to create your own theme. ## Preferences Koel allows you to set a couple of preferences: * Whether playing a song should trigger continuous playback of the entire playlist, album, artist, or genre * Whether to show a notification whenever a new song starts playing * Whether to confirm before closing Koel’s browser tab * Whether to show a translucent, blurred overlay of the current album’s art * Crossfade duration between songs (0–15 seconds, 0 to disable) * Whether to transcode music to a lower bitrate (mobile only, useful if you have a slow connection) * Whether to set your uploaded music as public by default * Whether to include public media (songs, albums, and artists) from other users in your library and search results These preferences are saved immediately upon change and synced across all of your devices. ## Offline Storage If you have songs [cached for offline playback](./offline-playback), this section displays your current storage usage with a progress bar showing used vs. available space. You can clear all cached songs at once by clicking the "Clear All" button. ## Service Integrations Connect your own [Last.fm](../service-integrations#last-fm) or [ListenBrainz](../service-integrations#listenbrainz) account here, so Koel can submit what you listen to. You can disconnect them at any time. ## Subsonic API Key This tab shows your personal API key for connecting [Subsonic-compatible clients](./subsonic) to your Koel library. You can reveal, copy, or regenerate the key from here. ## QR Code Authentication This tab displays a QR code that you can scan to log in to [Koel Player](../mobile-apps.md) on your phone without having to manually enter your credentials. The code refreshes every ten minutes, but you can also manually refresh it. --- --- url: /plus/proxy-auth.md description: >- Configuring reverse proxy authentication via headers and IP allow-lists for Koel Plus. --- # Proxy Authentication Koel can be configured to authenticate users via a reverse proxy. Proxy authentication is useful in environments where users are already authenticated by a proxy server. :::danger Caution Proxy authentication bypasses Koel's built-in authentication system and relies on the proxy server to authenticate users. Make sure your proxy server is secure and properly configured. ::: To enable proxy authentication, set `PROXY_AUTH_ENABLED` in `.env` to `true` and provide the necessary configuration: * `PROXY_AUTH_USER_HEADER`: The header name that contains the unique identifier for the user, e.g., `remote-user`. * `PROXY_AUTH_PREFERRED_NAME_HEADER`: The header name that contains the user's preferred, humanly-readable name, e.g., `remote-preferred-name`. * `PROXY_AUTH_ALLOW_LIST`: A comma-separated list of allowed proxy IPs or Classless Inter-Domain Routing (CIDRs), e.g., `10.10.1.0/24` or `2001:0db8:/32`. If this value is empty, NO requests will be allowed (which essentially means proxy authentication is disabled). Now when a request comes in, Koel will look for specific headers to determine the user's identity. If the headers are found, Koel will attempt to log the user in automatically using the unique identifier. If the user is not found, Koel will create a new user with the unique identifier and the preferred name. ## Troubleshooting If proxy authentication isn't logging users in (you keep landing on Koel's password login screen despite a successful upstream auth handshake), check `storage/logs/laravel.log` for `[ProxyAuth]` entries. Koel logs a structured warning for each of the three failure modes: * **`Remote address not in allow list`** — the request reached Koel from an IP not covered by `PROXY_AUTH_ALLOW_LIST`. The log line includes both the observed `remote_addr` and the configured allow list. If your proxy runs in a Docker network or behind another layer, the address Koel sees may not be the one you expect — adjust the allow list or your `TrustProxies` config accordingly. * **`User header not present on request`** — Koel got the request but the configured `PROXY_AUTH_USER_HEADER` was missing. This usually means the proxy isn't propagating the header to the upstream request. Caddy users: double-check the `copy_headers` directive in your `forward_auth` block. Traefik / nginx users: confirm the header is in the forward-auth response and isn't being stripped before the upstream call. * **`Failed to create or update user from SSO headers`** — Koel received the header but failed to provision the user. The log entry includes the full exception; common causes are organization-membership constraints or invalid email derivation from the identifier. --- --- url: /plus/purchase-activation.md description: >- Koel Plus pricing, purchasing, license activation via web or CLI, and deactivation steps. --- # Purchase & Activation ## Pricing Koel Plus employs a super simple pricing model: You set your own price starting at {{ config.plus.price }}, **one time**. You read that right: no subscription, no recurring payments, no hidden fees, no per- or additional-seat costs — I hate that bullshit as much as you do. Pay **once**, and you get all current *and* future updates at no extra fee, forever. ::: tip If the price is still too steep for you, [let me know](mailto:me@phanan.net), and we can work something out. Also, if you’re a current or past sponsor ❤️, you’re eligible for a discount. ::: ## Purchase Click here to purchase Koel Plus. ## Activation After purchasing, you'll receive an email with a license key. To activate Koel Plus, you can use either the web interface or the command line. ### Via the Web Interface 1. Log into your Koel installation as an admin and locate the "Upgrade to Plus" button in the bottom-left corner of the screen. 2. Click the button to bring up the "Koel Plus" dialog: 3. Click "I have a license key," enter your key in the input field, and click "Activate." 4. If the key is valid, Koel will display a Success message and reload itself. ### Via the Command Line From the root of your Koel installation, run: ```bash php artisan koel:license:activate YOUR_LICENSE_KEY ``` If the key is valid, you'll see a Success message. ### Verification To verify that Koel Plus is activated, reload the web app if you haven't done so. Afterward, trigger the About Koel modal by clicking the icon in the bottom-right corner of the screen. You should see the information about your Koel Plus license in the modal. ## Deactivation Since Koel Plus only supports one installation per license key, if you're about to reinstall (including Docker re-creation) or move Koel to another server, **make sure to deactivate the license first**. You can do so via the command line: ```bash php artisan koel:license:deactivate ``` Reload Koel, and it will revert to the Community edition. You can re-activate it at any time following the steps above. --- --- url: /usage/radio.md description: >- Adding and streaming internet radio stations in Koel with ICY metadata support. --- # Radio You can also add and listen to radio stations with Koel. To manage radio stations, click on the "Radio" link from the sidebar to go to the "Radio Stations" page. Here you will find the list of available radio stations — those you have added and those made public by other users. You can mark radio stations as favorites and edit or delete the stations you own. ![Radio Stations screen](../assets/img/interface/radio-stations.avif) ::: info Information When adding or editing a radio station, Koel checks the URL to ensure it is a valid radio stream, i.e., it must have a content type of `audio/*`. An error will be thrown if it's not the case. ::: ## Streaming Radio Stations Streaming radio stations is not much different from streaming songs: You basically click the play button to start listening. Most of the interface elements you are familiar with, such as volume control, equalizer, and visualizer, work the same way. However, there are a couple of differences: * Radio stations do not use a queue, and therefore there are no next or previous buttons. Repeat and shuffle modes are also not available. * You cannot rewind, fast-forward, or seek a radio stream. * You cannot add radio stations to playlists. * Inapplicable interface elements such as the side sheet (artist, albums, lyrics, etc.) will be hidden. * If the radio stream supports [ICY metadata](https://liquidsoap.readthedocs.io/en/stable/content/icy_metadata.html), Koel will display the currently playing song name in the footer. * As of current, Koel doesn't maintain the "state" of radio stations. This means when you open Koel again, it will revert to the queued songs (or podcast episodes). This limitation applies to both the web interface and the [mobile apps](../mobile-apps.md) (which don't support radio stations yet). --- --- url: /usage/remote-controller.md description: >- Using Koel's Pusher-based remote controller to control a desktop instance from your phone. --- # Remote Controller Koel has a built-in remote controller that lets you control a desktop instance. ## Configuration In order for the remote controller to work, [register](https://dashboard.pusher.com/accounts/sign_up) for a Pusher account and create an app. Then, populate the app's credentials into `.env`: ``` PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= PUSHER_APP_CLUSTER= ``` Finally, reload your Koel instance to apply the changes. ## Usage You can access the remote controller by visiting `https:///remote`. :::tip You can pin the remote controller to your phone's home screen for quick access. ::: The remote controller will scan for an active Koel instance. If found, it will connect to it and allow you to control the playback as well as like/unlike the current song and adjust the volume. --- --- url: /guide/running-with-frankenphp.md description: >- Install FrankenPHP, configure Koel's Caddyfile, run artisan commands via php-cli, set up a systemd service on Ubuntu, and serve behind a reverse proxy. --- # Running with FrankenPHP [FrankenPHP](https://frankenphp.dev) is a PHP application server that packages a webserver (Caddy) and the PHP runtime into a single binary, with automatic HTTPS. Koel ships a `Caddyfile.example` at the project root to use with it. ::: tip Want a one-download install instead? The [Standalone Binary](/guide/standalone-binary) distribution from [koel/franken](https://github.com/koel/franken/releases) packages FrankenPHP, Koel, and a launcher into a single archive. This page is for installing FrankenPHP yourself as the runtime for an existing Koel install. ::: FrankenPHP is only the runtime. Install Koel first via [Pre-Compiled Archive](/guide/getting-started#using-a-pre-compiled-archive) or [Building from Source](/guide/getting-started#building-from-source), then use FrankenPHP as the webserver. ## Install FrankenPHP Pre-built binaries are published at [frankenphp.dev/docs/#install](https://frankenphp.dev/docs/#install). On Linux, the one-line installer fetches the right binary for your architecture: ```bash curl https://frankenphp.dev/install.sh | sh sudo mv frankenphp /usr/local/bin/ ``` ## Configure the Caddyfile From the Koel project root: ```bash cp Caddyfile.example Caddyfile ``` Edit `Caddyfile` and replace `localhost` with the domain you'll serve from. Using a real public domain enables automatic HTTPS via Let's Encrypt. ## Run it ```bash frankenphp run ``` That's it — Koel is now served on port 443 (and 80, redirected to HTTPS). ## Running artisan commands FrankenPHP ships its own PHP, available via the `php-cli` subcommand. Any `php artisan …` from Koel's CLI documentation becomes: ```bash frankenphp php-cli artisan ``` For example, `frankenphp php-cli artisan migrate` runs database migrations and `frankenphp php-cli artisan koel:sync` triggers a media scan. For convenience: `alias artisan='frankenphp php-cli artisan'`. If you also have system PHP installed (e.g. for `composer install`), running `php artisan ` against it works the same — both share the same `.env` and database. ::: warning DB\_HOST=localhost gotcha If your `.env` has `DB_HOST=localhost` with MySQL, FrankenPHP's PHP will fail to find the MySQL socket and you'll see `Checking database connection … ERROR` when running `koel:init`, even though HTTP serving works fine. Fix it one of two ways: * Override per-command: `DB_HOST=127.0.0.1 frankenphp php-cli artisan ` * Or change `.env` to `DB_HOST=127.0.0.1`. ::: For FrankenPHP CLI options not covered here (worker mode, multi-domain serving, custom PHP flags, etc.), refer to the [official FrankenPHP documentation](https://frankenphp.dev/docs/). ## Run as a systemd service (Ubuntu) For a production deployment, run FrankenPHP under `systemd` so it starts on boot and restarts on failure. Create `/etc/systemd/system/koel.service`: ```ini [Unit] Description=Koel (FrankenPHP) After=network.target [Service] Type=simple User=www-data WorkingDirectory=/var/www/koel ExecStart=/usr/local/bin/frankenphp run Restart=on-failure AmbientCapabilities=CAP_NET_BIND_SERVICE CapabilityBoundingSet=CAP_NET_BIND_SERVICE [Install] WantedBy=multi-user.target ``` Adjust `WorkingDirectory` and `User` to match where Koel lives on your host. Then enable and start it: ```bash sudo systemctl daemon-reload sudo systemctl enable --now koel sudo journalctl -u koel -f ``` ## Behind a reverse proxy If you're already running nginx (or another reverse proxy) and want to use FrankenPHP only as the PHP runtime, make two changes to your `Caddyfile`: 1. **Uncomment the `auto_https off` and `servers { trusted_proxies … }` block** at the top — `Caddyfile.example` ships it commented and ready for this case. Adjust the trusted-proxies CIDR if the reverse proxy lives on a different host. 2. **Bind the site block to a local port** — change `localhost {` to `:8001 {` and add `bind 127.0.0.1` as the first line inside the block. Everything else inside the site block stays as-is. Then point your existing reverse proxy at `127.0.0.1:8001` and let it terminate TLS as before. --- --- url: /service-integrations.md description: >- Setting up MusicBrainz, Last.fm, ListenBrainz, Spotify, and YouTube integrations for metadata, artwork, and scrobbling. --- # Service Integrations To further enhance your music experience, Koel supports several 3rd-party service integrations: MusicBrainz, Last.fm, ListenBrainz, Spotify, and YouTube. :::tip Note Koel prefers MusicBrainz/Wikipedia for artist and album information, the Cover Art Archive for album covers, and Wikidata for artist images. Spotify is used for images only when those have none. ::: ## MusicBrainz (+Wikipedia) [MusicBrainz](https://musicbrainz.org/) is a community-maintained open music encyclopedia that collects music metadata and makes it available to the public. Koel uses MusicBrainz (with cross-reference to Wikipedia) to retrieve artist and album information, and to find artist images and album covers. Album covers come from the [Cover Art Archive](https://coverartarchive.org/), which MusicBrainz runs together with the Internet Archive. Koel finds the cover by the album's MusicBrainz identifier and prefers it over Spotify. Albums without an identifier are skipped, so on an existing library run [`koel:fetch-mbids`](./cli-commands.md#koel-fetch-mbids) before [`koel:fetch-artwork`](./cli-commands.md#koel-fetch-artwork). Artist images come from [Wikidata](https://www.wikidata.org/), found by the artist's MusicBrainz identifier, and are also preferred over Spotify. If an album has no release year in its tags, Koel fills it in with the year the album was first released, according to MusicBrainz. A year from your tags is never replaced. You don't have anything to do to enable this integration, as it is enabled by default. However, you can disable it by explicitly setting `USE_MUSICBRAINZ` to `false` in `.env`. Do note that MusicBrainz [rate-limits](https://musicbrainz.org/doc/MusicBrainz_API/Rate_Limiting) its API. Koel tries to play nice by caching the data it retrieves and using a conformed user agent, which you can customize via the `MUSICBRAINZ_USER_AGENT` variable in `.env`. The default user agent is `Koel/ ()`. ## Last.fm Connecting Koel to Last.fm will instruct Koel to retrieve artist and album information from Last.fm as well as support scrobbling. To enable the connection: 1. [Create a Last.fm API account](https://www.last.fm/api/account/create). In the **Callback URL** field, fill in `https:///api/lastfm/callback` (though this is not used). 2. Populate the two variables `LASTFM_API_KEY` and `LASTFM_API_SECRET` in `.env` with the credentials grabbed from step 1. This enables Koel to retrieve media information from Last.fm. 3. To enable scrobbling, go to `https:///#/profile` and click the **Connect** button under Last.fm Integration. This connection is per-user, i.e. each user can connect their own Last.fm account. ## ListenBrainz [ListenBrainz](https://listenbrainz.org) is an open-source alternative to Last.fm, run by the MetaBrainz Foundation. Your listening history is released into the public domain instead of being locked away. Koel can submit your listens there, on its own or alongside Last.fm. No server-side setup is needed — each user connects their own account: 1. Go to `https:///#/profile` and find **ListenBrainz Integration**. 2. Paste the user token from your [ListenBrainz settings](https://listenbrainz.org/settings/) and click **Connect**. If you run your own ListenBrainz server, point Koel at it with the `LISTENBRAINZ_API_ENDPOINT` variable in `.env`. ## Spotify :::warning Spotify API Access As of early 2026, Spotify's Web API [requires a Premium subscription](https://developer.spotify.com/blog/2026-02-06-update-on-developer-access-and-platform-security) for new developer apps. If you don't have one, you can still use MusicBrainz and Last.fm for artwork and metadata. ::: Integration with Spotify allows Koel to fetch more metadata like album arts and artist images. To enable the integration: 1. Register for a developer account and create an app in [Spotify dashboard](https://developer.spotify.com/dashboard/) 2. Populate `SPOTIFY_CLIENT_ID` and `SPOTIFY_CLIENT_SECRET` in `.env` with the credentials from step 1. ## YouTube With YouTube integration, whenever a song is played, Koel will search YouTube for related videos and display them in the sidebar for you to watch without leaving Koel. The only thing you need to do is fill in `.env` with your `YOUTUBE_API_KEY`, which can be obtained via the following: 1. [Create a new Google Project](https://console.developers.google.com/) 2. From the project's Dashboard, click “ENABLE API” and make sure “YouTube Data API v3” is enabled 3. From the project's Credentials, click Create credentials → API Key → Server key :::tip Limitations YouTube integration is always disabled on mobile due to OS restrictions. Also, you interact with the videos via YouTube controls. Koel's equalizer, volume, seeker, play/pause buttons, etc., doesn't have an effect on the videos. ::: --- --- url: /plus/sso.md description: >- Configure Google or generic OpenID Connect SSO for Koel Plus, including environment variables and user management. --- # Single Sign-On Apart from the default authentication mechanism with email and password, users can also log in to Koel Plus via Single Sign-On (SSO). Koel supports Google and any OpenID Connect–compliant identity provider (Authentik, Authelia, Keycloak, Zitadel, …). ## Google To enable SSO with Google, you need to create a new OAuth client ID in the [Google Cloud Console](https://console.cloud.google.com/apis/credentials). Pick "Web application" as the application type, and set the "Authorized redirect URIs" to `https:///auth/google/callback`, replacing `` with your actual Koel domain. Create a new Google OAuth client ID Afterward, take note of the client ID and client secret values. You can then add them to your `.env` file: ``` SSO_GOOGLE_CLIENT_ID= SSO_GOOGLE_CLIENT_SECRET= ``` Finally, set the Google-hosted domain that you want to restrict logins. For example, if you only accept users from `your-koel.com`: ``` SSO_GOOGLE_HOSTED_DOMAIN=your-koel.com ``` Save the `.env` file and reload Koel. You should now see a "Log in with Google" button on the login page: Clicking on the Google button will open a new window where you can log in with your Google account (make sure to allow pop-ups if you have a pop-up blocker enabled). ## OpenID Connect Koel works with any OIDC-compliant provider. Set the issuer URL (the one that exposes `/.well-known/openid-configuration`), the client ID, and the client secret: ``` SSO_OIDC_ISSUER=https://your-idp.example.com SSO_OIDC_CLIENT_ID= SSO_OIDC_CLIENT_SECRET= ``` Set the redirect URI in your IdP to `https:///auth/oidc/callback`. Koel asks for the `openid`, `email`, and `profile` scopes. Optionally, customize the label shown on the login button (defaults to "OpenID Connect"): ``` SSO_OIDC_BUTTON_LABEL=Authentik ``` Save the `.env` file and reload Koel. A corresponding button appears on the login page. ## User Management When a user logs in via SSO for the first time, a new user account will be created in Koel with the email address, name, avatar, and the SSO ID obtained from the SSO provider. If, however, there's already an existing user with the same email address, Koel will merge the two accounts with a sensible merging strategy. New SSO users are given the **User** role by default. To place them in the **Guest** role instead, set `SSO_DEFAULT_ROLE=guest` in your `.env` file. SSO users can update their name and avatar, but not their email address. Furthermore, a new user created via SSO does not have a password set and won't be able to log in via the email+password method. --- --- url: /guide/standalone-binary.md description: >- Install Koel from the single-archive standalone distribution (FrankenPHP + Caddy + PHP + Koel app), customize the database, and run as a systemd service. --- # Standalone Binary The [koel/franken](https://github.com/koel/franken/releases) distribution packages [FrankenPHP](https://frankenphp.dev) (Caddy + the PHP runtime) with the compiled Koel application into a single archive — no Composer, Node, or system PHP needed on the host. Uses SQLite by default for zero-setup operation. The easiest way to get Koel running. ## Install and run Download the archive matching your platform from the [releases page](https://github.com/koel/franken/releases) — available for `linux-x86_64`, `linux-aarch64`, `mac-x86_64`, and `mac-arm64`. Extract it and run: ```bash tar -xzf koel-franken-v9.3.2-linux-x86_64.tar.gz cd koel-franken-v9.3.2-linux-x86_64 # plain HTTP on :8000 ./koel php-server --listen :8000 # or, with a real domain (auto-HTTPS via Let's Encrypt): ./koel php-server --domain koel.example.com ``` ::: warning On macOS The included FrankenPHP binary isn't code-signed, so Gatekeeper blocks it on first run with a "frankenphp can't be verified" dialog. Strip the quarantine flag from the extracted directory once and the binary will run from then on: ```bash xattr -dr com.apple.quarantine . ``` Run this from inside the extract directory before `./koel php-server …`. ::: On first run, Koel sets up `$HOME/.koel/`, generates an app key, and creates a fresh SQLite database. FrankenPHP then starts serving on the chosen port or domain. You can now [set up the storage](../cli-commands.md#koel-storage) and start using Koel. ## What lives where Everything writable lives under `$HOME/.koel/`: | Path | What | |--------------------------------------|----------------------------------------------------------------------------| | `$HOME/.koel/.env` | Environment file (Koel's config) | | `$HOME/.koel/db.sqlite` | SQLite database | | `$HOME/.koel/storage/` | Laravel storage path (logs, sessions, cache, uploaded images) | | `$HOME/.koel/storage/app/artifacts/` | Transcodes, downloaded podcasts, temp downloads | | `$HOME/.koel/php.d/koel.ini` | PHP-INI overrides (display\_errors off, error\_reporting mask, 512M uploads) | The installation directory itself doesn't change after extraction — anything worth backing up or moving lives in `$HOME/.koel/`. ## Running Artisan commands The distribution ships an `./artisan` shortcut next to `./koel`: ```bash ./artisan koel:sync ./artisan tinker ``` It's a shortcut for `./koel php-cli artisan …`, using the same PHP runtime and environment. See [Running with FrankenPHP](/guide/running-with-frankenphp#running-artisan-commands) for the full set of caveats (notably the `DB_HOST=localhost` MySQL-socket gotcha). ## Customization and migration To use a different database or change any other setting, edit `$HOME/.koel/.env`. If you changed the database connection, run `./artisan migrate --force` afterwards. To move an existing Koel install into the standalone setup: 1. Copy your `.env` to `$HOME/.koel/.env`. 2. Copy your media files into place, or set `MEDIA_PATH` in `.env` to point at where they already live. 3. Copy `storage/app/public/images/`, your search indexes, and any other data from the old install into `$HOME/.koel/storage/`. ## Update To upgrade to a newer release: 1. Download the new archive from the [releases page](https://github.com/koel/franken/releases). 2. Extract it over the existing directory. 3. Restart the server. Your data in `$HOME/.koel/` — settings, database, uploaded images, search indexes — is preserved across upgrades. ::: tip New environment variables New Koel versions occasionally introduce new settings. After upgrading, compare your `~/.koel/.env` against `app/.env.example` to spot any new keys you might want to set. ::: ## Running as a systemd service (Ubuntu) For a production deployment, run Koel under `systemd` so it starts on boot and restarts on failure. Place the extracted directory somewhere stable (e.g. `/opt/koel`) and create `/etc/systemd/system/koel.service`: ```ini [Unit] Description=Koel (standalone) After=network.target [Service] Type=simple User=koel WorkingDirectory=/opt/koel ExecStart=/opt/koel/koel php-server --domain koel.example.com Restart=on-failure AmbientCapabilities=CAP_NET_BIND_SERVICE CapabilityBoundingSet=CAP_NET_BIND_SERVICE [Install] WantedBy=multi-user.target ``` Adjust `WorkingDirectory`, `User`, and the `--domain` (or `--listen`) flag to match your host. Then enable and start it: ```bash sudo systemctl daemon-reload sudo systemctl enable --now koel sudo journalctl -u koel -f ``` ## Behind a reverse proxy If you're already running nginx (or another reverse proxy) in front of Koel, bind Koel to a local port: ```bash ./koel php-server --listen 127.0.0.1:8001 ``` Then point your existing reverse proxy at `127.0.0.1:8001` and let it terminate TLS as before. --- --- url: /usage/streaming.md description: >- Streaming methods (PHP, x-accel-redirect, x-sendfile), transcoding with FFmpeg, cloud storage streaming, and crossfade. --- # Streaming Music ## Streaming Methods Koel supports three streaming methods which can be configured via the `STREAMING_METHOD` setting in `.env` file: * `php`: Uses native PHP file reading. This is the default method. * `x-accel-redirect`: Only applicable if your webserver is nginx. Uses nginx's [X-Accel](https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/) module, designed for serving larger contents directly to the end user. Refer to [`nginx.conf.example`](https://github.com/koel/koel/blob/master/nginx.conf.example) for a sample nginx configuration file. * `x-sendfile`: Only applicable if your webserver is Apache (with or without a proxy server like nginx). Uses Apache's [mod\_xsendfile](https://tn123.org/mod_xsendfile/) module. You'll need to install and configure the module manually. A sample configuration is as follows: ```apache LoadModule xsendfile_module libexec/apache2/mod_xsendfile.so # These configuration can be put in the VirtualHost directive as well XSendFile on XSendFilePath /mnt/media ``` Note that although its home page denotes "Apache2/Apache2.2," the module itself is known to work with later versions of Apache as well. When streaming from a cloud storage like S3 or Dropbox, Koel will simply use the presigned URL provided by the storage service. A **pre-signed URL** is a secret, generated on-the-fly URL that provides temporary access to your (private) files stored in the cloud. Koel sets the expiration time of these URLs to 1 hour. ## Lossless Transcoding Koel supports transcoding your lossless audio to AAC, a format [widely supported](https://caniuse.com/?search=aac) by modern browsers. To enable this feature, you'll need to have [FFmpeg](https://ffmpeg.org/) installed on your server and set its executable path via the `FFMPEG_PATH` setting in the `.env` file. If you use the [Docker image](https://github.com/koel/docker/), this is already done for you. The transcode quality can be controlled via `TRANSCODE_BIT_RATE`. The default value is `128`, which means the audio will be transcoded to 128 kbps AAC. You can set it to a higher value, such as `192`, to improve the audio quality at the cost of a larger file size. Practically, it's almost impossible to differentiate between 320 kbps AAC and lossless audio. As transcoding can take some time (albeit typically several seconds) and resources, Koel will cache the transcoded files for later use. You should also expect a slight delay when you first play a song that requires transcoding, as Koel will need to do its transcoding magic first (which can involve downloading the file from your cloud storage if necessary). ### FLAC Transcoding Since FLAC is [well-supported](https://caniuse.com/?search=flac) by modern browsers, Koel streams FLAC files as-is by default and maintains the lossless experience. You can also force transcoding FLAC as well by setting `TRANSCODE_FLAC` to `true` in the `.env` file. ### Forced Transcoding For audio formats that aren't [widely supported by browsers](https://caniuse.com/?search=audio%20format), such as AIFF or APE, Koel will always resort to transcoding. If you upload files in such formats, make sure the FFmpeg setup and configuration (see above) are correct. ### Transcoding on Mobile On a mobile device where data usage is a concern, the user might want to instruct Koel to transcode all songs (regardless of their formats) to a lower bit rate to save bandwidth. This can be done via the [Preferences screen](./profile-preferences#preferences). ## Crossfade Koel supports crossfading between songs, where the currently playing track fades out while the next track fades in, creating a seamless transition. The crossfade duration can be configured from 0 to 15 seconds via the [Preferences screen](./profile-preferences#preferences). A value of 0 disables crossfade. :::tip Crossfade is automatically skipped for tracks shorter than twice the crossfade duration, and is not applied when the repeat mode is set to "Repeat One" or during radio playback. ::: --- --- url: /usage/subsonic.md description: >- Connecting Subsonic-compatible apps to your Koel library with your personal API key. --- # Subsonic Clients Koel speaks the [Subsonic / OpenSubsonic API](https://opensubsonic.netlify.app/), so you can connect any Subsonic-compatible app to your Koel library and stream your music from wherever that app runs. Feishin streaming a Koel library over the Subsonic API ## Get Your API Key 1. Go to your profile. 2. Switch to the **Subsonic** tab. 3. Reveal or copy the key. Your Subsonic API key was generated automatically when your account was created. It stays the same until you regenerate it. ## Connect a Client In your Subsonic client, point the server URL at your Koel installation — the same URL you use in the browser — and enter: * **Username** — your Koel email address * **Password** (or **API key**, if the client offers it) — your Subsonic API key The Subsonic API key replaces your Koel password here — do **not** enter your actual Koel account password. Your Koel password is never used by Subsonic clients. Your Koel library will appear in the client right after you save the connection. ## Compatible Clients Any client that speaks Subsonic or OpenSubsonic will work. A few popular ones: * [Feishin](https://github.com/jeffvli/feishin) — macOS, Windows, Linux * [Amperfy](https://github.com/BLeeEZ/amperfy) — macOS, iOS, iPadOS * [Symfonium](https://symfonium.app/) — Android * [Tempo](https://github.com/CappielloAntonio/tempo) — Android * [substreamer](https://substreamer.org/) — iOS, Android, Web * [play:Sub](https://michaelsapps.dk/playsubapp/) — iOS ## Regenerate Your Key If you think your key has been exposed, regenerate it from the same Subsonic tab. Click **Regenerate Key** and confirm. ::: warning Regenerating the key invalidates the old one straight away. Any client still using it will stop working until you update its credentials. ::: ## What Koel Supports Koel implements the OpenSubsonic API for browsing, streaming, search, playlists, favorites, ratings, internet radio stations, podcasts, and lyrics. Sharing, bookmarks, chat, and play-queue sync endpoints are not implemented. --- --- url: /usage/themes.md description: Built-in theme selection and custom theme support in Koel Plus. --- # Themes Koel supports multiple themes out of the box. You can preview and choose one from the [Profile & Preferences](./profile-preferences.md#themes) screen. Koel Plus users can also [create their own themes](../plus/custom-themes.md). --- --- url: /plus/ticketmaster.md description: >- Integrating Ticketmaster for live event listings on artist pages, including API key setup and country detection. --- # Ticketmaster [Ticketmaster](https://www.ticketmaster.com/) is one of the most popular ticketing platforms for events, tours, and concerts. By integrating with Ticketmaster, you can access the upcoming events in your area by going to the Events tab found on the *Artist* screen. Clicking on an event will take you to the corresponding webpage where you can purchase tickets and learn more about the event. ![Live Events](../assets/img/plus/live-events.avif) ## Setup The only required configuration for this integration is the `TICKETMASTER_API_KEY` environment variable. To get your API key, [create a free developer account](https://developer-acct.ticketmaster.com/user/login). A default app will be created and approved for you, which you can find under the *My Apps* tab. Feel free to rename it to something more descriptive. Default app created by Ticketmaster Copy the Consumer Key and paste it into the `TICKETMASTER_API_KEY` environment variable in your `.env` file: ```dotenv TICKETMASTER_API_KEY=sample-qJraSgjtTrHbdzEwyfW6yqKowcFGmFbi ``` This is all you need to get started. Reload the server, and you should see the Events tab appear on any Artist screen. ### (Slightly More) Advanced Configuration By default, Koel will search for events in the United States. If you want to search for events in a different country, set the `TICKETMASTER_DEFAULT_COUNTRY_CODE` environment variable in your `.env` file to that country's [two-letter code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). For example, to search for events in Germany, set the variable to `DE`. If you want to search for events in the user's country, Koel can make use of [IPinfo](https://ipinfo.io/)'s API. IPinfo is a popular geolocation service with a generous free ("Lite") plan. After registering for the Lite plan, you can find your token under the *API Token* tab. IPinfo's API Token screen Populate this token in the `IPINFO_TOKEN` environment variable in your `.env` file: ```dotenv IPINFO_TOKEN=sample-52d06efe5f5b10d ``` Reload the server, and Koel will attempt to look up the user's country by sending their IP address (and only that) to IPinfo's API. If successful, Koel will use the detected country code to search for events. If not, it will fall back to the default country code, which in turn defaults to `US` as described above. --- --- url: /troubleshooting.md description: >- Common Koel issues and solutions, debugging steps, log locations, and how to get help. --- # Troubleshooting While Koel strives to be as user-friendly and bug-free as possible, things can still go wrong. Don't panic! This page will guide you through the process of troubleshooting your issues. ## First Steps When a wild error appears, the very first step you should take is to check `storage/logs/laravel.log`. More often than not, this file will provide you with a lot of details and hints on what went wrong. This is so important that it is worth repeating: :::danger Always check the log If you receive an error, the first step is to take a look at `storage/logs/laravel.log`. ::: Next, look at the browser console for any JavaScript errors. While you're at it, check the network tab for any failed requests and try disabling the network cache. Also, try clearing the cache, reinstalling dependencies, and recompiling the front-end assets. Below are a couple of commands that might help in this area: ```bash # Remove Composer vendor/ directory and reinstall the packages rm -rf vendor && composer install # Clear node_modules, re-install, and re-build the front-end assets rm -rf node_modules && pnpm install && pnpm run build # Clear the Laravel cache. This command is automatically run after `composer install`. php artisan cache:clear # Clear the Laravel config cache php artisan config:clear ``` ### Run Koel Doctor For Linux and macOS systems, Koel comes with a `doctor` command that checks your setup for common issues. You can run it by executing the following **as your web server user**: ```bash php artisan koel:doctor ``` This command will check your environment and configuration for common issues (file/folder permissions, storage setup, server configuration, etc.) and provide you with a report. An example output might look like this: ![Koel Doctor](./assets/img/doctor.webp) If you're still stuck, check below for a couple of common issues and their solutions. ## Common Issues ::: Koel is stuck at the loading screen after a fresh installation If Koel isn't getting passed the loading screen, but there's no error in the console, there might be a cache issue. Try clearing your browser's cache. A neat trick is to open the DevTools panel, go to the Network tab, tick the "Disable cache" checkbox, and refresh Koel with DevTools open. ::: details You run into a permission issue Make sure your web server has the necessary permissions to *recursively* read/write to critical folders like `storage`, `bootstrap/cache`, and `public`. Also, remember to run artisan commands as your web server user (e.g. `www-data` or `nginx`), **never** as `root`, as these commands might create files that your web server user must have access to. If you use the Docker installation, for example, run the scan command as the `www-data` user as follows: ```bash docker exec --user www-data php artisan koel:scan ``` ::: ::: details You receive a `Class 'Pusher' not found` error Add or set `BROADCAST_CONNECTION=log` in your `.env` file. This will instruct Laravel to use `log` as the default broadcast driver instead. ::: ::: details You receive an "Unknown error" when scanning using the web interface Try scanning from the command line with `php artisan koel:scan`. Most of the time, you should receive a more detailed, easier to debug, message. See also: [Music Discovery](usage/music-discovery). ::: ::: details You receive an `Integrity constraint violation: 1062 Duplicate entry for key 'artists_name_unique'` error when scanning Set your database and table collation to `utf8_unicode_ci` or `utf8mb4_unicode_ci`. ::: ::: details You receive an \ error when running `pnpm` This most likely has little to do with Koel but more with your node/npm/pnpm environment and installation. Deleting `node_modules` and rerunning the command sometimes help. ::: ::: details Song stops playing, and you receive a `Failed to load resource: net::ERR_CONTENT_LENGTH_MISMATCH` error This may sometimes happen with the native PHP streaming method. Check [Streaming Music](usage/streaming) for alternatives. ::: ::: details You receive a `Multiple licenses found` warning when running `koel:license:status` command Koel Plus only requires one license key. If it detects more than one key in the database, the warning will be issued. Most of the time this shouldn't cause any problem, but if you're experiencing issues, try emptying the `licenses` table and re-activating your license key. ::: ::: details You've reinstalled or moved Koel to another server and receive a Koel Plus license error This typically comes from the fact that Koel Plus supports only one installation per license key. To fix the issue, you can either disable the current activation via the Lemon Squeezy dashboard or email me for support. ::: ## Reinstalling Koel In the worst case scenario, you can always reinstall Koel. Although Koel doesn't provide a built-in way to reinstall itself, you can do so manually by following these steps: 1. Backup your database 2. Have you backed up your database yet? 3. No, seriously, make sure you have a backup of your database 4. (Koel Plus only) [Deactivate](plus/purchase-activation.md#deactivation) your Koel Plus license 5. Back up the `public/img` directory. This is where your album art, artist images, user avatars, etc. are stored. 6. Delete or empty the root Koel directory 7. Follow the [installation guide](guide/getting-started#installation) to install Koel afresh 8. Restore your database and the `public/img` directory 9. (Koel Plus only) [Reactivate](plus/purchase-activation.md#activation) your Koel Plus license By now you should have a fresh Koel installation with all your data intact and hopefully without the issue you were facing. ## Ask for Help If you're still stuck, the [issue page](https://github.com/koel/koel/issues) on GitHub is a good place to ask for help. Remember to be civil and patient. --- --- url: /usage/two-factor-authentication.md description: >- Set up two-factor authentication on your Koel account to add a one-time code to every login. --- # Two-Factor Authentication ::: warning Mobile app compatibility Older versions of Koel Player may not support two-factor authentication. Update to the latest version before enabling 2FA on an account you sign in to from mobile. ::: Two-factor authentication (2FA) adds a second step to logging in. After your email and password, Koel asks for a one-time code from your authenticator app. Without that code (or one of your recovery codes), nobody can sign in to your account — even with your password. ## Enable Two-Factor Authentication 1. Go to your profile. 2. Switch to the **Security** tab. 3. Click **Enable Two-Factor Authentication**. 4. Scan the QR code with your authenticator app (1Password, Authy, Google Authenticator, Microsoft Authenticator, etc.). 5. Enter the 6-digit code your app shows you and click **Confirm**. 6. Save the recovery codes Koel reveals next. ## Save Your Recovery Codes When you enable 2FA, Koel shows you eight recovery codes. **Save them somewhere safe** — a password manager, a printout in a drawer, an encrypted note. You won't be able to see them again. Each recovery code can be used once if you ever lose access to your authenticator app. After a code is used, it disappears. If you run out (or just want fresh ones), open the **Security** tab and click **Regenerate Recovery Codes**. You'll need to confirm with a code from your authenticator app or one of your existing recovery codes. The old codes stop working as soon as new ones are generated. ## Logging In With Two-Factor Authentication 1. Enter your email and password as usual. 2. Koel asks for a code. 3. Enter the 6-digit code from your authenticator app — or a recovery code if you don't have your phone. 4. Click **Verify**. You're in. ## Disable Two-Factor Authentication 1. Go to the **Security** tab. 2. Click **Disable**. 3. Confirm, then enter a code from your authenticator app or a recovery code. Your account is back to email-and-password only. The 2FA secret and recovery codes are cleared — re-enabling later starts the setup from scratch. --- --- url: /usage/user-management.md description: >- User roles (admin, user, manager, guest), creating and inviting users, and managing permissions in Koel. --- # User Management Koel supports multiple users with different roles and permissions. * **Admins** can do basically anything: manage settings, users, and the shared music library * **Users** can access the shared library and manage their own profile and preferences. * **Managers** can manage users and the shared library, but not settings. * **Guests** can only play music shared by others — they have no library of their own and cannot upload, edit, or delete songs. Useful when you want to share a Koel Plus instance with friends or family without giving them a personal library. :::tip Multi-library support In the Community edition, all users share a common library (though playlists, favorites, and other stats are private). In [Koel Plus](../plus/what-is-koel-plus), each user manages their own library with the ability to share and collaborate with others. A private library can only be managed by its owner. ::: ## First Admin User Upon installation, Koel prompts to create a first (default) admin user. If you're using the [Docker image](../guide/getting-started#using-docker), the admin user will be created automatically with these credentials: ``` Email: admin@koel.dev Password: KoelIsCool ``` ## Changing First Admin Password If you forgot the default admin’s password and are unable to log in, you can change it via the command line: ```bash php artisan koel:admin:change-password ``` ## Adding More Users With the `manage users` permission (admin and manager roles), you can add more users and manage their profiles under Manage → Users. If Koel has been [configured](../guide/getting-started#configure-a-mailer) with a mailer, you can also invite a user via email. You cannot add, invite, edit, or delete users whose roles are higher than yours. ## Changing User Roles User roles can be changed via the web interface from the Users page if the current user has the `manage users` permission, or via the [command line](../cli-commands#koel-admin-set-user-role). --- --- url: /usage/web-interface.md description: >- Guide to browsing, searching, playing, editing, downloading, and sharing music through Koel's web interface. --- # Using the Web Interface Koel’s web interface is designed to be as simple and intuitive as possible, drawing inspiration from the best music players out there. Here’s a quick rundown of what you can do with it. ## Browsing and Searching for Music The navigation bar on the left side of the screen is where you can find all the main features of Koel. You can browse your music library by songs… ![All Songs screen](../assets/img/interface/all-songs.avif) artists… ![Artists screen](../assets/img/interface/artists.avif) albums… ![Albums screen](../assets/img/interface/albums.avif) or genres: ![Genres screen](../assets/img/interface/genres.avif) You can click on any artist, album, or genre name to see all the songs that belong to it. On the artist and album screens, you can also find other albums and songs by the same artist as well as extra metadata like the artist or album's information when [configured](../service-integrations). On any screen with a song list (a "song-list screen") you can: * Sort the list by clicking on the column headers * Filter the list by typing in the filter box (marked with ) * Select a song by clicking on it. To select multiple songs, hold down Shift or Ctrl/Cmd while clicking. * Drag and drop one or more songs to reorder them when applicable * Drag and drop one or more songs to applicable menu items to the left to perform actions like adding to a playlist, queueing, or marking them as favorite * Right-click on a song to bring up a context menu with options to play, queue, mark as favorite, add to a playlist, and more To search globally for songs, artists, and albums, see [Instant Search](./search). ## Streaming Music Play a song by double-clicking on it or pressing Enter on either a song list row or a song card. Queue a song (or multiple songs) by right-clicking and choose one of the Queue options, or by dragging and dropping it into the Queue icon in the footer. You can use the dedicated button (with the icon) to shuffle the current list or the selected songs. To play in order instead of shuffling, hold down Alt/Option while clicking the button. When a song is playing, you can control playback using the buttons at the bottom of the screen as well as rewind/fast-forward the song by clicking and dragging the progress bar. ![Footer controls](../assets/img/interface/footer.avif) From the same area, you can also: * click the rotating cover art to navigate to the Queue screen and scroll to the currently playing song * like/unlike the current song * switch between repeat modes * toggle the Queue screen * show the visualizer * show the equalizer * control the volume * toggle full-screen mode Right-clicking the footer area brings up the context menu for the currently playing song. ## Editing Songs As a user with `manage songs` permission (Community edition) or the song owner (Koel Plus), you can edit a song's metadata by right-clicking on it and choosing "Edit…". A dialog will appear, allowing you to change the song's title, artist, album, genre, lyrics, and more. You can also edit multiple songs at once, in which case the changes will be applied to all of them. ## Downloading Songs If `ALLOW_DOWNLOAD` is set to `true` (the default value) in your `.env` file, you can download a song by right-clicking on it and choosing "Download." You can also download several songs, all songs from an album, all songs by an artist, or a whole playlist as a zip file via the "Download" buttons found on according screens or context menus. ## Deleting Songs As a user with `manage songs` permission (Community edition) or the song owner (Koel Plus), you can delete a song by right-clicking on it and choosing "Delete from Filesystem." You'll be prompted to confirm the deletion, and once confirmed, the song will be removed from the database and the filesystem. :::danger Backup on Delete By default, Koel keeps a backup of the song before deleting it. You can disable this behavior by setting `BACKUP_ON_DELETE` to `false` in your `.env` file, but be aware that this will make it impossible to recover a song once it's deleted. ::: ## Share and Embed a Song Share a song by right-clicking on it and choosing *Share → Copy URL*. This will copy a URL to the clipboard that can then be shared with anyone with a Koel account. You can also embed a song for public sharing by right-clicking on it and choosing *Share → Embed…*. See [Embedding](./embedding) for more details. ## Keyboard Shortcuts Koel has a number of keyboard shortcuts to make it easier to navigate and control playback. Note that these shortcuts are only triggered when the focus is not on an input field (including the volume control and the music track) or a textarea. * h shows the Home screen * q toggles the Queue screen * f puts the focus into the search box and opens the [instant search](search.md) screen * Enter plays a song. If multiple songs are being selected, Enter adds them to the bottom of the queue, Shift+Enter queues them to top. Adding a Cmd or Ctrl into the combo plays the first selected song right away. * Space toggles playback * j plays the next song in queue * k plays the previous song in queue * l marks/unmarks the current song as favorite * r circles through repeat modes (no repeat, repeat all, repeat one) * → seeks forward 10 seconds * ← seeks backward 10 seconds * ↑ increases volume by 10% * ↓ decreases volume by 10% * m mutes/unmutes * Cmd/Ctrl+a selects all songs in the current song-list screen when the list is focused * Delete removes selected song(s) from the current queue/playlist With a browser that supports [MediaSession](https://developer.mozilla.org/en-US/docs/Web/API/MediaSession) — basically, all modern browsers — you can control Koel using hardware media keys without even having Koel *or the browser* focused. --- --- url: /plus/what-is-koel-plus.md description: >- Overview of Koel Plus premium features including multi-library, collaboration, cloud storage, SSO, white labeling, and AI assistant. --- # What Is Koel Plus? ## Introduction Koel Plus is the premium version of Koel. It offers additional features and enhancements on top of the Community edition, including but not limited to: * **Multi-library Support**: Each user has their own library, which they populate by uploading their own music. * **Music Sharing**: By default, uploaded music is private to the user who uploaded it. However, users can choose to share their music with others by marking songs as public. * **[Collaboration](./collaboration)**: Invite other users to collaborate on playlists, allowing them to add, remove, and reorder songs. * **[Cloud Storage Support](./cloud-storage-support)**: Remote and cloud storage drivers like SFTP, Amazon S3 (or any S3-compatible service), Dropbox, and WebDAV (NextCloud, Owncloud, …) are supported in addition to local storage. * **[Single Sign-On (SSO) Support](./sso)**: Log in with your existing credentials from another service like Google. * **[Authentication via Proxy-Authorization Header](./proxy-auth)**: Seamlessly log in users already authenticated via a proxy server, allowing Koel to be used in a corporate environment. * **[Media Browser](./media-browser)**: A file-explorer-like interface that allows browsing music directly at a file level, useful for libraries without proper ID3 tagging. * **[White Labeling](white-labeling.md)**: Customize the app's branding elements (name, logo, cover) to match those of your organization's. * **[Custom Themes](custom-themes.md)**: Create and apply your own themes. * **[AI Assistant](./ai-assistant)**: Interact with your music library using natural language — play music, manage playlists, and more. New features are constantly planned or in active development. ## Do you need Koel Plus? Since Koel Plus's flagship feature is multi-library support, it's most useful in a multi-user environment, such as a family or an organization. If you're a single user, you might not need Koel Plus. However, if you want to host your media files on a cloud storage service like Amazon S3 or Dropbox, or if you want some of the more-niche features above, you'll need Koel Plus. [Purchasing Koel Plus](./purchase-activation) is also a good way to financially support the active development of Koel. --- --- url: /guide/what-is-koel.md description: >- An overview of Koel, a modern, open-source personal music streaming service built with Laravel and Vue.js. --- ![Koel's homepage](../assets/img/interface/home.webp) # What Is Koel? A complete music streaming solution that hosts and streams your own music collection, Koel (also stylized as koel, with a lowercase k) is designed to be: * **Modern and snappy** – Unlike some of our music, we’re not living in the 90s anymore. Koel looks and functions like a true modern application: clean, gorgeous, and fast. As a matter of fact, it won’t even work on prehistoric browsers. * **Easy to set up** – Born from a personal frustration of failing to find something easy to install, Koel is dead-simple to get up and running, utilizing some of the most popular and proven technologies in the web development world: [Laravel](https://laravel.com), [Vue](https://vuejs.org), and [Docker](https://www.docker.com/). * **Easy to use** – As Koel’s interface draws inspiration from proven streaming services like Spotify and Apple Music, you will feel right at home where everything is exactly where you’d expect it to be. * **Feature-rich** – Apart from the standard functionalities (stream, search, song/album/artist browsing, playlist management, etc.), Koel provides advanced features like smart playlists, equalizer, visualizers, 3rd-party service integrations, and many more. * **Plays well with others** – Koel speaks the [Subsonic / OpenSubsonic API](/usage/subsonic), so you’re not stuck inside the web interface. Point any Subsonic-compatible client on macOS, Windows, Linux, iOS, or Android at your Koel library and stream from anywhere. * **Open source** – Koel’s source code is [available on GitHub](https://github.com/koel/koel) under the [MIT License](https://opensource.org/license/mit), meaning you can use it for free and even contribute to its development. To help finance the project, you can either purchase [Koel Plus](/plus/what-is-koel-plus), get the [mobile app](/mobile-apps), or [become a sponsor](https://github.com/sponsors/phanan). :::tip TL;DR Koel is a music streaming solution that actually works™. ::: --- --- url: /plus/white-labeling.md description: 'Customizing the app name, logo, and default cover image in Koel Plus.' --- # White Labeling Another Plus-only feature is the ability to customize the branding of the app, a practice commonly known as "white-labeling." Koel Plus users with administrative privileges can change the app name, logo, and the default cover image used for artists, albums, and other media without an associated artwork. These settings can be found on the "Settings" page, under the "Branding" tab. ::: tip For the best visual results, use a square image of at least 512×512 pixels for the logo and the cover. SVG is fully supported and recommended. :::