Deployment guide

Karaoke Eternal

Overview

Karaoke Eternal is a popular open source, self-hosted karaoke party system. You point it at your own collection of karaoke files, and it becomes a searchable library that everyone at the party can browse.

Guests pick songs and join the queue from their own phones. A browser player screen shows the lyrics and moves the party along automatically as each singer finishes.

This SecureAnyCloud image builds Karaoke Eternal Server 2.0.2 from the pinned upstream release under /opt/karaoke-eternal. It runs the Node.js application server as the dedicated karaoke system user on 127.0.0.1:8080 and fronts it with nginx on port 80. The React client is compiled into the image at build time, so there is nothing for you to build. The datastore is an embedded SQLite database at /var/lib/karaoke-eternal/data/database.sqlite3.

Your library ships empty, by design. SecureAnyCloud does not include, host or distribute any karaoke tracks, backing tracks, lyrics or artwork. The image gives you an empty media folder at /srv/karaoke, already registered with the application. You copy your own legally obtained karaoke files into it.

Supported formats are MP3 with a matching CDG sidecar, ZIP archives of those pairs, and MP4 video.

Secure by default. No usable credential ships in the image. There is no database until your instance first boots, so no session signing key, account or room exists beforehand.

On first boot, the karaoke-eternal-firstboot.service oneshot runs these steps in order:

  • Creates the database, which mints a random JWT signing key unique to this VM
  • Generates a strong administrator password and a strong room password
  • Drives Karaoke Eternal's own one-time first-run setup to create exactly one administrator
  • Records both secrets in a root-only file
  • Enables the public nginx site

Until that has happened, nothing listens on port 80 at all. The application server never accepts a connection from anywhere but 127.0.0.1.

Nobody can claim your server before you do. Karaoke Eternal's first-run setup page is a genuine one-shot: it creates the first administrator and then closes permanently. The image ships with no database, and the application is unreachable from outside the instance until first boot has finished. Your own instance therefore always uses that one shot, never a passer-by.

What you get:

  • Karaoke Eternal Server 2.0.2 (ISC) under /opt/karaoke-eternal, with the React client prebuilt
  • An embedded SQLite database at /var/lib/karaoke-eternal/data/database.sqlite3
  • An empty media library folder at /srv/karaoke, pre-registered with the application
  • karaoke-eternal.service running as karaoke, reachable only on 127.0.0.1:8080
  • An nginx reverse proxy on port 80 with correct websocket proxying, ready for TLS
  • karaoke-eternal-firstboot.service for per-instance secret generation and admin seeding
  • Node.js 24 LTS runtime
  • Ubuntu 24.04 LTS base, patched at build time
  • 24/7 SecureAnyCloud support, 24h response SLA

The sample song used in this guide is a short sound-check clip that SecureAnyCloud generated: synthesised tones and SecureAnyCloud's own sample words. No commercial karaoke content appears anywhere in this image or this guide.

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet.

The recommended VM size is Standard_B2s (2 vCPU, 4 GB). Karaoke Eternal is light, so the size that matters is the OS disk, which holds your karaoke library. Pick a disk large enough for it.

Your guests need nothing but a modern browser on their phone.

1

Deploy from the Azure Portal

Search the Marketplace for Karaoke Eternal on Ubuntu 24.04, then choose your VM size and OS disk size.

Attach an NSG that allows TCP 22 (SSH) from your management network. Also allow TCP 80 / 443 from the networks your guests connect from. These ports carry the web application, including the websockets for the live song queue. On a home or venue network, that usually means your own LAN.

If the server is reachable from the internet, front port 80 with TLS (see the HTTPS section below).

2

Deploy from the Azure CLI

RG="karaoke-prod"; LOCATION="eastus"; VM_NAME="karaoke-01" IMAGE_URN="sac1647283583153:karaoke-eternal-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION" az network vnet create -g "$RG" --name ke-vnet --address-prefix 10.91.0.0/16 --subnet-name ke-subnet --subnet-prefix 10.91.1.0/24 az network nsg create -g "$RG" --name ke-nsg az network nsg rule create -g "$RG" --nsg-name ke-nsg --name allow-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp az network nsg rule create -g "$RG" --nsg-name ke-nsg --name allow-web --priority 110 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 80 443 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:karaoke-eternal-ubuntu-24-04:default:latest
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
  --size Standard_B2s --storage-sku StandardSSD_LRS --os-disk-size-gb 256 \
  --admin-username azureuser --ssh-key-values "$SSH_KEY" \
  --vnet-name ke-vnet --subnet ke-subnet --nsg ke-nsg --public-ip-sku Standard

