Skip to content

Commit

Permalink
Update dockerfile to Ubuntu 24.04 and add various improvements.
Browse files Browse the repository at this point in the history
- Docker base image updated to Ubuntu 24.04
  - Account for new ubuntu images now coming with default user 'ubuntu', which conflicted with adding a new user with default UID of 1000.
  - Conditionally add new user only if CONTAINER_USERNAME is not 'ubuntu'
- Docker healthcheck added
- Resolve buildx warnings
- Add Github Actions
  • Loading branch information
elgeeko1 committed Oct 1, 2024
1 parent 4b4d0c1 commit d9bf33f
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 66 deletions.
59 changes: 59 additions & 0 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Docker Image CI

on:
pull_request:
branches:
- "main"
push:
branches:
- "main"

jobs:
build:
runs-on: ubuntu-24.04
outputs:
image_date: ${{ steps.date.outputs.image_date }}
steps:
- name: Set build date for image tagging
id: date
run: echo "image_date=$(date +'%Y.%m.%d')" >> "$GITHUB_OUTPUT"

- name: Checkout repository
uses: actions/checkout@v4.0.0

- name: Setup Docker buildx
uses: docker/setup-buildx-action@v3
with:
driver-opts: image=moby/buildkit:latest

- name: Docker build
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: false
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/roon-server:${{ steps.date.outputs.image_date }}
${{ secrets.DOCKERHUB_USERNAME }}/roon-server:${{ github.sha }}
- name: Docker compose build
run: docker compose build

push:
needs: build
runs-on: ubuntu-24.04
if: github.event_name == 'push'
steps:
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Docker push
uses: docker/build-push-action@v6
with:
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/roon-server:${{ needs.build.outputs.image_date }}
${{ secrets.DOCKERHUB_USERNAME }}/roon-server:${{ github.sha }}
73 changes: 40 additions & 33 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,57 +1,56 @@
##################
## base stage
##################
FROM ubuntu:jammy AS BASE
FROM ubuntu:24.04 AS base

USER root

# Preconfigure debconf for non-interactive installation - otherwise complains about terminal
# Avoid ERROR: invoke-rc.d: policy-rc.d denied execution of start.
ARG DEBIAN_FRONTEND=noninteractive
ARG DISPLAY localhost:0.0
ARG DISPLAY=localhost:0.0
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
RUN dpkg-divert --local --rename --add /sbin/initctl
RUN ln -sf /bin/true /sbin/initctl
RUN echo "#!/bin/sh\nexit 0" > /usr/sbin/policy-rc.d

# configure apt
RUN apt-get update -q
RUN apt-get install --no-install-recommends -y -q apt-utils 2>&1 \
RUN apt update -q
RUN apt install --no-install-recommends -y -q apt-utils 2>&1 \
| grep -v "debconf: delaying package configuration"
RUN apt-get install --no-install-recommends -y -q ca-certificates
RUN apt install --no-install-recommends -y -q ca-certificates

# install prerequisites
# Roon prerequisites:
# - Roon requirements: ffmpeg libasound2 libicu70
# - Roon requirements: ffmpeg libasound2
# - Roon access samba mounts: cifs-utils
# - Roon play to local audio device: alsa
# - Query USB devices inside Docker container: usbutils udev
RUN apt-get install --no-install-recommends -y -q ffmpeg
RUN apt-get install --no-install-recommends -y -q libasound2
RUN apt-get install --no-install-recommends -y -q libicu70
RUN apt-get install --no-install-recommends -y -q cifs-utils
RUN apt-get install --no-install-recommends -y -q alsa
RUN apt-get install --no-install-recommends -y -q usbutils
RUN apt-get install --no-install-recommends -y -q udev
RUN apt install --no-install-recommends -y -q ffmpeg
RUN apt install --no-install-recommends -y -q libasound2-dev
RUN apt install --no-install-recommends -y -q cifs-utils
RUN apt install --no-install-recommends -y -q alsa
RUN apt install --no-install-recommends -y -q usbutils
RUN apt install --no-install-recommends -y -q udev
# app prerequisites
# - Docker healthcheck: curl
# - App entrypoint downloads Roon: wget bzip2
# - set timezone: tzdata
RUN apt-get install --no-install-recommends -y -q curl
RUN apt-get install --no-install-recommends -y -q wget
RUN apt-get install --no-install-recommends -y -q bzip2
RUN apt-get install --no-install-recommends -y -q tzdata
RUN apt install --no-install-recommends -y -q curl
RUN apt install --no-install-recommends -y -q wget
RUN apt install --no-install-recommends -y -q bzip2
RUN apt install --no-install-recommends -y -q tzdata

# apt cleanup
RUN apt-get autoremove -y -q
RUN apt-get -y -q clean
RUN apt autoremove -y -q
RUN apt clean -y -q
RUN rm -rf /var/lib/apt/lists/*

####################
## application stage
####################
FROM scratch
COPY --from=BASE / /
COPY --from=base / /
LABEL maintainer="elgeeko1"
LABEL source="https://github.com/elgeeko1/roon-server-docker"

Expand Down Expand Up @@ -104,36 +103,44 @@ RUN echo "${TZ}" > /etc/timezone \
# accessing network shares that are not public,
# or if the RoonServer build is mapped in from
# the host filesystem.
ARG CONTAINER_USER=roon
ARG CONTAINER_USER=ubuntu
ARG CONTAINER_USER_UID=1000
RUN adduser --disabled-password --gecos "" --uid ${CONTAINER_USER_UID} ${CONTAINER_USER}

# add container user to audio group
RUN usermod -a -G audio ${CONTAINER_USER}
RUN if [ "${CONTAINER_USER}" != "ubuntu" ]; \
then useradd \
--uid ${CONTAINER_USER_UID} \
--user-group \
${CONTAINER_USER}; \
fi
RUN usermod -aG audio ${CONTAINER_USER}

# copy application files
COPY app/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
RUN chmod +x /entrypoint.sh
COPY README.md /README.md

# configure filesystem
## map a volume to this location to retain Roon Server data
RUN mkdir -p /opt/RoonServer \
&& chown ${CONTAINER_USER} /opt/RoonServer \
&& chgrp ${CONTAINER_USER} /opt/RoonServer
&& chown ${CONTAINER_USER}:${CONTAINER_USER} /opt/RoonServer
## map a volume to this location to retain Roon Server cache
RUN mkdir -p /var/roon \
&& chown ${CONTAINER_USER} /var/roon \
&& chgrp ${CONTAINER_USER} /var/roon
# volume for local music library
VOLUME ["/music"]
&& chown ${CONTAINER_USER}:${CONTAINER_USER} /var/roon

# create /music directory (users may override with a volume)
RUN mkdir -p /music \
&& chown ${CONTAINER_USER}:${CONTAINER_USER} /music \
&& chmod og+r /music

USER ${CONTAINER_USER}

# entrypoint
# set environment variables consumed by RoonServer
# startup script
ENV DISPLAY localhost:0.0
ARG DISPLAY=localhost:0.0
ENV DISPLAY=${DISPLAY}
ENV ROON_DATAROOT=/var/roon
ENV ROON_ID_DIR=/var/roon

ENTRYPOINT ["/entrypoint.sh"]
HEALTHCHECK --interval=1m --timeout=1s \
CMD curl -f http://localhost:9330/display
79 changes: 50 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,59 @@
# Roon Sever in Docker
Roon Server in a docker container.

### Features
## Features

- Downloads and installs latest Roon Server on first container start
- Subsequent in-app Roon Server upgrades persist
- Audio input from a local music library
- Audio input from Tidal or Qobuz
- Audio output to USB DAC devices connected to the Roon Server host
- Audio output to RAAT devices such as the Roon app, Roon Bridge,
RoPieee, etc.
- Audio output to RAAT devices such as the Roon app, Roon Bridge, RoPieee, etc.
- Audio output to local audio output on the Roon Server host
- Local timezone support for accurate last.fm tagging
- Persistent cache
- Secure execution (unprivileged execution, macvlan network)
- Privileged execution mode and host network are supported

# Running
## Configure the Roon Host

### Install host prerequisites

Install the following audio packages into the host that will run Roon.

## Install host prerequisites
Install the following audio packages into your host:
```sh
apt-get install alsa-utils libasound2 libasound2-data libasound2-plugins
```bash
apt install alsa-utils libasound2 libasound2-data libasound2-plugins
```

### Create persistent data volumes and paths

Create persistent docker volumes to retain the binary installation of
Roon Server and its configuration across restarts of the service:
```sh
Roon Server and its configuration across restarts of the service.

```bash
docker volume create roon-server-data
docker volume create roon-server-cache
```

Create the folder on your host while which contains your
local music library. Example: `/home/myuser/roon/music`.
Locate (or create) a folder to host your local music library. This step is optional and only needed if you have a local music library.
- This folder can also be used as a Samba or NFS share for network access to your library.
- This folder is optional. Omit if you plan to exclusively stream music.
```sh

Example:

```bash
mkdir -p ~/roon/music
```

## Option 1: Run in least secure mode (easiest)
Run using privileged execution mode and host network mode:
```sh
## Run Roon

There are three ways to configure the Roon Docker container, each with different security levels. The first option is the easiest and simplest and should work for most users.

### Least secure mode (easiest)

This is the simplest way to run the docker container. Run using privileged execution mode and host network mode:

```bash
docker run \
--name roon-server \
--volume roon-server-data:/opt/RoonServer \
Expand All @@ -52,11 +64,13 @@ docker run \
elgeeko/roon-server
```

## Option 2: Run in macvlan mode (more secure)
Run in an unprivileged container using macvlan network mode. Replace the subnet, gateway and IP address to match your local network.
### Run in macvlan mode (more secure)

### Create docker macvlan network
```sh
Run in an unprivileged container using macvlan network mode. Replace the subnet, gateway, IP address, and primary ethernet adapter to match your local network.

#### Create docker macvlan network

```bash
docker network create \
--driver macvlan \
--subnet 192.168.1.0/24 \
Expand All @@ -65,8 +79,9 @@ docker network create \
roon
```

### Run using unprivileged execution mode and macvlan network mode
```sh
### Run the container with the macvlan network

```bash
docker run \
--name roon-server \
--publish-all \
Expand All @@ -78,35 +93,39 @@ docker run \
elgeeko/roon-server
```

## Option 3: Bridge mode
This works but with a significant limitation. Docker containers on bridged networks
don't receive broadcast or multicast communication, which is used by Roon Server
## Run the container in bridged mode

This option works but with a significant limitation. Docker containers on bridged networks don't receive broadcast or multicast communication, which is used by Roon Server
to discover RAAT devices such as Roon Bridge or RoPieee. Hence, Roon Server is
limited to USB DACs or your Roon App on your PC.

See the Dockerfile source below for ports to open. See Docker documentation for
creating and using a bridged network.

# Additional functionality
## Additional functionality

### Useful docker flags

You may optionally want to add the `-d` flag to output to syslog
instead of the console, and `--restart-unless-stopped` flag to
restart the container if it fails.

### Use USB DACs connected to the host

Add the following arguments to the `docker run` command:
`--volume /run/udev:/run/udev:ro` - allow Roon to enumerate USB devices
`--device /dev/bus/usb` - allow Roon to access USB devices (`/dev/usbmon0` for Fedora)
`--device /dev/snd` - allow Roon to access ALSA devices
`--group-add $(getent group audio | cut -d: -f3)` - add container user to host 'audio' group

### Synchronize filesystem and last.fm timestamps with your local timezone

Add the following arguments to the `docker run` command:
`--volume /etc/localtime:/etc/localtime:ro` - map local system clock to container clock
`--volume /etc/timezone:/etc/timezone:ro` - map local system timezone to container timezone

# Known Issues
## Known Issues

- USB DACs connected to the system for the first time do not appear in Roon.
The workaround is to restart the container. Once the device has been initially
connected, disconnecting and reconnecting is reflected in Roon.
Expand All @@ -116,10 +135,12 @@ run the container with the `user=root` option in the `docker run` command.
requires. Add the following argument to the `docker run` command:
`--ulimit nofile=8192`

# Building from the Dockerfile
## Building from the Dockerfile

`docker build .`

# Resources
## Resources

- [elgeeko/roon-server](https://hub.docker.com/repository/docker/elgeeko/roon-server) on Docker Hub
- Ansible script to deploy the Roon Server image, as well as an optional Samba server for network sharing of a local music library: https://github.com/elgeeko1/elgeeko1-roon-server-ansible
- Roon Labs Linux install instructions: https://help.roonlabs.com/portal/en/kb/articles/linux-install
9 changes: 5 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
version: '3'
services:
roon:
container_name: roon-server
image: elgeeko/roon-server:latest
build:
context: .
image: roon-server:latest
restart: unless-stopped
network_mode: host
privileged: true
user: roon
user: ubuntu
volumes:
- ~/music:/music:ro # TODO: replace ~/mymusic with your music directory
- ~/music:/music:ro # TODO: replace ~/music with your music directory
- roon-server-data:/opt/RoonServer
- roon-server-cache:/var/roon
- /etc/localtime:/etc/localtime:ro
Expand Down

0 comments on commit d9bf33f

Please sign in to comment.