Below is the "production-grade" procedure from scratch, which radically eliminates the ordering cycle error on LibreELEC.
The principle is simple: kodi-avrcp must never be directly linked to kodi.service, kodi.target, or graphical.target.
In production, it is started via a systemd post-boot timer.
Production Installation (LibreELEC) – anti-ordering-cycle version
1) Prerequisites on LibreELEC
Enable SSH (if needed): LibreELEC Settings → Services → SSH ON
Enable Kodi JSON-RPC via HTTP: Kodi Settings → Services → Control
Allow remote control via HTTP: ON
Port: 8080
Quick verification (from LibreELEC):
curl -s -u kodi:kodi -X POST -H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","method":"JSONRPC.Ping","id":1}' \
http://127.0.0.1:8080/jsonrpc
Expected: pong.
2) Compile on PC (Ubuntu) for the correct architecture
On LibreELEC:
uname -m
If it is armv7l:
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 \
go build -trimpath -ldflags="-s -w" -o kodi-avrcp
If it is aarch64:
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 \
go build -trimpath -ldflags="-s -w" -o kodi-avrcp
Check:
file ./kodi-avrcp
2.2) Copy to LibreELEC
scp ./kodi-avrcp root@IPLIBREELEC:/storage/.config/kodi-avrcp
On LibreELEC:
chmod +x /storage/.config/kodi-avrcp
ls -l /storage/.config/kodi-avrcp
3) "Root" cleanup (removes links that generate cycles)
On LibreELEC:
# Stop/disable any old versions
systemctl disable --now kodi-avrcp.service 2>/dev/null
true
systemctl disable --now kodi-avrcp.timer 2>/dev/null
true
systemctl reset-failed kodi-avrcp.service 2>/dev/null
true
# (Optional but recommended) if kodi-autostart creates cycles, disable it
systemctl disable --now kodi-autostart.service 2>/dev/null
true
systemctl reset-failed kodi-autostart.service 2>/dev/null ![]()
[cite_start]true [cite: 8]
# Remove any old units in /storage/.config/system.d
rm -f /storage/.config/system.d/kodi-avrcp.service
rm -f /storage/.config/system.d/kodi-avrcp.timer
If you created drop-ins for kodi in the past (rare but possible), check:
ls -l /storage/.config/system.d/kodi.service.d 2>/dev/null
true
If files exist there that mention kodi-avrcp/kodi-autostart, delete them.
4) Create the kodi-avrcp.service (without Kodi/graphical.target)
On LibreELEC:
mkdir -p /storage/.config/system.d
cat > /storage/.config/system.d/kodi-avrcp.service <<'EOF'
[Unit]
Description=Kodi AVRCP metadata bridge (Kodi JSON-RPC -> BlueZ RegisterPlayer)
After=bluetooth.service
Wants=bluetooth.service
[Service]
Type=simple
# Delay to allow time for BT and Kodi to be ready, without direct dependencies
ExecStartPre=/bin/sh -c 'sleep 12'
ExecStart=/storage/.config/kodi-avrcp
Restart=always
RestartSec=2
Environment=KODI_URL=http://127.0.0.1:8080/jsonrpc
Environment=KODI_USER=kodi
Environment=KODI_PASS=kodi
Environment=BLUEZ_ADAPTER=hci0
# (optional) Environment=POLL_MS=900
EOF
Note: the [Install] WantedBy=... section is intentionally missing here.
5) Create and enable the timer (post-boot start)
This is the point that eliminates the cycle.
cat > /storage/.config/system.d/kodi-avrcp.timer <<'EOF'
[Unit]
Description=Start kodi-avrcp after boot (avoid ordering cycles)
[Timer]
OnBootSec=25
Unit=kodi-avrcp.service
[Install]
WantedBy=timers.target
EOF
Activate:
systemctl daemon-reload
systemctl enable --now kodi-avrcp.timer
systemctl status kodi-avrcp.timer -l --no-pager
6) Immediate test (without rebooting)
Manual start:
systemctl start kodi-avrcp.service
systemctl status kodi-avrcp.service -l --no-pager
journalctl -u kodi-avrcp.service -n 60 --no-pager
Expected: a line like “Registered MPRIS player …”.