Give the OS disk enough room for your library. The setting --os-disk-size-gb 256 holds roughly 20,000 MP3 plus CDG song pairs.

3

Connect over SSH

ssh azureuser@<vm-ip>
4

Verify the services are running

First boot takes about thirty seconds. When it finishes, both units are active and nginx answers on port 80.

sudo systemctl is-active karaoke-eternal nginx
sudo systemctl is-enabled karaoke-eternal nginx curl -s -o /dev/null -w 'Karaoke Eternal: HTTP %{http_code}\n' http://127.0.0.1/

Expected output:

active active enabled enabled Karaoke Eternal: HTTP 200

The listening sockets show the separation. nginx holds port 80 for the outside world, and the application's own port 8080 is reachable only from inside the instance. systemd enforces that separation, not just convention.

5

Read your per-instance credentials

First boot generated two secrets on this VM. One is your administrator password. The other is the password for the default room, which everyone joining the party needs.

sudo cat /root/karaoke-eternal-credentials.txt

Expected output (your values will differ, because every instance gets its own):

# Karaoke Eternal on Ubuntu 24.04 - per-VM credentials (root only, chmod 600)
# ... KARAOKE_ETERNAL_URL=http://<vm-ip>/ KARAOKE_ETERNAL_ADMIN_USER=kadmin KARAOKE_ETERNAL_ADMIN_PASSWORD=<admin-password> KARAOKE_ETERNAL_ROOM_PASSWORD=<room-password> KARAOKE_ETERNAL_MEDIA_DIR=/srv/karaoke

The file is 0600 root:root, so only root can read it. Once you are signed in, change the administrator password from the app's Account page. Keep the room password to hand, because it is what you give your guests.

6

Sign in

Browse to http://<vm-ip>/. Pick Room 1 and enter the room password from Step 5. Then sign in with the administrator username and password.

7

Copy your own karaoke files into the library

The library folder is /srv/karaoke, and it ships empty. Your instance's admin user is a member of the karaoke group, so you can copy files straight into the folder over SSH from your own machine:

rsync -av --progress ~/my-karaoke-library/ azureuser@<vm-ip>:/srv/karaoke/

Karaoke Eternal recognises:

  • MP3 + CDG: the classic MP3+G format. Keep the two files side by side with the same base name, for example Some Artist - Some Song.mp3 and Some Artist - Some Song.cdg.
  • ZIP: a zip containing one MP3 (or M4A) and its CDG sidecar.
  • MP4: karaoke videos with the lyrics burned in.

Name files Artist - Title, and the scanner splits them into artist and title automatically.

On the instance, confirm what has landed:

ls -la /srv/karaoke | sed -n '1,6p' find /srv/karaoke -type f | wc -l

On a freshly deployed instance, the command reports an empty folder, which is how the image ships:

total 8 drwxrwsr-x 2 karaoke karaoke 4096 Aug 7 10:48 . drwxr-xr-x 3 root root 4096 Aug 7 10:48 .. 0
8

Scan the library

In the app, open the smiley menu, choose Preferences, and press Rescan next to /srv/karaoke. New files are also picked up automatically while the folder is being watched.

You can trigger the same scan from the shell:

CF=/root/karaoke-eternal-credentials.txt
U=$(sudo grep '^KARAOKE_ETERNAL_ADMIN_USER=' "$CF" | cut -d= -f2-)
P=$(sudo grep '^KARAOKE_ETERNAL_ADMIN_PASSWORD=' "$CF" | cut -d= -f2-) sudo node /usr/local/sbin/karaoke-eternal-ctl.mjs req http://127.0.0.1 "$U" "$P" - - GET /api/prefs/paths/scan

Expected output:

STATUS 200

When the scan finishes, the library page lists your songs by artist.

9

Optional: test the whole chain with a clip you generate yourself

To confirm the appliance works before you copy your real library across, generate a short sound-check clip on the instance. Nothing is downloaded. ffmpeg synthesises the video and the tone locally. ffmpeg is not part of the image, and the steps remove it again at the end.

