Initial commit: Nicotine-Less Docker container
Some checks failed
Build and Push Docker Image / check-version (push) Successful in 15s
Build and Push Docker Image / build (linux/arm64, linux-arm64) (push) Failing after 1s
Build and Push Docker Image / build (linux/amd64, linux-amd64) (push) Failing after 1s
Build and Push Docker Image / create-manifest (push) Has been skipped

Lightweight Docker container for Nicotine+ with noVNC web interface and Firefox ESR browser.

Features:
- noVNC web interface with dynamic resize support
- Firefox ESR for testing ports and browsing
- TigerVNC with AcceptSetDesktopSize enabled
- Supervisor for process management
- Configurable user/group permissions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-19 20:09:10 +02:00
commit a333d8a8c0
10 changed files with 545 additions and 0 deletions

113
.gitea/workflows/build.yml Normal file
View File

@@ -0,0 +1,113 @@
name: Build and Push Docker Image
on:
push:
branches:
- main
workflow_dispatch:
schedule:
- cron: '0 * * * *' # Every hour
jobs:
check-version:
runs-on: linux-amd64
container:
image: node:20-slim
outputs:
should_build: ${{ steps.compare.outputs.should_build }}
latest_version: ${{ steps.get_version.outputs.version }}
steps:
- name: Install dependencies
run: |
apt-get update && apt-get install -y curl jq
- name: Get latest Nicotine+ version from PyPI
id: get_version
run: |
VERSION=$(curl -s https://pypi.org/pypi/nicotine-plus/json | jq -r '.info.version')
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Latest Nicotine+ version: $VERSION"
- name: Get current built version
id: current_version
run: |
CURRENT=$(curl -s https://git.nicola.sh/api/v1/repos/public/nicotine-less/releases/latest 2>/dev/null | jq -r '.tag_name // "none"')
echo "current=$CURRENT" >> $GITHUB_OUTPUT
echo "Current built version: $CURRENT"
- name: Compare versions
id: compare
run: |
if [ "${{ steps.get_version.outputs.version }}" != "${{ steps.current_version.outputs.current }}" ]; then
echo "should_build=true" >> $GITHUB_OUTPUT
echo "New version detected, will build"
else
echo "should_build=false" >> $GITHUB_OUTPUT
echo "Already up to date"
fi
build:
needs: check-version
if: needs.check-version.outputs.should_build == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
strategy:
matrix:
include:
- platform: linux/amd64
runner: linux-amd64
- platform: linux/arm64
runner: linux-arm64
runs-on: ${{ matrix.runner }}
steps:
- name: Checkout code
run: |
rm -rf nicotine-less
git clone --depth 1 https://git.nicola.sh/public/nicotine-less.git nicotine-less
cd nicotine-less
- name: Set up Docker Buildx
run: |
docker buildx create --use --name builder --driver docker-container || docker buildx use builder
- name: Login to Gitea Container Registry
run: |
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login git.nicola.sh -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin
- name: Build and push
run: |
cd nicotine-less
docker buildx build \
--platform ${{ matrix.platform }} \
--tag git.nicola.sh/public/nicotine-less:latest-${{ matrix.platform }} \
--tag git.nicola.sh/public/nicotine-less:${{ needs.check-version.outputs.latest_version }}-${{ matrix.platform }} \
--cache-from type=registry,ref=git.nicola.sh/public/nicotine-less:buildcache-${{ matrix.platform }} \
--cache-to type=registry,ref=git.nicola.sh/public/nicotine-less:buildcache-${{ matrix.platform }},mode=max \
--push \
.
create-manifest:
needs: [check-version, build]
runs-on: linux-amd64
steps:
- name: Login to Gitea Container Registry
run: |
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login git.nicola.sh -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin
- name: Create and push multi-arch manifest
run: |
docker buildx imagetools create \
-t git.nicola.sh/public/nicotine-less:latest \
-t git.nicola.sh/public/nicotine-less:${{ needs.check-version.outputs.latest_version }} \
git.nicola.sh/public/nicotine-less:latest-linux/amd64 \
git.nicola.sh/public/nicotine-less:latest-linux/arm64
- name: Create release
if: needs.check-version.outputs.should_build == 'true'
run: |
curl -X POST "https://git.nicola.sh/api/v1/repos/public/nicotine-less/releases" \
-H "Authorization: token ${{ secrets.TOKEN }}" \
-H "Content-Type: application/json" \
-d "{
\"tag_name\": \"${{ needs.check-version.outputs.latest_version }}\",
\"name\": \"Nicotine+ ${{ needs.check-version.outputs.latest_version }}\",
\"body\": \"Automated build with Nicotine+ version ${{ needs.check-version.outputs.latest_version }}\"
}"

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
# Dati utente e configurazioni personali
data/
# File di configurazione personalizzati
nicotine-config
docker-compose.yml

89
Dockerfile Normal file
View File

@@ -0,0 +1,89 @@
FROM debian:bookworm-slim
# Metadata
LABEL maintainer="Nicola"
LABEL description="Nicotine+ with noVNC web interface and SSH X forwarding support"
LABEL version="1.0"
# Avoid interactive prompts
ENV DEBIAN_FRONTEND=noninteractive
# Environment variables
ENV DISPLAY=:0 \
VNC_PORT=5900 \
NOVNC_PORT=6080 \
VNC_RESOLUTION=1280x720 \
VNC_DEPTH=24 \
USER=nicotine \
PUID=1000 \
PGID=1000
# Install dependencies
RUN apt-get update && apt-get install -y \
# Lightweight window manager
openbox \
xterm \
dbus-x11 \
# VNC server
tigervnc-standalone-server \
tigervnc-common \
# noVNC for web interface
novnc \
python3-websockify \
# Nicotine+ dependencies
python3 \
python3-pip \
python3-gi \
python3-gi-cairo \
gir1.2-gtk-3.0 \
# Lightweight browser
firefox-esr \
# For SSH X forwarding
xauth \
# Supervisord to manage processes
supervisor \
# Utilities
wget \
curl \
procps \
net-tools \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install Nicotine+ latest version
RUN pip3 install --no-cache-dir --break-system-packages nicotine-plus
# Create non-root user
RUN groupadd -g ${PGID} ${USER} && \
useradd -u ${PUID} -g ${PGID} -m -s /bin/bash ${USER}
# Create directories
RUN mkdir -p \
/config \
/downloads \
/incomplete \
/home/${USER}/.config/openbox \
/home/${USER}/.config/nicotine \
/home/${USER}/.vnc \
/var/log/supervisor
# Copy configurations
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
COPY openbox-rc.xml /home/${USER}/.config/openbox/rc.xml
COPY index.html /usr/share/novnc/index.html
# Set permissions
RUN chmod +x /usr/local/bin/entrypoint.sh \
&& chown -R ${USER}:${USER} /home/${USER} /config /downloads /incomplete
# Expose noVNC and Soulseek ports
EXPOSE 6080 2234 2235/udp
# Volumes
VOLUME ["/config", "/downloads", "/incomplete"]
WORKDIR /home/${USER}
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]

