docs/guide/standalone-binary.md
The koel/franken distribution packages FrankenPHP (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.
Download the archive matching your platform from the
releases page — available for linux-x86_64, linux-aarch64,
mac-x86_64, and mac-arm64. Extract it and run:
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:
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 and start using Koel.
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/.
The distribution ships an ./artisan shortcut next to ./koel:
./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 for the full set of
caveats (notably the DB_HOST=localhost MySQL-socket gotcha).
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:
.env to $HOME/.koel/.env.MEDIA_PATH in .env to point at where they already live.storage/app/public/images/, your search indexes, and any other data from the old install into
$HOME/.koel/storage/.To upgrade to a newer release:
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.
:::
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:
[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:
sudo systemctl daemon-reload
sudo systemctl enable --now koel
sudo journalctl -u koel -f
If you're already running nginx (or another reverse proxy) in front of Koel, bind Koel to a local port:
./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.