sudo apt-get -o DPkg::Lock::Timeout=120 update -qq sudo apt-get -o DPkg::Lock::Timeout=120 install -y --no-install-recommends ffmpeg >/dev/null ffmpeg -version 2>/dev/null | sed -n '1p' command -v ffmpeg >/dev/null || { echo 'FAIL: ffmpeg did not install'; exit 1; }
sudo -u karaoke ffmpeg -nostdin -hide_banner -loglevel error -y \
  -f lavfi -i "sine=frequency=440:duration=12" \
  -f lavfi -i "color=c=0x101828:s=1280x720:d=12" \
  -filter_complex "[1:v]drawtext=fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf:text='YOUR LIBRARY IS WORKING':fontcolor=white:fontsize=54:x=(w-tw)/2:y=(h-th)/2[v]" \
  -map "[v]" -map 0:a -c:v libx264 -pix_fmt yuv420p -c:a aac -shortest \
  "/srv/karaoke/sac - Library Test.mp4" ls -l "/srv/karaoke/sac - Library Test.mp4" [ -s "/srv/karaoke/sac - Library Test.mp4" ] || { echo 'FAIL: test clip not generated'; exit 1; }
CF=/root/karaoke-eternal-credentials.txt
U=$(sudo grep '^KARAOKE_ETERNAL_ADMIN_USER=' "$CF" | cut -d= -f2-)
P=$(sudo grep '^KARAOKE_ETERNAL_ADMIN_PASSWORD=' "$CF" | cut -d= -f2-) sudo node /usr/local/sbin/karaoke-eternal-ctl.mjs req http://127.0.0.1 "$U" "$P" - - GET /api/prefs/paths/scan for i in $(seq 1 30); do
N=$(sudo sqlite3 /var/lib/karaoke-eternal/data/database.sqlite3 'SELECT COUNT(*) FROM songs;') [ "$N" != "0" ] && break sleep 2 done
sudo sqlite3 /var/lib/karaoke-eternal/data/database.sqlite3 \
  'SELECT artists.name || " - " || songs.title FROM songs JOIN artists USING(artistId);' [ "$(sudo sqlite3 /var/lib/karaoke-eternal/data/database.sqlite3 'SELECT COUNT(*) FROM songs;')" = "1" ] \
  || { echo 'FAIL: the test clip did not appear in the library'; exit 1; }

Expected output:

STATUS 200 sac - Library Test

The song is now in the library and can be queued from any phone signed in to the room. When you are done, remove it to return the instance to a clean state:

sudo rm -f "/srv/karaoke/sac - Library Test.mp4" CF=/root/karaoke-eternal-credentials.txt
U=$(sudo grep '^KARAOKE_ETERNAL_ADMIN_USER=' "$CF" | cut -d= -f2-)
P=$(sudo grep '^KARAOKE_ETERNAL_ADMIN_PASSWORD=' "$CF" | cut -d= -f2-) sudo node /usr/local/sbin/karaoke-eternal-ctl.mjs req http://127.0.0.1 "$U" "$P" - - GET /api/prefs/paths/scan
sudo apt-get -o DPkg::Lock::Timeout=120 purge -y ffmpeg >/dev/null
sudo apt-get -o DPkg::Lock::Timeout=120 autoremove --purge -y >/dev/null command -v ffmpeg >/dev/null && { echo 'FAIL: ffmpeg still installed'; exit 1; } echo "ffmpeg removed again" sleep 3 echo "songs now: $(sudo sqlite3 /var/lib/karaoke-eternal/data/database.sqlite3 'SELECT COUNT(*) FROM songs;')"
10

Let your guests join from their phones

By default, nobody can create their own account. That is the safe setting for a server reachable from a browser. For a party you will usually want guests to join, so open it deliberately:

  • Sign in as the administrator and open Preferences from the smiley menu.
  • Under Rooms, edit Room 1.
  • Tick Allow new guest accounts. Also tick Allow new user accounts if you want regulars to keep a named account and their starred songs.
  • Save.

Now anyone who can reach the server, enters the room password and picks a name can join. Give your guests the URL and the room password. The room password keeps the party to the people in the room.

Everyone browses the same library from their phone and taps a song to join the queue.

11

Put the player on the big screen

On the machine wired to the TV or projector, sign in as the administrator. Open the player tab in the bottom bar, or browse to http://<vm-ip>/player. Press play, and the player screen takes over. Lyrics for MP3+G songs are rendered live from the CDG stream, MP4 songs play as video, and a visualiser fills the background. Use the full-screen control at the top right.

As each song ends, the player advances automatically to whoever is next in the queue, so nobody has to touch the machine again all night.

12

The websockets that carry the party

Every live action rides a socket.io websocket: a guest queueing a song, the queue reordering, the player advancing and the progress bar on everyone's phone. The nginx vhost in this image is already configured to proxy that upgrade, with long timeouts so a connection is never reaped mid-song.

You can watch the handshake succeed:

