Ll
Applications Azure

LMS Lightweight Music Server on Ubuntu 24.04 on Azure User Guide

| Product: LMS Lightweight Music Server on Ubuntu 24.04 LTS on Azure

Overview

LMS (Lightweight Music Server) is an open source, self hosted music streaming server that streams your personal music collection from anywhere through a fast in browser web player or any Subsonic compatible mobile and desktop app. It is written in C++ on the Wt web toolkit, which makes it unusually light on memory and CPU for a full featured streaming server, and it keeps its catalogue in an embedded SQLite database. It scans the audio files you copy into its library and presents your albums, artists, genres and playlists with cover art, on the fly transcoding through ffmpeg, and a Subsonic compatible API that a wide ecosystem of player apps already speak.

The cloudimg image serves LMS 3.79.0 behind an nginx reverse proxy on a hardened, fully patched Ubuntu 24.04 LTS base. The server listens only on the loopback interface (127.0.0.1:5082) and nginx serves it on port 80.

The image is secure by default. Upstream LMS opens a setup wizard to whoever reaches it first while no account exists, so a shared image that shipped with an empty database would hand the administrator account to whoever found the VM first. This image instead generates a unique administrator password and a separate Subsonic API key on every VM's first boot, before LMS ever accepts a connection. No shared, default or guessable credential ever ships in the image. Backed by 24/7 cloudimg support.

What is included:

  • LMS 3.79.0 built from source and served behind nginx, managed by systemd
  • The LMS web player and Subsonic compatible API on :80
  • A unique administrator password and Subsonic API key generated per VM on first boot
  • ffmpeg for on the fly transcoding to any bitrate or format
  • The server bound to 127.0.0.1:5082 only, never exposed directly to the network
  • A dedicated Azure data disk at /var/lms for the music library, the SQLite database and the transcode cache
  • A preconfigured music library at /var/lms/music and a nightly library scan at 03:00
  • lms.service and nginx.service as enabled systemd units
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a sensible starting point; size up for larger libraries or more concurrent listeners. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web player. LMS serves plain HTTP on port 80; for production, terminate TLS in front of it with your own domain. The server is never exposed directly: it listens on 127.0.0.1:5082 only, so port 5082 stays off the network.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for LMS Lightweight Music Server by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Review the dedicated data disk on the Disks tab, then Review + create then Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name lms \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

Then open port 80 to the web player:

az vm open-port --resource-group <your-rg> --name lms --port 80

Step 3 - Confirm the services are running

SSH in as azureuser and confirm LMS and nginx are active. Note that the server listens only on 127.0.0.1:5082 while nginx serves the web player on port 80.

systemctl is-active lms nginx lms-firstboot

Confirm what is listening. LMS is on loopback only; only nginx is bound to a public interface:

ss -lntH 'sport = :5082 or sport = :80' | awk '{print $1, $4}'

The lms, nginx and lms-firstboot services reporting active, with nginx listening on port 80 and the LMS server bound to loopback 127.0.0.1:5082 only

Step 4 - Retrieve this VM's administrator password

On first boot, lms-firstboot.service generates an administrator password and a Subsonic API key that are unique to this VM, seeds them into the LMS database before the server starts accepting connections, and writes them to a root only file. Read them with:

sudo cat /root/lms-credentials.txt

The per VM credentials file showing the instance URL, the generated administrator username and password, and the separate Subsonic API key, readable only by root

The file contains four values:

  • lms.url - the address to open in a browser
  • lms.admin.username - the administrator login (lmsadmin)
  • lms.admin.password - the password for the web player
  • lms.subsonic.apikey - the key Subsonic client apps use in place of a password

Confirm the first boot completed and exactly one administrator exists:

test -f /var/lib/cloudimg/lms-firstboot.done && echo "first boot complete"
sudo sqlite3 /var/lms/lms.db "SELECT login_name, CASE type WHEN 1 THEN 'admin' ELSE 'user' END FROM user;"

Step 5 - Sign in to the web player

Open http://<vm-public-ip>/ in a browser. LMS presents its login screen.

The LMS login screen in a browser, asking for the login name and password with a remember me option

Sign in with the lms.admin.username and lms.admin.password values from Step 4. You land on the album library. A freshly deployed VM starts with an empty library; the screenshot below shows it after music has been added in Step 7.

The LMS album library after signing in, showing albums as a cover art grid with artist names, the Albums, Artists, Tracks and Playlists navigation, and the player bar along the bottom

Change the password to one of your own at any time from the user menu under Settings then Change password.

Step 6 - Browse and stream

Select an album to open its detail page, with cover art, artist, year, genre and the full tracklist.

An LMS album detail page showing the cover art, artist, year, genre tag, a Play button and the numbered tracklist with durations