122
README.md Normal file
View File

@@ -0,0 +1,122 @@
# Nicotine-Less
Lightweight Docker container for Nicotine+ (Soulseek client) with noVNC web interface and integrated Firefox ESR browser.
## Features
- Web interface accessible from browser (noVNC)
- Integrated Firefox ESR browser
- Dynamic VNC window resizing
- SSH X forwarding support
- Persistent volumes for configuration and downloads
## Requirements
- Docker and Docker Compose installed
- Available ports: 6080 (web), 2234 (TCP), 2235 (UDP)
## Installation
### 1. Clone the repository
```bash
git clone https://git.nicola.sh/public/nicotine-less.git
cd nicotine-less
```
### 2. Configure docker-compose.yml
Copy the example file and edit it:
```bash
cp docker-compose.yml.example docker-compose.yml
```
**Required changes in `docker-compose.yml`:**
- Set your download and incomplete paths:
```yaml
volumes:
- ./data/config:/config
- /path/to/your/downloads:/downloads # CHANGE THIS
- /path/to/your/incomplete:/incomplete # CHANGE THIS
```
- (Optional) Add shared folders:
```yaml
- /path/to/your/music:/shares/music:ro # ADD YOUR SHARES
```
- (Optional) Adjust PUID/PGID to match your user (to avoid permission issues):
```bash
id -u # Get your user ID
id -g # Get your group ID
```
Then update in `docker-compose.yml`:
```yaml
environment:
- PUID=1000 # Change to your user ID
- PGID=1000 # Change to your group ID
```
### 3. Create config directory
```bash
mkdir -p data/config
```
### 4. Build and start
```bash
docker compose build
docker compose up -d
```
## Usage
### Web access
Open browser:
```
http://localhost:6080
```
Or from another device:
```
http://YOUR_HOST_IP:6080
```
### SSH X Forwarding
```bash
ssh -X user@host_ip
docker exec -it nicotine-less nicotine
```
## Updates
**Automatic updates**: The container image is automatically rebuilt every hour when a new Nicotine+ version is released on PyPI.
To update to the latest version, pull the new image:
```bash
docker compose pull
docker compose up -d
```
Or rebuild locally:
```bash
docker compose down
docker compose build --no-cache
docker compose up -d
```
## License
This project is provided "as-is" without warranties.
## Links
- [Nicotine+ GitHub](https://github.com/nicotine-plus/nicotine-plus)
- [noVNC GitHub](https://github.com/novnc/noVNC)

View File

@@ -0,0 +1,62 @@
services:
nicotine-less:
image: git.nicola.sh/public/nicotine-less:latest
# Or build locally:
# build: .
container_name: nicotine-less
hostname: nicotine-less
restart: unless-stopped
# Ports
ports:
- "6080:6080" # noVNC web interface
- "2234:2234" # Soulseek TCP
- "2235:2235/udp" # Soulseek UDP
# Environment variables
environment:
- PUID=1000 # User ID (change if needed)
- PGID=1000 # Group ID (change if needed)
- TZ=Europe/Rome # Timezone
- VNC_RESOLUTION=1920x1080 # Desktop resolution
- VNC_DEPTH=24 # Color depth
- VNC_PASSWORD=nicotine # VNC password (optional)
- DISPLAY=:0
# Persistent volumes
volumes:
# Nicotine+ configuration (database, settings, user lists, etc)
- ./data/config:/config
# Completed downloads - CHANGE TO YOUR PATH
- /path/to/your/downloads:/downloads
# Incomplete downloads - CHANGE TO YOUR PATH
- /path/to/your/incomplete:/incomplete
# Optional: additional shared folders - CHANGE TO YOUR PATHS
# - /path/to/your/music:/shares/music:ro
# - /path/to/your/other:/shares/other:ro
# Resource limits (optional, useful for Raspberry Pi)
deploy:
resources:
limits:
memory: 1G
cpus: '2'
reservations:
memory: 256M
# Network
network_mode: bridge
# Privileged mode not required
privileged: false
# Healthcheck
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:6080"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s

43
entrypoint.sh Executable file
View File

@@ -0,0 +1,43 @@
#!/bin/bash
set -e
# Update PUID and PGID if needed
if [ ! -z "$PUID" ] && [ "$PUID" != "1000" ]; then
usermod -u $PUID nicotine
fi
if [ ! -z "$PGID" ] && [ "$PGID" != "1000" ]; then
groupmod -g $PGID nicotine
fi
# Fix permissions on volumes
chown -R nicotine:nicotine /config /downloads /incomplete 2>/dev/null || true
# Create necessary directories
mkdir -p /home/nicotine/.local/share/nicotine
chown -R nicotine:nicotine /home/nicotine/.local
# Create symlink from Nicotine+ config to persistent volume
if [ ! -L "/home/nicotine/.config/nicotine" ]; then
rm -rf /home/nicotine/.config/nicotine
ln -s /config /home/nicotine/.config/nicotine
fi
# Configure VNC password if specified
if [ ! -z "$VNC_PASSWORD" ]; then
mkdir -p /home/nicotine/.vnc
echo "$VNC_PASSWORD" | vncpasswd -f > /home/nicotine/.vnc/passwd
chmod 600 /home/nicotine/.vnc/passwd
chown nicotine:nicotine /home/nicotine/.vnc/passwd
fi
# Export environment variables for supervisord
export DISPLAY
export VNC_PORT
export NOVNC_PORT
export VNC_RESOLUTION
export VNC_DEPTH
export USER
# Start supervisord as root (which will start processes as nicotine user)
exec "$@"

12
index.html Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0; url=vnc.html?autoconnect=true&resize=remote&reconnect=true">
<title>Nicotine+ noVNC</title>
</head>
<body>
<p>Redirecting to noVNC with remote resize enabled...</p>
<p>If not redirected automatically, <a href="vnc.html?autoconnect=true&resize=local">click here</a>.</p>
</body>
</html>

5
openbox-autostart.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Avvia Nicotine+ automaticamente
sleep 2
nicotine &

50
openbox-rc.xml Normal file
View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<openbox_config xmlns="http://openbox.org/3.4/rc"
xmlns:xi="http://www.w3.org/2001/XInclude">
<desktops>
<!-- This wm is GNOME aware -->
<number>1</number>
<!-- Always try to focus new windows when they appear. other rules do
apply -->
<firstdesk>1</firstdesk>
<names>
<name>Desktop</name>
</names>
<popupTime>0</popupTime>
</desktops>
<mouse>
<dragThreshold>8</dragThreshold>
<doubleClickTime>500</doubleClickTime>
<screenEdgeWarpTime>0</screenEdgeWarpTime>
<screenEdgeWarpMouse>false</screenEdgeWarpMouse>
<context name="Desktop">
<mousebind button="Up" action="Click">
<!-- Disabled desktop switching with mouse wheel -->
</mousebind>
<mousebind button="Down" action="Click">
<!-- Disabled desktop switching with mouse wheel -->
</mousebind>
</context>
<context name="Titlebar">
<mousebind button="Left" action="Drag">
<action name="Move"/>
</mousebind>
<mousebind button="Left" action="DoubleClick">
<action name="ToggleMaximize"/>
</mousebind>
<mousebind button="Up" action="Click">
<!-- Disabled desktop switching -->
</mousebind>
<mousebind button="Down" action="Click">
<!-- Disabled desktop switching -->
</mousebind>
</context>
</mouse>
<keyboard>
<chainQuitKey>C-g</chainQuitKey>
</keyboard>
</openbox_config>

43
supervisord.conf Normal file
View File

@@ -0,0 +1,43 @@
[supervisord]
nodaemon=true
user=root
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
[program:xvnc]
command=/usr/bin/Xvnc :0 -geometry %(ENV_VNC_RESOLUTION)s -depth %(ENV_VNC_DEPTH)s -SecurityTypes None -rfbport %(ENV_VNC_PORT)s -AcceptSetDesktopSize=1
user=%(ENV_USER)s
autostart=true
autorestart=true
priority=10
stdout_logfile=/var/log/supervisor/xvnc.log
stderr_logfile=/var/log/supervisor/xvnc_err.log
[program:openbox]
command=/usr/bin/openbox
environment=DISPLAY=":0"
user=%(ENV_USER)s
autostart=true
autorestart=true
priority=20
stdout_logfile=/var/log/supervisor/openbox.log
stderr_logfile=/var/log/supervisor/openbox_err.log
[program:novnc]
command=/usr/share/novnc/utils/novnc_proxy --vnc localhost:%(ENV_VNC_PORT)s --listen %(ENV_NOVNC_PORT)s
user=%(ENV_USER)s
autostart=true
autorestart=true
priority=30
stdout_logfile=/var/log/supervisor/novnc.log
stderr_logfile=/var/log/supervisor/novnc_err.log
[program:nicotine]
command=/usr/local/bin/nicotine
environment=DISPLAY=":0",HOME="/home/%(ENV_USER)s",XDG_CONFIG_HOME="/home/%(ENV_USER)s/.config",XDG_DATA_HOME="/home/%(ENV_USER)s/.local/share"
user=%(ENV_USER)s
autostart=true
autorestart=true
priority=40
stdout_logfile=/var/log/supervisor/nicotine.log
stderr_logfile=/var/log/supervisor/nicotine_err.log