UP=$(curl -s -i -N -m 8 -H 'Connection: Upgrade' -H 'Upgrade: websocket' \
  -H "Sec-WebSocket-Key: $(head -c 16 /dev/urandom | base64)" -H 'Sec-WebSocket-Version: 13' \
  'http://127.0.0.1/socket.io/?EIO=4&transport=websocket' 2>/dev/null | sed -n '1,3p' || true) printf '%s\n' "$UP" printf '%s' "$UP" | grep -q '101 Switching Protocols' || { echo 'FAIL: nginx did not proxy the websocket upgrade'; exit 1; }

Expected output:

HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade

101 Switching Protocols confirms that the upgrade works. A reverse proxy that answers 200 here has silently broken the live queue. This is why the image ships the proxy already configured.

You can go one step further and confirm that an authenticated session receives server-pushed events over that websocket:

CF=/root/karaoke-eternal-credentials.txt
U=$(sudo grep '^KARAOKE_ETERNAL_ADMIN_USER=' "$CF" | cut -d= -f2-)
P=$(sudo grep '^KARAOKE_ETERNAL_ADMIN_PASSWORD=' "$CF" | cut -d= -f2-)
R=$(sudo grep '^KARAOKE_ETERNAL_ROOM_PASSWORD=' "$CF" | cut -d= -f2-) sudo node /usr/local/sbin/karaoke-eternal-ctl.mjs wscheck http://127.0.0.1 "$U" "$P" 1 "$R"

Expected output:

OK:websocket-upgraded 'HTTP/1.1 101 Switching Protocols' authenticated=kadmin live-events=prefs-push+queue-push
13

Rooms

Rooms let you run more than one party from the same library, or reset the evening with a clean queue. Under Preferences, you can create a room, give it its own password, and open or close it.

Closing a room stops anyone new from joining without stopping the music. Guests already in the room stay put.

The default Room 1 was created for you at first boot with the room password from Step 5.

14

The runtime stack

cat /opt/karaoke-eternal/SAC_VERSION node -v nginx -v ls /opt/karaoke-eternal/build/client | sed -n '1,4p'

Expected output:

2.0.2 v24.19.0 nginx version: nginx/1.24.0 (Ubuntu) 240.e30082e5be751ce05500.js 447.e30082e5be751ce05500.js 715.e30082e5be751ce05500.js 851.e30082e5be751ce05500.css

The client bundles under build/client are the compiled React app. They are built into the image, so a customer VM never needs a build toolchain, an internet connection or a CDN to serve the interface.

15

Data and persistence

Everything that matters lives on the OS disk and is included in your normal VM backups or snapshots:

  • /srv/karaoke: your karaoke files
  • /var/lib/karaoke-eternal/data/database.sqlite3: accounts, rooms, queues, stars and the media index
  • /root/karaoke-eternal-credentials.txt: the credentials generated on this instance

To back up the database safely while the service is running, use SQLite's own online backup:

sudo sqlite3 /var/lib/karaoke-eternal/data/database.sqlite3 ".backup '/var/tmp/karaoke-eternal-backup.sqlite3'"
sudo ls -l /var/tmp/karaoke-eternal-backup.sqlite3

The media index is derived data. If you ever lose the database, you lose only accounts, rooms and stars, and a rescan rebuilds your library.

16

Enable HTTPS

If the server is reachable from the internet, put TLS in front of it. Point a DNS record at the VM and open port 443 in the NSG. Then run:

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com

Certbot edits the shipped vhost in place and keeps the websocket and media proxy settings. The live queue keeps working over wss://.

Managing the service

sudo systemctl status karaoke-eternal --no-pager | sed -n '1,5p'
sudo systemctl restart karaoke-eternal for i in $(seq 1 30); do curl -sf -o /dev/null -m 5 http://127.0.0.1/ && break; sleep 2; done
sudo journalctl -u karaoke-eternal -n 20 --no-pager | sed -n '1,20p' curl -s -o /dev/null -w 'back up: HTTP %{http_code}\n' http://127.0.0.1/

The application logs to the journal, and Karaoke Eternal also keeps its own logs under /var/lib/karaoke-eternal/data.

Support

SecureAnyCloud publishes and supports this image. Karaoke Eternal is open source software licensed under the ISC licence. SecureAnyCloud is not affiliated with or endorsed by its authors. Email sales@secureanycloud.com, 24/7, with a 24 hour response SLA.

SecureAnyCloud does not supply karaoke content of any kind. You are responsible for holding the appropriate rights to the karaoke files you place in /srv/karaoke.

Deploy on Azure

Launch Karaoke Eternal on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure