Self-hosted IPTV Player for NAS and Docker
Features β’ Quick Start β’ GPU Acceleration β’ Configuration β’ Codec Support
- πΊ M3U/M3U8 Playlist Support - Import local files or URLs (up to 200MB)
- π Xtream Codes API - Connect to Xtream-compatible providers
- π¬ VOD Support - Browse and play Movies & Series from Xtream Codes playlists
- π EPG Guide - Electronic Program Guide with XMLTV support
- π¬ Live Recording - Record streams directly to your server
- π Stream Sharing - Share live streams with guests via unique URLs
- π₯ Guest Viewer Tracking - See who's watching your shared streams in real-time
- β‘ GPU Transcoding - Hardware-accelerated video encoding
- πΎ Persistent Playlists - Playlists stored on disk, accessible from any device
- π¨ Dark/Light Theme - Customizable interface
- π³ Docker-first - Designed for self-hosted environments
- π± Responsive - Works on desktop and mobile browsers
For NAS systems like Synology, QNAP, or Unraid, use pre-built images from GitHub Container Registry. No need to clone the repo!
- Create a
docker-compose.ymlfile:
name: bacalhau
services:
web:
image: ghcr.io/filipeneves/bacalhau:latest
ports:
- "8456:80"
depends_on:
- proxy
- transcoder
restart: unless-stopped
proxy:
image: ghcr.io/filipeneves/bacalhau-proxy:latest
ports:
- "8888:8080"
restart: unless-stopped
transcoder:
# Choose your image based on GPU and architecture:
# - ghcr.io/filipeneves/bacalhau-transcoder:latest (CPU only, amd64 + arm64)
# - ghcr.io/filipeneves/bacalhau-transcoder:latest-vaapi (AMD/Intel GPU, amd64 only)
# - ghcr.io/filipeneves/bacalhau-transcoder:latest-nvidia (NVIDIA GPU, amd64 only)
# β οΈ ARM NAS (Synology, etc.): Use the CPU-only image (no -vaapi or -nvidia suffix)
image: ghcr.io/filipeneves/bacalhau-transcoder:latest
ports:
- "3001:3001"
volumes:
- ./recordings:/recordings
- ./playlists:/playlists
# Uncomment for AMD/Intel GPU on x86 systems:
# devices:
# - /dev/dri:/dev/dri
restart: unless-stopped
volumes:
recordings:
playlists:- Create data directories and start:
mkdir -p recordings playlists
docker compose up -d- Access the player at: http://your-nas-ip:8456
- Clone the repository:
git clone https://github.com/filipeneves/bacalhau.git
cd bacalhau- Create data directories:
mkdir -p recordings playlists- Start the services:
docker compose up -d- Access the player at: http://localhost:8456
For development with hot-reload:
docker compose --profile dev up devThen access at: http://localhost:5173
| Service | Port | Description |
|---|---|---|
| Web UI (prod) | 8456 | Main player interface |
| Web UI (dev) | 5173 | Development server with hot-reload |
| CORS Proxy | 8888 | Proxy for cross-origin streams |
| Transcoder | 3001 | FFmpeg transcoding & playlist storage |
| Image | Description |
|---|---|
ghcr.io/filipeneves/bacalhau:latest |
Web UI (nginx, multi-arch) |
ghcr.io/filipeneves/bacalhau-proxy:latest |
CORS proxy (multi-arch) |
ghcr.io/filipeneves/bacalhau-transcoder:latest |
CPU-only transcoder (multi-arch) |
ghcr.io/filipeneves/bacalhau-transcoder:latest-vaapi |
AMD/Intel GPU transcoder (amd64) |
ghcr.io/filipeneves/bacalhau-transcoder:latest-nvidia |
NVIDIA GPU transcoder (amd64) |
bacalhau supports hardware-accelerated video transcoding for reduced CPU usage and better performance.
| GPU | Encoder | Docker Config | Requirements |
|---|---|---|---|
| NVIDIA | NVENC | Dockerfile.nvidia |
nvidia-container-toolkit |
| AMD | VAAPI | Dockerfile.vaapi |
/dev/dri device |
| Intel | QSV/VAAPI | Dockerfile.vaapi |
/dev/dri device |
| Apple | VideoToolbox | N/A (macOS only) | - |
| CPU | libx264 | Dockerfile |
None |
- Install nvidia-container-toolkit:
# Ubuntu/Debian
sudo apt install nvidia-container-toolkit
sudo systemctl restart docker- Update
docker-compose.yml:
transcoder:
build:
context: ./transcoder
dockerfile: Dockerfile.nvidia
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu, video]- Rebuild and start:
docker compose down
docker compose build transcoder
docker compose up -d- Update
docker-compose.yml:
transcoder:
build:
context: ./transcoder
dockerfile: Dockerfile.vaapi
devices:
- /dev/dri:/dev/dri- Rebuild and start:
docker compose down
docker compose build transcoder
docker compose up -dCheck if GPU is detected inside the container:
# For VAAPI (AMD/Intel)
docker exec bacalhau-transcoder-1 vainfo
# Check available encoders
docker exec bacalhau-transcoder-1 ffmpeg -hide_banner -encoders 2>/dev/null | grep -i "vaapi\|nvenc\|qsv"Create a .env file or set these in your environment:
| Variable | Default | Description |
|---|---|---|
RECORDINGS_PATH |
./recordings |
Path to store recordings |
PLAYLISTS_PATH |
./playlists |
Path to store playlists |
VITE_PROXY_URL |
http://localhost:8888 |
CORS proxy URL |
VITE_TRANSCODER_URL |
http://localhost:3001 |
Transcoder service URL |
BACALHAU_USER |
- | Username for authentication (optional) |
BACALHAU_PASSWORD |
- | Password for authentication (optional) |
SESSION_SECRET |
- | Secret key for session encryption (optional) |
bacalhau supports optional password protection for your instance. This is useful when exposing your server to the internet or sharing it with others.
β οΈ Note: The current authentication system is temporary. A full-fledged authentication and user management system with multiple users is planned for a future release.
To enable authentication, add these environment variables to your docker-compose.yml:
transcoder:
environment:
- BACALHAU_USER=your_username
- BACALHAU_PASSWORD=your_password
- SESSION_SECRET=your-random-secret-key-hereExample:
transcoder:
image: ghcr.io/filipeneves/bacalhau-transcoder:latest
ports:
- "3001:3001"
volumes:
- ./recordings:/recordings
- ./playlists:/playlists
environment:
- RECORDINGS_DIR=/recordings
- PLAYLISTS_DIR=/playlists
- BACALHAU_USER=admin
- BACALHAU_PASSWORD=changeme
- SESSION_SECRET=change-this-to-a-random-string
restart: unless-stoppedAfter enabling authentication, restart the transcoder service:
docker compose down
docker compose up -dWhen you access the app, you'll be prompted to log in with your credentials. Sessions are valid for 7 days.
Playlists are stored on disk (not in browser storage) so they persist across devices and browsers. This is ideal for NAS deployments where you want to access the same playlists from any device on your network.
# In .env file
PLAYLISTS_PATH=/path/to/your/playlists
# Or inline with docker compose
PLAYLISTS_PATH=/mnt/nas/playlists docker compose up -dTo customize where recordings are saved, set the RECORDINGS_PATH environment variable:
# In .env file
RECORDINGS_PATH=/path/to/your/recordings
# Or inline with docker compose
RECORDINGS_PATH=/mnt/nas/recordings docker compose up -dAccess settings via the βοΈ icon in the player:
- General - Theme preferences
- Playlists - Manage M3U playlists and Xtream connections
- Transcoding - GPU acceleration and quality settings
- Recording - View and manage recordings
- Credits - Version info and credits
| Format | Container | Status |
|---|---|---|
| H.264/AVC | TS, MP4, MKV | β Supported |
| H.265/HEVC | TS, MP4, MKV | β Supported |
| MPEG-2 | TS | β Supported |
| VP9 | WebM | β Supported |
| AAC | All | β Supported |
| MP3 | All | β Supported |
| AC3/EAC3 | TS | β Supported |
| GPU | H.264 | H.265 | Notes |
|---|---|---|---|
| NVIDIA | h264_nvenc | hevc_nvenc | GTX 600+ |
| AMD | h264_vaapi | hevc_vaapi | RX 400+ |
| Intel | h264_qsv / h264_vaapi | hevc_qsv | 6th gen+ |
| CPU | libx264 | libx265 | Universal |
| Protocol | Status | Notes |
|---|---|---|
| HLS (.m3u8) | β Native | Direct playback |
| MPEG-TS | β Via transcoder | Converted to HLS |
| HTTP(S) | β Supported | Via CORS proxy |
services:
dev:
build:
context: .
dockerfile: Dockerfile
target: development
ports:
- "5173:5173"
environment:
- VITE_PROXY_URL=http://localhost:8888
- VITE_TRANSCODER_URL=http://localhost:3001
depends_on:
- proxy
- transcoder
proxy:
image: redocly/cors-anywhere
ports:
- "8888:8080"
transcoder:
build:
context: ./transcoder
dockerfile: Dockerfile.vaapi # or Dockerfile.nvidia
ports:
- "3001:3001"
volumes:
- ${RECORDINGS_PATH:-./recordings}:/recordings:z
- ${PLAYLISTS_PATH:-./playlists}:/playlists:z
devices:
- /dev/dri:/dev/dri # For AMD/Intel GPU
# For NVIDIA, replace devices with:
# deploy:
# resources:
# reservations:
# devices:
# - driver: nvidia
# count: all
# capabilities: [gpu, video]For production, use the prod profile:
docker compose up -dThis serves the built static files via nginx on port 8456.
- Check if the transcoder is running:
docker logs bacalhau-transcoder-1- Verify CORS proxy is accessible:
curl http://localhost:8888/https://example.com- Check device permissions:
ls -la /dev/dri/- Verify container has access:
docker exec bacalhau-transcoder-1 ls -la /dev/dri/Enable GPU acceleration in Settings β Transcoding and select your GPU type.
- Node.js 20+
- Docker & Docker Compose
# Install dependencies
npm install
# Start dev server
npm run devnpm run buildbacalhau is created by Filipe Neves
MIT License - see LICENSE for details.
π bacalhau means salted codfish in Portuguese
π«π· Cooked in France & Luxembourg π±πΊ by a Portuguese π΅πΉ
