Applications Azure

Black Candy on Ubuntu 24.04 on Azure User Guide

| Product: Black Candy on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of Black Candy on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Black Candy is an open source, self hosted music streaming server. You point it at a folder of your own audio files and it reads the metadata to build a tidy library of artists, albums, songs and playlists, then streams it to a clean web player you can use from a laptop or phone, transcoding on the fly when a track needs it. A token authenticated JSON API is included for companion apps.

The cloudimg image ships the free and open source, MIT licensed Black Candy server (Ruby on Rails 8), built from source at the pinned upstream release and run behind nginx. Because the upstream project seeds a well known default administrator account (admin@admin.com / foobar) and regenerates its signing key on every restart, nothing here ships with a known secret: that default account is never created, and a persistent per instance signing key and a per instance admin account with a random password are generated for each VM on first boot, before the app is reachable. The application binds to loopback behind a single nginx front door, login is required for all content, and a demo track is pre loaded so your library is never empty. Backed by 24/7 cloudimg support.

Black Candy is a trademark of its respective owner. This image is produced by cloudimg and is not affiliated with, endorsed by, or sponsored by it. It ships the free and open source MIT licensed self hosted software.

The black-candy-firstboot, black-candy and nginx services all reporting active, and ss showing nginx listening on port 80 while puma is bound to 127.0.0.1:3000 on the loopback interface only

What is included:

  • Black Candy v3.2.0 (the MIT licensed Ruby on Rails music streaming server), built from source at the pinned upstream tag
  • Ruby 4.0.2 compiled from source to match the upstream .ruby-version, with the app and its bundled gems under /opt/black-candy
  • SQLite databases (application, cache, queue and cable), created empty on first boot, with background jobs run in process (no Redis, no PostgreSQL, no separate worker)
  • ffmpeg for on the fly audio transcoding and libvips for cover art processing
  • puma bound to 127.0.0.1:3000 only, fronted by nginx on port 80 as the single public front door
  • black-candy-firstboot.service, black-candy.service and nginx.service as systemd units, enabled and active on boot
  • A per instance Rails signing secret and a per instance admin account with a random password generated per VM on first boot, never baked into the image
  • No default login: the upstream admin@admin.com / foobar account is never created, and the databases are empty on first boot
  • A pre loaded demo track so the library and player are populated on first use
  • Ubuntu 24.04 LTS base with latest security patches applied at build time
  • Azure Linux Agent for seamless cloud integration and SSH key injection
  • 24/7 cloudimg support with guaranteed 24 hour response SLA

Prerequisites

  • Active Azure subscription, SSH public key, VNet + subnet in target region
  • Subscription to the Black Candy listing on Azure Marketplace

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is a sensible starting point for a personal library. For large libraries or several concurrent listeners use Standard_D2s_v5 or larger, and attach a data disk for your music if the collection is big. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web player from the networks that use it.

Step 1: Deploy from the Azure Portal

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Black Candy 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). Then Review + create and Create.

Step 2: Deploy from the Azure CLI

RG="black-candy-prod"; LOCATION="eastus"; VM_NAME="black-candy-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/black-candy-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name bc-vnet --address-prefix 10.100.0.0/16 --subnet-name bc-subnet --subnet-prefix 10.100.1.0/24
az network nsg create -g "$RG" --name bc-nsg
az network nsg rule create -g "$RG" --nsg-name bc-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 bc-nsg --name allow-http --priority 110 \
  --destination-port-ranges 80 --access Allow --protocol Tcp
az vm create -g "$RG" --name "$VM_NAME" --image "$GALLERY_IMAGE_ID" \
  --size Standard_B2s --storage-sku StandardSSD_LRS \
  --admin-username azureuser --ssh-key-values "$SSH_KEY" \
  --vnet-name bc-vnet --subnet bc-subnet --nsg bc-nsg --public-ip-sku Standard

Step 3: Connect to your VM

ssh azureuser@<vm-ip>

Step 4: Confirm the services are running

Black Candy runs as a puma application under black-candy.service, with background jobs (media sync) run in process, fronted by nginx. Confirm the services are active and see the listeners:

sudo systemctl is-active black-candy-firstboot black-candy nginx
sudo ss -tlnp | grep -E ':80 |:3000 '

You will see the services report active. nginx listens on :80 as the single public front door; the puma application is bound to 127.0.0.1:3000 on the loopback interface only and is never exposed directly.

Step 5: Read the per instance credentials

A per instance Rails signing secret and a per instance admin account with a random password were generated for this VM on the first boot, before the app was reachable, and written to a root only file. Read them:

sudo cat /root/black-candy-credentials.txt

The file (mode 0600 root:root) holds ADMIN_EMAIL and ADMIN_PASSWORD (the account you sign in to the web player with) and WEB_URL. These do not ship in the image; every value is unique to this VM, so no two instances share a credential and there is no default login to change. The upstream admin@admin.com / foobar demo account is never created.

The black-candy-credentials.txt file at mode 0600 root root showing the per VM WEB_URL and admin email with the password value redacted, and a note that the credentials are generated uniquely on first boot and that no default account is shipped

Step 6: Verify the security model

Login is required for all content. Confirm that the well known upstream default account does not exist, that an unauthenticated request to a protected streaming endpoint is rejected with 401, and that the per instance admin credential authenticates and streams real audio:

ADMIN_EMAIL=$(sudo grep '^ADMIN_EMAIL=' /root/black-candy-credentials.txt | cut -d= -f2-)
ADMIN_PASSWORD=$(sudo grep '^ADMIN_PASSWORD=' /root/black-candy-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'stream (no token):   %{http_code}\n' 'http://localhost/api/v1/stream/new?song_id=1'
TOKEN=$(curl -s -X POST http://localhost/api/v1/authentication -H 'Content-Type: application/json' \
  -d "{\"session\":{\"email\":\"$ADMIN_EMAIL\",\"password\":\"$ADMIN_PASSWORD\"}}" \
  | python3 -c 'import sys,json;print(json.load(sys.stdin)["user"]["api_token"])')
curl -s -o /dev/null -w 'stream (with token): %{http_code}\n' -H "Authorization: Bearer $TOKEN" 'http://localhost/api/v1/stream/new?song_id=1'

The unauthenticated stream request is rejected with 401, and the same request with the per instance admin token returns 200 and streams the audio. This is the security model: content is gated behind login, and the API is authenticated with a token you obtain by signing in with the per instance admin credentials.

curl output showing the streaming endpoint returning HTTP 401 without a token and HTTP 200 with the per instance admin bearer token, proving content is gated behind authentication

The same proof is bundled as a single script on the image, which the build and smoke tests run to prove the credential and streaming work end to end (it looks up a real song, authenticates, refuses an unauthenticated stream, then streams real audio bytes):

sudo /opt/black-candy/black-candy-selftest.sh

The bundled self test script reporting round-trip OK: the per instance admin authenticated, a real song was streamed over HTTP, and an unauthenticated stream request was refused with 401

Step 7: Open the web player and sign in

Browse to http://<vm-ip>/ to reach the Black Candy web player. Sign in with the ADMIN_EMAIL and ADMIN_PASSWORD from Step 5.

The Black Candy sign in page in a browser, with the email and password fields and the sign in button

Step 8: Browse your library

After signing in you land on the player. The left navigation gives you Artists, Albums, Songs and Playlists. The image ships with a pre loaded demo track (cloudimg Test Tone on the cloudimg Demo Album) so the library is populated on first use. Open Albums to see your collection as a grid of album cards.

The Black Candy albums view showing the pre loaded cloudimg Demo Album card in the library grid, with the player navigation for Artists, Albums, Songs and Playlists

Step 9: Play a track

Open Songs to see the track list, and click a song to start playback. The persistent player bar at the bottom shows the current track, progress and playback controls, and streams the audio straight from your VM.

The Black Candy songs list with the cloudimg Test Tone track playing, the player bar at the bottom showing the track title, progress and playback controls

Step 10: Add your own music

Copy your audio files onto the VM into the media directory, then trigger a library sync. The media directory is /var/lib/black-candy/media:

sudo install -d -o blackcandy -g blackcandy /var/lib/black-candy/media
# copy your files in, e.g. from your machine:  scp -r ./music/* azureuser@<vm-ip>:/tmp/music/
sudo cp -r /tmp/music/. /var/lib/black-candy/media/
sudo chown -R blackcandy:blackcandy /var/lib/black-candy/media

Then open Settings in the web player and click Sync to scan the media directory and import the new files. The media path and transcoding options (bitrate, lossless) are configured on the same Settings page.

The Black Candy settings page showing the media path pointing at the media directory and the sync control used to scan and import audio files, along with the transcoding options

Step 11: Use the API from your own machine

Every feature is backed by a JSON API. Authenticate to obtain a token, then stream or query with the VM address:

TOKEN=$(curl -s -X POST http://<vm-ip>/api/v1/authentication -H 'Content-Type: application/json' \
  -d '{"session":{"email":"<ADMIN_EMAIL>","password":"<ADMIN_PASSWORD>"}}' \
  | python3 -c 'import sys,json;print(json.load(sys.stdin)["user"]["api_token"])')
curl -s -o track.mp3 -H "Authorization: Bearer $TOKEN" 'http://<vm-ip>/api/v1/stream/new?song_id=1'

The authentication call returns a token in user.api_token; pass it as an Authorization: Bearer header to the streaming and library endpoints. This is the same API that Black Candy companion apps use.

Step 12: Server components

Component Version / Detail
Application Black Candy v3.2.0 (Ruby on Rails 8, built from source at the pinned tag)
Interpreter Ruby 4.0.2 compiled from source, at /opt/ruby
Web server puma bound to 127.0.0.1:3000 (loopback only), background jobs run in process
Front door nginx on :80, reverse proxying to puma
Databases SQLite (application, cache, queue, cable), created empty on first boot
Media / transcoding ffmpeg + libvips; media directory /var/lib/black-candy/media
Signing secret per instance, generated first boot into /etc/black-candy/black-candy.env
Admin account per instance random password, created first boot into /root/black-candy-credentials.txt
Operating system Ubuntu 24.04 LTS (patched at build)
License MIT (Black Candy)

Step 13: Managing the service

sudo systemctl status black-candy --no-pager | head -12
sudo journalctl -u black-candy --no-pager | tail -40

Restart the application with sudo systemctl restart black-candy, follow its logs live with sudo journalctl -u black-candy -f, and view configuration in /etc/black-candy/black-candy.env. After changing the environment file, restart the service to apply it.

Step 14: Use your own domain and HTTPS (production)

The image serves the web player over plain HTTP on port 80, which is convenient behind a load balancer or private network. Because the sign in password travels to the server, terminate TLS in front of the VM for production:

  • Front the VM with Azure Application Gateway, a load balancer with a managed certificate, or a CDN, and forward to nginx on port 80.
  • Or install your own certificate: add a TLS server block to /etc/nginx/sites-available/cloudimg-black-candy referencing your certificate and key, open 443/tcp on the NSG, then sudo systemctl reload nginx. You can also set FORCE_SSL=true in /etc/black-candy/black-candy.env once TLS is terminated in front.
  • Point a DNS name you control at the VM public IP.

Step 15: Security recommendations

  • Restrict the NSG. Allow TCP 80 (and 22 for admin) only from the networks that use the app.
  • Terminate TLS in front of the app (Step 14) so the admin password travels encrypted.
  • Rotate the seeded admin password. Sign in and change it in the web player once you have deployed.
  • Keep the application private to the loopback. puma is bound to 127.0.0.1:3000 and fronted by nginx; keep it that way and never expose the application port directly.
  • Keep the OS patched. Unattended security upgrades remain enabled on the running VM.

Step 16: Support and Licensing

Black Candy is distributed under the MIT License. This cloudimg image builds the unmodified official open source release from source; cloudimg provides the packaging, the nginx front door, the per instance signing key and admin generation, the paired deploy guide, and 24/7 support with a guaranteed 24 hour response SLA. Black Candy is an independent open source project and this image is not affiliated with or endorsed by it.

Deploy on Azure

Launch Black Candy on Ubuntu 24.04 LTS by cloudimg from the Azure Marketplace and follow this guide to a working self hosted music streaming server in minutes.