Skip to content

Docker

The quickest way to install nextcp/2 is by using a Docker container. This guide assumes that Docker is already installed on your system. If not, please refer to the official Docker documentation for installation instructions.

The following example uses docker compose to set up nextcp/2 along with a fork of Universal Media Server (UMS) in a single command. While UMS is the recommended media server, you can also use any other DLNA-compatible media server, though with fewer features.

Create a docker-compose.yml file in your project directory and add the following content:

services:
ums:
container_name: ums
network_mode: host
restart: unless-stopped
# user: '1009:1003'
volumes:
- [MEDIA_VOLUME]:/media/music
- ums_public:/profile
environment:
JAVA_TOOL_OPTIONS: '-Xms2048m -Xmx4096m -XX:+UseShenandoahGC -Xbootclasspath/a:/ums/web/react-client -Dums.profile.path=/profile -Dfile.encoding=UTF-8'
image: ik6666/ums:latest
nextcp2:
container_name: nextcp2
network_mode: host
restart: unless-stopped
volumes:
- next_data:/nextcp2_data
environment:
JAVA_TOOL_OPTIONS: '-Xms1024m -Xmx2048m -XX:+UseShenandoahGC'
image: ik6666/nextcp2:latest
volumes:
ums_public:
external: true
next_data:
external: true

Replace [MEDIA_VOLUME] with the path to your media files on the host system. This will mount the media directory into the UMS container. For example, if your media files are located at /path/to/your/music, replace [MEDIA_VOLUME] with /path/to/your/music. If you want the container to run under a specific user ID and group ID, make sure to uncomment and adjust the user property in the ums service section. The example uses 1009:1003, which you can change to match your system’s user and group IDs.

Create the external volumes ums_public and next_data if they do not exist yet. You can do this by running the following commands:

Terminal window
docker volume create ums_public
docker volume create next_data

Check if the volumes were created successfully:

Terminal window
docker volume ls

The docker volumes are used to persist data across container restarts. The ums_public volume is used by UMS to store its configuration and cache, while the next_data volume is used by nextcp/2 to store its data. If you want to use a different volume name, make sure to update the docker-compose.yml file accordingly.

To start the containers, run the following command in the directory where your docker-compose.yml file is located:

Terminal window
docker-compose up -d

To stop the containers, run:

Terminal window
docker-compose down

If you prefer not to use UMS as your media server, you can either replace or remove the ums service from the docker-compose.yml file.

Due to certain issues with Docker container networking, it is necessary to adjust the UPnP network settings. Once the containers are running, you can access the UMS web interface at http://localhost:9001 (or the IP address of your Docker host). In the UMS web interface, navigate to the Settings tab, then go to Server Configuration, and click on the first tab, General Configuration.

Ensure that the Display Extended Configuration checkbox is selected. This will enable the extended configuration options in UMS.

Next, click on Network Configuration. Use the Enforce Network Interface dropdown to select your Docker host’s network interface. This ensures that UMS uses the correct interface for UPnP discovery. Do not select any other interface. In the example below, the host interface enp0s31f6 has been selected.

Save the configuration.

Once the containers are running, you can access the NextCP/2 web interface at http://localhost:8085 (or the IP address of your Docker host). In the NextCP/2 web interface, navigate to the App Settings tab and scroll down to the `General Configuration“ section.

In the Bind Interface Stream Server field, enter the same network interface name that you selected in the UMS configuration (e.g., enp0s31f6).

Save the configuration.

Restart the application:

Terminal window
docker-compose down
docker-compose up -d

You can now start using NextCP/2 with UMS as your media server. To continue configuring the application, navigate to the App Settings tab.

If you already operate a reverse proxy such as Traefik (for TLS termination, a single entry point, certificates, etc.), you can put the nextCP/2 web UI behind it. You do not need to start Traefik together with nextCP/2 — the steps below assume an existing proxy and only describe how to point it at the running container. A few details are specific to nextCP/2 because it is a UPnP control point.

nextCP/2 must stay on network_mode: host. UPnP discovery (SSDP multicast), GENA event callbacks and the audio stream server all talk directly to renderers and media servers on your LAN, which does not work from a bridge network. The reverse proxy therefore only fronts the web UI on port 8085 (and, if you use it, the /mcp endpoint). UPnP and streaming traffic keeps flowing directly over the LAN and bypasses the proxy — leave the Bind Interface Stream Server setting pointing at your host interface as described above. Do not try to route the stream server through the proxy.

The connection between the proxy and nextCP/2 is plain HTTP on port 8085 — you do not enable HTTPS or install certificates on the container itself. When TLS terminates at the proxy, nextCP/2 must honour the X-Forwarded-* headers so it reconstructs the correct external scheme and host for any absolute URLs (relevant e.g. for the /mcp endpoint); this is what conveys the original https scheme across the plain-HTTP hop to the backend.

Since version 4.3.2 this is a single option — no JVM properties required. Enable it either in the UI under App Settings → General Configuration → “Behind reverse proxy”, or directly in nextcp2Config.json:

{
"applicationConfig": {
"behindReverseProxy": true
}
}

The option is opt-in on purpose: only enable it when nextCP/2 is reachable solely through a trusted proxy — on a directly exposed port the X-Forwarded-* headers could be spoofed. (Older versions instead required -Dserver.forward-headers-strategy=framework in JAVA_TOOL_OPTIONS.)

Use a dedicated (sub)domain — not a sub-path

Section titled “Use a dedicated (sub)domain — not a sub-path”

Because a host-network container has no dedicated container IP, Traefik’s Docker label provider cannot auto-discover it reliably. Use the file (dynamic) provider and point it at the LAN address of the Docker host on port 8085:

# Traefik dynamic configuration, e.g. /etc/traefik/dynamic/nextcp2.yml
http:
routers:
nextcp2:
rule: "Host(`nextcp.example.com`)"
entryPoints: [websecure]
service: nextcp2
tls:
certResolver: le
services:
nextcp2:
loadBalancer:
passHostHeader: true
servers:
# LAN IP of the Docker host running nextCP/2
- url: "http://192.168.1.10:8085"

If Traefik runs as a bridge-network container on the same host, you can instead use http://host.docker.internal:8085 and add extra_hosts: ["host.docker.internal:host-gateway"] to the Traefik service. If your Traefik itself runs with network_mode: host, http://127.0.0.1:8085 works and label-based discovery is possible too.

The UI receives live updates over Server-Sent Events (/SSE), and the optional MCP endpoint (/mcp) is a long-lived stream as well. To keep reverse proxies and load balancers from dropping these connections during quiet periods, nextCP/2 sends a periodic SSE heartbeat (every 30 s by default). You can change the interval — or disable it with 0 — in the UI under App Settings → General Configuration → “SSE heartbeat interval”, or via sseHeartbeatSeconds in nextcp2Config.json.

With the heartbeat active you normally don’t need to touch the proxy timeouts. Traefik also forwards text/event-stream responses immediately, so no buffering configuration is required (unlike nginx). If you still see streams being cut, raise or disable the responding idle timeout on the entry point as a fallback:

# Traefik static configuration (fallback only — usually unnecessary with the heartbeat)
entryPoints:
websecure:
address: ":443"
transport:
respondingTimeouts:
idleTimeout: 0 # do not cut off long-lived SSE / MCP streams