A cross-platform DLNA/UPnP media server written in Rust. Streams video, audio, and images to any DLNA-compatible device (smart TVs, receivers, game consoles). Less than 8Mb of RAM needed
Built with Tokio, Axum, and Redb for high performance and reliability.
Supported platforms: Windows, Linux, macOS, Docker (x64 and ARM64)
- DLNA/UPnP Media Server - Stream to any DLNA device with SSDP discovery
- Web Interface - Modern dashboard showing server status, scanned files, and directories
- AI Agent & MCP Integration - AI agents (voice assistants, chatbots, and autonomous agents) can interact with your media library and control playback on smart TVs on the local network.
- Global Search - Instant search across all indexed filenames and paths
- HTTP Range Streaming - Seek support for large media files
- Multi-format Support - MKV, MP4, AVI, MP3, FLAC, WAV, AAC, OGG, JPEG, PNG, and more
- Audio Metadata - Automatic extraction of artist, album, genre, year from tags
- Music Browsing - Browse by Artists, Albums, Genres, Years via DLNA
- Playlist Support - Auto-imports M3U/PLS playlists from media directories
- Real-time Monitoring - Detects file changes and updates database automatically
- Cross-platform - Native integration for Windows, macOS, Linux
- Redb Database - Embedded ACID-compliant database with crash recovery
You can install VuIO using our official Homebrew Tap:
brew tap vuiodev/vuio
brew install vuioOnce installed, start the server using:
vuio /path/to/mediaFor a detailed guide on all HTTP, REST, UPnP, and MCP endpoints, see the API Reference.
VuIO features a built-in web dashboard at http://<server-ip>:<port> (default: http://localhost:8080):
- Sleek Dashboard: Real-time server status, monitored directories, and database statistics.
- Media Explorer: Browse all scanned videos, music, and pictures directly in your web browser.
- Instant Search: Quick client-side filtering/searching across all files and paths as you type.
# Run with default settings (scans ~/Videos, ~/Music, ~/Pictures)
./vuio
# Specify media directory
./vuio /path/to/media
# Custom port and name
./vuio -p 9090 -n "My Media Server" /path/to/media
# Multiple directories (using -m or --media-dir)
./vuio /movies -m /music -m /photosUsage: vuio [OPTIONS] [MEDIA_DIR]
Arguments:
[MEDIA_DIR] Directory containing media files
Options:
-p, --port <PORT> Port to listen on [default: 8080]
-n, --name <NAME> DLNA server name
-c, --config <CONFIG> Path to config file
-m, --media-dir <DIR> Additional media directories
--debug Enable debug logging
--log-file <PATH> Path to custom log file
--log-level <LEVEL> Set log level (off, error, warn, info, debug, trace)
--update Update binary to the latest version from GitHub
--auth Enable administrative authentication
-h, --help Print help
-V, --version Print version
You can automatically update your installed binary to the latest release on GitHub at any time:
vuio --updateDocker does not work on macOS due to multicast limitations.
git clone https://github.com/vuiodev/vuio.git
cd vuio
docker-compose -f docker-compose.yml upservices:
vuio:
image: vuio:latest
container_name: vuio-server
restart: unless-stopped
network_mode: host # Required for DLNA multicast
cap_add:
- NET_ADMIN
- NET_RAW
volumes:
- ./vuio-config:/config
- /path/to/media:/media:ro
environment:
- VUIO_IP=192.168.1.100 # Your host IP (required)
- VUIO_PORT=8080
- VUIO_MEDIA_DIRS=/media
- VUIO_SERVER_NAME=VuIO
- VUIO_DB_PATH=/data/vuio.redbSingle directory:
volumes:
- ./vuio-config:/config
- /path/to/media:/media:roMultiple directories:
volumes:
- ./vuio-config:/config
- /home/user/Movies:/media/movies:ro
- /home/user/Music:/media/music:ro
- /home/user/Pictures:/media/pictures:ro
- /mnt/nas/media:/media/nas:ro
environment:
- VUIO_MEDIA_DIRS=/media/movies,/media/music,/media/pictures,/media/nasNetwork storage (NFS/SMB):
volumes:
- type: bind
source: /mnt/nas/media
target: /media
read_only: truedocker run -d \
--name vuio-server \
--restart unless-stopped \
--network host \
--cap-add NET_ADMIN \
--cap-add NET_RAW \
-v /path/to/media:/media:ro \
-v ./vuio-config:/config \
-e VUIO_IP=192.168.1.100 \
-e VUIO_PORT=8080 \
-e VUIO_MEDIA_DIRS=/media \
-e VUIO_DB_PATH=/data/vuio.redb \
vuio:latest| Variable | Default | Description |
|---|---|---|
VUIO_IP |
- | Required. Host IP for DLNA announcements |
VUIO_PORT |
8080 | HTTP server port |
VUIO_SERVER_NAME |
VuIO | DLNA server name |
VUIO_UUID |
random | Device UUID (set for persistence) |
VUIO_MEDIA_DIRS |
/media | Comma-separated media paths |
VUIO_SCAN_ON_STARTUP |
true | Scan media on startup |
VUIO_WATCH_CHANGES |
true | Monitor for file changes |
VUIO_CLEANUP_DELETED |
true | Remove deleted files from DB |
VUIO_SCAN_PLAYLISTS |
true | Import M3U/PLS playlists |
VUIO_DB_PATH |
/data/vuio.redb | Database file path |
VUIO_MULTICAST_TTL |
4 | Multicast TTL |
VUIO_ANNOUNCE_INTERVAL |
30 | SSDP announce interval (seconds) |
VUIO_AUTH |
false | Enable administrative/management authentication |
VUIO_ADMIN_TOKEN |
- | Pre-configured admin password token for sign in |
Find your host IP:
# Linux
ip route get 1.1.1.1 | grep -oP 'src \K[0-9.]+'
# macOS
ipconfig getifaddr en0
# Windows
ipconfig | findstr "IPv4"Generate UUID for multiple instances:
uuidgen # Linux/macOS
[System.Guid]::NewGuid() # Windows PowerShellYou can deploy VuIO to a Kubernetes cluster using the provided Helm chart.
You can install the chart directly from GitHub Container Registry without cloning the repository:
helm install vuio oci://ghcr.io/vuiodev/charts/vuio --version 0.0.35If you have cloned the repository, you can install the chart locally:
# From the repository root directory
helm install vuio ./helm/vuioFor SSDP/UPnP multicast auto-discovery to work natively on your local area network (LAN), the container needs to bind directly to the host's networking. The Helm chart is configured to use host networking by default:
hostNetwork: trueNote: On platforms like macOS, multicast routing restrictions in the hypervisor layer prevent SSDP from working. If you are deploying locally on macOS Kubernetes or do not require LAN discovery, you should disable host networking in your values.yaml or overrides:
helm install vuio ./helm/vuio --set hostNetwork=falseThe Helm chart supports configuring a Persistent Volume Claim (PVC) to retain the database and generated configuration across restarts. Additionally, you can configure your media volume mounts directly under media values:
# Mount your local media library directory into the container
media:
volumeMounts:
- name: media
mountPath: /media
readOnly: true
volumes:
- name: media
hostPath:
path: /Users/random/test-media # Path to media on your host machine
type: DirectoryRefer to the default values.yaml file for a complete list of parameters, including resource constraints, service configurations, and Ingress routing rules.
VuIO uses TOML configuration files on native platforms. Config location: ./config/config.toml
Server:
port- HTTP server portinterface- Network interface to bind (0.0.0.0 for all)name- DLNA server friendly nameuuid- Device UUID (auto-generated if not set)ip- Specific IP for DLNA announcements (optional)
Network:
interface_selection- "Auto", "All", or specific interface namemulticast_ttl- Multicast time-to-liveannounce_interval_seconds- SSDP announcement interval
Media:
scan_on_startup- Scan directories on startupwatch_for_changes- Real-time file monitoringcleanup_deleted_files- Auto-remove deleted files from databasescan_playlists- Import M3U/PLS playlist filessupported_extensions- Global list of media extensions
Media Directories:
path- Directory pathrecursive- Scan subdirectoriesextensions- Override extensions for this directoryexclude_patterns- Patterns to exclude (e.g., ".tmp", ".")validation_mode- Path validation: "Strict" (fail if missing), "Warn" (log warning), "Skip" (no validation)case_sensitive- Optional per-root override; omit it to detect the filesystem behavior automatically
Database:
path- Database file locationvacuum_on_startup- Compact database on startupbackup_enabled- Enable automatic backups
VuIO automatically extracts metadata from audio files:
- Title, Artist, Album, Album Artist
- Genre, Year, Track Number
- Duration
- Falls back to filename parsing when tags are missing
- Lossless: FLAC, WAV, AIFF
- Lossy: MP3, AAC, OGG, WMA, OPUS, M4A
VuIO automatically discovers and imports playlist files:
- M3U/M3U8 - Most common format
- PLS - WinAmp/iTunes compatible
Playlists are scanned from media directories on startup and made available to DLNA clients.
Configure: scan_playlists = true or VUIO_SCAN_PLAYLISTS=true
Recommended directory structure:
/music/
├── Artist Name/
│ └── Album Name (Year)/
│ ├── 01 - Track.flac
│ └── folder.jpg
└── playlists/
├── favorites.m3u
└── workout.pls
VuIO uses Redb, an embedded ACID-compliant database.
| Platform | Default Path |
|---|---|
| Windows | [exe dir]\config\database\media.redb |
| Linux | ~/.local/share/vuio/media.redb |
| macOS | ~/Library/Application Support/vuio/media.redb |
| Docker | /data/vuio.redb (or VUIO_DB_PATH) |
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Web Server │ │ SSDP Service │ │ File Watcher │
│ (Axum/HTTP) │ │ (Discovery) │ │ (Real-time) │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└──────────────────────┼──────────────────────┘
│
┌──────────────────────┴──────────────────────┐
│ Application Core │
│ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │
│ │ Config │ │ Database│ │ Platform │ │
│ │ Manager │ │ (Redb) │ │ Abstraction │ │
│ └─────────┘ └─────────┘ └─────────────┘ │
└──────────────────────┬──────────────────────┘
│
┌──────────────────────┴──────────────────────┐
│ Platform Layer │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Windows │ │ macOS │ │ Linux │ │
│ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────┘
VuIO is designed to run cleanly by default. Standard startup displays a clean, visual card containing crucial server information, while silencing verbose background execution traces.
By default, all detailed logs (INFO level and below) are automatically recorded to a rolling background log file so that troubleshooting info is always preserved.
Default Log Path:
- Windows:
[exe dir]\config\logs\vuio.log - macOS:
[exe dir]/config/logs/vuio.log(Native) or platform cache path - Linux:
[exe dir]/config/logs/vuio.log - Docker:
/data/logs/vuio.log
If you want to view verbose background logs directly on the console, you can use either of the following approaches:
-
Command Line Flag: Run with
--debugto enable verbose debug logs on the terminal:./vuio --debug
-
Environment Variable: Set
RUST_LOGenv variable:RUST_LOG=info ./vuio RUST_LOG=debug ./vuio
You can fully control where logs are written and their severity level using command line options:
- Specify Custom Log File:
./vuio --log-file /path/to/my-custom.log
- Set Log Level:
./vuio --log-level debug ./vuio --log-level warn
VuIO contains built-in endpoints optimized for Kubernetes orchestration and observability via Grafana, Prometheus, and Loki.
- Liveness Probe (
/healthz): A lightweight endpoint indicating that the web server is running.- Returns:
200 OKwith JSON{"status": "healthy"}
- Returns:
- Readiness Probe (
/readyz): Verifies database connectivity and readiness to serve requests.- Returns:
200 OKwith JSON{"status": "ready"}if healthy, or503 Service Unavailableif database access fails.
- Returns:
To monitor the server health, cache efficiency, and indexing status, you can query the metrics endpoints:
- Prometheus Exposition Format (
/metrics): Returns raw metrics formatted for Prometheus.- Query:
curl http://localhost:8080/metrics - Returns:
200 OKwithtext/plainPrometheus exposition format.
- Query:
- JSON Format (
/metrics/json): Returns JSON telemetry.- Query:
curl http://localhost:8080/metrics/json - Returns:
200 OKwith JSON structure like:{ "web_handler_metrics": { "browse_requests": 12, "cache_hits": 9, "cache_misses": 3, "cache_hit_rate_percent": 75.0, "average_response_time_ms": 12, "gigabytes_transferred": 0.25, "redb_database": "active" } }
- Query:
To support instant directory listings for directories containing 1000+ files, VuIO implements an automatic, thread-safe SOAP response cache:
- How it works: The cache stores the fully rendered XML response mapped to a unique signature of
(ObjectID, StartingIndex, RequestedCount, ClientProfile, UpdateID). Subsequent scrolls or refreshes from the TV/client are served in sub-milliseconds without hitting the database, resolving paths, or performing memory cloning. - Cache Invalidation: The cache is automatically and immediately cleared whenever a filesystem change or directory scan increments the
UpdateIDcounter, ensuring no stale data is ever served.
- Log Scraper Endpoint (
/logs): Stream the last N log entries (default100, max5000) dynamically over HTTP. Useful for pull-based logs scraping.- Query:
curl http://localhost:8080/logs?limit=50 - Returns:
200 OKwith raw plaintext log lines.
- Query:
VuIO supports the Model Context Protocol (MCP), allowing AI agents (like voice assistants, chatbots, and autonomous agents) to interact with your media library and control playback on smart TVs on the local network.
The MCP server is served over SSE (Server-Sent Events) on the existing HTTP port:
- Establish SSE Session:
GET http://localhost:8080/sse- When connected, the server will yield an initial
endpointevent containing the POST message target, e.g.data: /mcp/message?client_id=<uuid>
- When connected, the server will yield an initial
- Post Messages:
POST http://localhost:8080/mcp/message?client_id=<uuid>- Used to send standard MCP JSON-RPC 2.0 messages to the server.
| Tool Name | Parameters | Description |
|---|---|---|
search_media |
query (string) |
Search media files by keyword matching filenames or tags |
browse_folder |
path (string), category (optional string) |
Browse files and directories in a specific folder path |
get_media_info |
file_id (integer) |
Fetch detailed metadata for a file by its ID |
get_server_stats |
None | Retrieve media counts, library size, and server URL info |
list_renderers |
None | List DLNA/UPnP MediaRenderers with stable IDs |
cast_media_to_renderer |
file_id (integer), renderer_id (string) |
Start playing a media file on a discovered renderer |
control_renderer |
renderer_id (string), action ("play"|"pause"|"stop") |
Send playback control commands to a renderer |
list_media |
category (optional string), limit (optional integer) |
Retrieve a flat list of indexed media files (all, audio, video, image) |
list_playlists |
None | List all playlists stored on the server |
create_playlist |
name (string), description (optional string) |
Create a new media playlist |
delete_playlist |
playlist_id (integer) |
Delete a playlist by ID |
add_to_playlist |
playlist_id (integer), media_file_ids (integer[]) |
Add multiple tracks in bulk to a playlist |
remove_from_playlist |
playlist_id (integer), media_file_id (integer) |
Remove a specific track from a playlist |
get_playlist_tracks |
playlist_id (integer) |
Retrieve all media files/tracks in a specific playlist |
cast_playlist_to_renderer |
playlist_id (integer), renderer_id (string) |
Cast a playlist to a local renderer and start playing it |
-
Discover TVs:
curl -X POST "http://localhost:8080/mcp/message?client_id=agent-1" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"tools/call","id":1,"params":{"name":"list_renderers","arguments":{}}}'
-
Search for Media:
curl -X POST "http://localhost:8080/mcp/message?client_id=agent-1" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"tools/call","id":2,"params":{"name":"search_media","arguments":{"query":"matrix"}}}'
By default, the web interface and management endpoints run without authentication for easy local usage.
To enable administrative/management authentication, start VuIO with either:
- The
--authcommand-line option - The
VUIO_AUTH=trueenvironment variable
When enabled:
- Visits to the dashboard in the browser will be redirected to a secure sign-in page.
- You must sign in using the administration token.
- API endpoints (excluding DLNA/UPnP SOAP endpoints) will require a session cookie or bearer token.
On startup, if no pre-defined token is found, VuIO generates a random cryptographically secure token and writes it to a file named admin.token in the configuration directory (e.g. ./admin.token). Keep this file private.
You can also set a pre-defined administration token via the VUIO_ADMIN_TOKEN environment variable:
export VUIO_ADMIN_TOKEN="your-custom-secure-token"Contributions welcome! Please ensure cross-platform compatibility is maintained. Input license = output license