Press Play to start streaming. The player bar along the bottom shows the current track, artist and album, elapsed and total time, a seek bar, volume, and the play queue.

The LMS web player streaming a track, with the bottom player bar showing the now playing title, artist and album, the elapsed time against total duration, seek bar, volume control and a three track play queue

Step 7 - Add your own music

The image ships with a preconfigured library at /var/lms/music on the dedicated data disk. Copy your audio files in and make sure the lms service account can read them. From your workstation:

scp -r ~/Music/* azureuser@<vm-public-ip>:/tmp/upload/

Then move them into the library and fix ownership:

sudo mkdir -p /var/lms/music
sudo chown -R lms:lms /var/lms/music
ls /var/lms/music

LMS scans the library automatically every night at 03:00. To pick up new music immediately, sign in to the web player and use Admin then Scan settings, or Admin then Scanner to run a scan on demand. Confirm what has been catalogued:

sudo sqlite3 /var/lms/lms.db "SELECT (SELECT COUNT(*) FROM track) || ' tracks, ' || (SELECT COUNT(*) FROM release) || ' albums, ' || (SELECT COUNT(*) FROM artist) || ' artists';"

LMS reads tags (title, artist, album, track number, year, genre) and embedded cover art from the files themselves, so a well tagged collection browses best.

Step 8 - Connect a Subsonic compatible client app

LMS exposes a Subsonic compatible API, so mobile and desktop apps that speak Subsonic can stream from this VM. Point the app at http://<vm-public-ip>/, use the administrator username, and use the lms.subsonic.apikey value as the password. LMS authenticates the Subsonic API with an API key rather than your account password, so the two values are deliberately different - your web player password is not sent to client apps.

Verify the API from the VM. A successful call returns "status":"ok":

curl -s "http://127.0.0.1/rest/ping.view?u=<admin-username>&p=<subsonic-api-key>&v=1.16.1&c=cloudimg&f=json"

An incorrect key is rejected, which confirms the API is genuinely authenticated rather than open:

curl -s "http://127.0.0.1/rest/ping.view?u=<admin-username>&p=wrong-key&v=1.16.1&c=cloudimg&f=json"

Step 9 - Where your data lives

/var/lms is a dedicated Azure data disk, separate from the OS disk and independently resizable. It holds the music library, the SQLite catalogue and the transcode cache, so rebuilding or resizing the OS disk never touches your music.

df -hT /var/lms | tail -1
sudo du -sh /var/lms/music /var/lms/lms.db 2>/dev/null

The key configuration is in /etc/lms.conf:

grep -E "^listen-addr|^listen-port|^behind-reverse-proxy|^working-dir" /etc/lms.conf

The web player returning HTTP 200 through nginx, the dedicated ext4 data disk mounted at /var/lms, the preconfigured music library path, and LMS pinned to loopback 127.0.0.1:5082 behind the reverse proxy

Step 10 - Production hardening

LMS serves plain HTTP on port 80 in this image. For anything beyond a private network, put your own domain and a TLS certificate in front of it. Point a DNS A record at the VM's public IP, then issue a certificate with certbot and let it rewrite the nginx site:

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d music.your-domain.example --redirect --agree-tos -m you@your-domain.example

Also restrict the NSG so port 80 and 443 are reachable only from the networks that need them, keep port 22 limited to your management range, and change the generated administrator password to one of your own from the web UI. Unattended security upgrades are enabled in the image, so the OS keeps patching itself.

Step 11 - Troubleshooting

Check service health and recent logs:

systemctl is-active lms nginx
sudo journalctl -u lms --no-pager | tail -20

If the web player does not load, confirm nginx is proxying to the server and that LMS is answering on loopback. Both should report HTTP 200. Use a GET rather than a HEAD request: LMS runs with behind-reverse-proxy enabled and answers a bare curl -I HEAD with 400 Bad Request, which looks like a fault but is not one.

curl -s -o /dev/null -w 'via nginx:  HTTP %{http_code}\n' http://127.0.0.1/
curl -s -o /dev/null -w 'lms direct: HTTP %{http_code}\n' http://127.0.0.1:5082/

If first boot did not complete, the credentials file will still contain its placeholder text. Re-run the first boot unit:

sudo rm -f /var/lib/cloudimg/lms-firstboot.done
sudo systemctl restart lms-firstboot.service
sudo systemctl restart lms nginx

If a scan does not pick up new files, confirm the lms account can read them - unreadable files are silently skipped:

sudo -u lms test -r /var/lms/music && echo "library readable by lms"

Support

This image is published and maintained by cloudimg with 24/7 support. For help with the image, deployment or configuration, contact support@cloudimg.co.uk.

LMS is free software licensed under the GNU General Public License v3.0 or later. cloudimg distributes it unmodified; the project itself is at github.com/epoupon/lms.