NixOS module for Koel - a simple web-based personal audio streaming service.
- 🎵 Declarative Koel configuration
- 🗄️ SQLite database (zero configuration)
- 🔍 TNTSearch engine (no additional services needed)
- 🚀 Automatic PHP-FPM setup
- 🌐 Works with Caddy reverse proxy (automatic HTTPS)
- 📦 Fully declarative, NixOS-native
- ⚡ Perfect for homelab environments
Add to your flake.nix:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
koel-nix.url = "github:DzmingLi/koel-nix";
};
outputs = { self, nixpkgs, koel-nix }: {
nixosConfigurations.yourhost = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
koel-nix.nixosModules.default
./configuration.nix
];
};
};
}{ config, pkgs, ... }:
{
services.koel = {
enable = true;
# Use agenix to manage the app key
appKey = "/run/agenix/koel-app-key";
listen = "/run/phpfpm/koel.sock";
database.type = "sqlite-persistent";
storage = {
driver = "local";
mediaPath = "/srv/music";
};
search.driver = "tntsearch";
streaming.method = "php";
};
# Caddy reverse proxy with automatic HTTPS
services.caddy = {
enable = true;
# email = "your-email@example.com"; # Optional: for Let's Encrypt notifications
virtualHosts."music.example.com".extraConfig = ''
root * /var/lib/koel/public
php_fastcgi unix//run/phpfpm/koel.sock
file_server
encode gzip
'';
};
networking.firewall.allowedTCPPorts = [ 80 443 ];
}First, add agenix to your flake inputs:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
koel-nix.url = "github:DzmingLi/koel-nix";
agenix.url = "github:ryantm/agenix";
};
outputs = { self, nixpkgs, koel-nix, agenix }: {
nixosConfigurations.yourhost = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
koel-nix.nixosModules.default
agenix.nixosModules.default
./configuration.nix
];
};
};
}Generate and encrypt the app key:
# Generate a random key
head -c 32 /dev/urandom | base64 > koel-app-key.tmp
# Add "base64:" prefix
echo "base64:$(cat koel-app-key.tmp)" > koel-app-key.txt
# Encrypt with agenix
agenix -e koel-app-key.age
# Clean up
rm koel-app-key.tmp koel-app-key.txtThen add to your configuration:
age.secrets.koel-app-key = {
file = ./koel-app-key.age;
mode = "0400";
owner = "koel";
};sudo mkdir -p /srv/music
# Copy your music files heresudo nixos-rebuild switchOpen your browser and navigate to the configured URL (e.g., http://music.local). On first access, you'll be prompted to create an admin account.
| Option | Type | Default | Description |
|---|---|---|---|
services.koel.enable |
bool | false | Enable Koel service |
services.koel.appName |
string | "Koel" | Application name |
services.koel.url |
string | "http://localhost" | Koel access URL |
services.koel.appKey |
string | - | Application encryption key (required). Use agenix path /run/agenix/koel-app-key or plaintext base64:YOUR_KEY |
services.koel.listen |
string | "127.0.0.1:9000" | PHP-FPM listen address |
services.koel.version |
string | "v8.2.0" | Koel version to install |
For homelab, SQLite is recommended (no extra services):
services.koel.database.type = "sqlite-persistent";Supported types: sqlite-persistent, mysql, mariadb, pgsql
services.koel.storage = {
driver = "local"; # local, sftp, s3, dropbox
mediaPath = "/srv/music"; # Music files path (required for local)
artifactsPath = null; # Optional: cache/transcoded files path
};TNTSearch is recommended for homelab (no extra services):
services.koel.search.driver = "tntsearch";Koel normally asks you to create the first admin account on first access. You can also create it declaratively:
services.koel.initialUser = {
enable = true;
email = "admin@example.com";
name = "Admin";
role = "admin";
passwordFile = "/run/agenix/koel-admin-password";
};Note: Using password directly will store it in the Nix store. Prefer passwordFile.
Supported drivers: tntsearch, database, algolia, meilisearch
Default is php for simpler setup. Use x-accel-redirect with nginx for better performance.
services.koel.streaming = {
method = "php"; # php, x-sendfile, x-accel-redirect
transcodeFlac = true; # Enable FLAC to MP3 transcoding
transcodeBitRate = 128; # Transcoding bitrate (kbps)
};services.koel.performance = {
maxScanTime = 600; # Max scan time (seconds)
memoryLimit = null; # Memory limit (MB), null = unlimited
};
services.koel.poolConfig = {
"pm" = "dynamic";
"pm.max_children" = 32;
"pm.start_servers" = 2;
"pm.min_spare_servers" = 2;
"pm.max_spare_servers" = 4;
};The module automatically creates these systemd services:
koel-setup.service- Initialize and update Koelkoel-scan.service- Media library scanningkoel-scan.timer- Automatic daily scanning
# Manual media library scan
sudo systemctl start koel-scan.service
# View Koel logs
sudo journalctl -u koel-setup.service
# View PHP-FPM logs
sudo journalctl -u phpfpm-koel.service
# View Caddy logs
sudo journalctl -u caddy.service
# Restart PHP-FPM
sudo systemctl restart phpfpm-koel.serviceBackup these locations:
# SQLite database
/var/lib/koel/database/database.sqlite
# Music files
/srv/music # or your configured mediaPath
# Quick backup script
sudo cp /var/lib/koel/database/database.sqlite \
~/koel-backup-$(date +%Y%m%d).sqliteMoving to a new server is simple:
# On old server
sudo systemctl stop phpfpm-koel.service
sudo tar czf koel-backup.tar.gz /var/lib/koel /srv/music
# Transfer to new server
scp koel-backup.tar.gz newserver:~
# On new server
sudo tar xzf koel-backup.tar.gz -C /
sudo nixos-rebuild switchOn first access, Koel will prompt you to create an admin account. Fill in your email and password.
Depends on library size. Automatic scanning runs daily, or trigger manually:
sudo systemctl start koel-scan.serviceEnable transcoding:
services.koel.streaming.transcodeFlac = true;Change the version and rebuild:
services.koel.version = "v8.3.0"; # New versionsudo nixos-rebuild switchCheck:
- Domain DNS points to your server
- Ports 80 and 443 are open
- View logs:
journalctl -u caddy.service
/var/lib/koel/ # Koel application directory
├── public/ # Web root
├── storage/ # Storage directory
│ ├── framework/ # Laravel framework files
│ ├── logs/ # Application logs
│ └── database/ # SQLite database
│ └── database.sqlite # Database file
└── .env # Environment config (auto-generated)
/srv/music/ # Music files (configurable)
/run/phpfpm/koel.sock # PHP-FPM socket
MIT License. See LICENSE file for details.
Koel itself is also MIT licensed.