← Volver a todos los artículos

Tu propio Spotify en casa: Navidrome en Ubuntu Server con Docker, Cloudflare Tunnel y Substreamer

En el artículo sobre mi servidor doméstico actual comentaba que uno de mis servicios favoritos dentro del Homelab es Navidrome: un servidor de música ligero, rapidísimo y de código abierto que me permite tener mi propio "Spotify privado" sin depender de suscripciones ni algoritmos, manteniendo mi colección en formatos sin pérdida como FLAC.

En esta guía paso a paso vamos a ver cómo desplegar Navidrome sobre Ubuntu Server utilizando Docker Compose, cómo exponerlo al exterior de forma segura sin abrir ni un solo puerto en el router mediante Cloudflare Tunnel, y cómo escucharlo desde el móvil con Substreamer.

¿Por qué Navidrome?

Si tienes una biblioteca musical propia acumulada durante años, Navidrome es probablemente la mejor alternativa actual a servidores pesados:

  • Consumo ridículo de recursos: Escrito en Go, apenas consume unos pocos megas de RAM incluso con bibliotecas de decenas de miles de canciones.
  • Compatible con la API de Subsonic: Esto significa que no estás atado a su interfaz web; puedes usar decenas de aplicaciones nativas en iOS, Android, Windows, macOS o Linux.
  • Soporte multiusuario y transcodificación al vuelo: Puedes compartir acceso con familiares y configurar conversión automática de FLAC a MP3/Opus cuando escuchas con datos móviles para no agotar tu tarifa.

Interfaz web de Navidrome mostrando la biblioteca de álbumes

1. Preparando el entorno en Ubuntu Server

Partimos de una máquina con Ubuntu Server en nuestro servidor doméstico con Docker y el plugin Docker Compose instalados.

Primero vamos a crear la estructura de directorios donde guardaremos la configuración del contenedor y los datos persistentes de Navidrome (base de datos SQLite, caché de carátulas y playlists):

mkdir -p ~/docker/navidrome/data
cd ~/docker/navidrome

Supongamos que nuestra biblioteca de música se encuentra almacenada en /mnt/almacenamiento/musica dentro del servidor. Es importante conocer el UID y GID de nuestro usuario en Ubuntu (habitualmente 1000:1000) para que el contenedor no tenga problemas de permisos:

id -u
id -g

2. Despliegue con Docker Compose y Cloudflare Tunnel

En lugar de abrir puertos en el router (algo tedioso y a menudo imposible si tu operadora usa CGNAT), vamos a integrar Cloudflare Tunnel (cloudflared) directamente junto a nuestro servicio. De este modo, el propio servidor doméstico establece un túnel saliente cifrado hacia la red de Cloudflare y nos proporciona certificado HTTPS automático.

Creamos un archivo .env en ~/docker/navidrome/.env para almacenar el token de nuestro túnel de forma limpia:

TUNNEL_TOKEN=tu_token_de_cloudflare_tunnel_aqui

Ahora creamos el archivo docker-compose.yml:

services:
  navidrome:
    image: deluan/navidrome:latest
    container_name: navidrome
    user: 1000:1000
    restart: unless-stopped
    ports:
      - "4533:4533"
    environment:
      ND_SCANSCHEDULE: 1h
      ND_LOGLEVEL: info
      ND_SESSIONTIMEOUT: 24h
      ND_BASEURL: ""
      ND_ENABLETRANSCODINGCONFIG: "true"
    volumes:
      - "./data:/data"
      - "/mnt/almacenamiento/musica:/music:ro"
    networks:
      - navidrome_net

  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: cloudflared-navidrome
    restart: unless-stopped
    command: tunnel --no-autoupdate run
    environment:
      TUNNEL_TOKEN: ${TUNNEL_TOKEN}
    depends_on:
      - navidrome
    networks:
      - navidrome_net

networks:
  navidrome_net:
    driver: bridge

Detalles clave de esta configuración

  • Volumen de música en modo solo lectura (:ro): Montamos /mnt/almacenamiento/musica:/music:ro con el flag ro (read-only). Así garantizamos que Navidrome pueda leer e indexar todos los metadatos e imágenes, pero jamás pueda modificar o borrar accidentalmente nuestros archivos originales.
  • ND_SCANSCHEDULE: 1h: Escanea automáticamente la carpeta cada hora para detectar nuevos álbumes que hayamos añadido al servidor.
  • Red interna navidrome_net: Tanto navidrome como cloudflared comparten red en Docker, lo que permite que el túnel apunte directamente a http://navidrome:4533 sin siquiera necesitar exponer el puerto 4533 al host si no queremos acceso directo por IP local.

Nota: Si ya tienes el demonio de cloudflared instalado directamente a nivel de sistema en Ubuntu Server o centralizado en otro contenedor para todos tus servicios (por ejemplo con Coolify), puedes omitir el bloque cloudflared del docker-compose.yml y apuntar tu túnel a http://localhost:4533.

Levantamos el stack en segundo plano:

docker compose up -d

3. Configurando la salida al exterior en Cloudflare Zero Trust

Para conectar nuestro servidor doméstico con un subdominio propio (por ejemplo, music.tudominio.xyz), seguimos estos pasos en el panel de Cloudflare Zero Trust:

  • Entramos en Networks > Tunnels y pulsamos en Create a tunnel.
  • Elegimos el conector Cloudflared, le damos un nombre identificativo (por ejemplo, homelab-ubuntu) y copiamos el Token que nos genera para pegarlo en nuestro archivo .env (o instalar el servicio en Ubuntu si preferimos tenerlo nativo en el host).
  • En la pestaña de aplicaciones publicadas (Public Hostname), añadimos una nueva ruta pública:
  • Subdominio: music
  • Dominio: tudominio.xyz
  • URL del servicio: http://navidrome:4533 (si cloudflared va en el mismo docker-compose.yml) o la IP local / http://localhost:4533 (si cloudflared corre directamente en el servidor doméstico).

Configuración del túnel en Cloudflare Zero Trust apuntando al puerto 4533 de Navidrome

En cuestión de segundos, al entrar desde cualquier navegador a https://music.tudominio.xyz nos recibirá la pantalla de bienvenida de Navidrome para crear nuestro usuario administrador. Es fundamental completar este primer registro inmediatamente tras exponer el servicio para dejar cerrado el panel de administración.

4. Consumiendo nuestra música en el móvil con Substreamer

Aunque la interfaz web de Navidrome es excelente en escritorio, en el día a día desde el móvil o en el coche con CarPlay / Android Auto necesitamos una aplicación nativa que gestione bien la caché offline y los cortes de cobertura. Aquí es donde brilla Substreamer (disponible de forma gratuita tanto en iOS como en Android).

Aplicación móvil Substreamer conectada a Navidrome mostrando actividad y álbumes recientes

Gracias a que Navidrome implementa el estándar Subsonic API, conectar Substreamer con nuestro servidor a través del túnel de Cloudflare no lleva más de un minuto:

  • Descargamos y abrimos Substreamer en nuestro smartphone.
  • En la pantalla de conexión introducimos los datos de nuestro servidor:
  • Server Address: https://music.tudominio.xyz (muy importante incluir https:// y no añadir barras ni puertos al final, ya que Cloudflare Tunnel sirve por el puerto estándar 443).
  • Username: Tu usuario creado en Navidrome.
  • Password: Tu contraseña de Navidrome.
  • Pulsamos en Connect y automáticamente empezará a sincronizar la lista de artistas, álbumes, carátulas y listas de reproducción.

Ajustes recomendados en Substreamer

Para sacarle todo el partido fuera de casa, te recomiendo revisar un par de opciones dentro de Settings en la app:

  • Streaming y límite de bitrate en red móvil: En el apartado de red puedes indicar que en Wi-Fi reproduzca en calidad original (FLAC sin compresión) y que bajo datos móviles solicite un flujo comprimido (por ejemplo, 192 kbps o 320 kbps). Navidrome hará la conversión al vuelo sin saturar la CPU de tu Ubuntu Server.
  • Caché automática: Substreamer guarda automáticamente en la memoria del teléfono las canciones que vas escuchando. Además, puedes marcar tus álbumes o playlists favoritas para descarga offline antes de un viaje.

Conclusión

Con apenas un archivo docker-compose.yml sobre Ubuntu Server, un túnel de Cloudflare que nos ahorra peleas con el router o el CGNAT, y Substreamer en el bolsillo, hemos montado una plataforma de streaming musical privada, rápida y accesible desde cualquier rincón del mundo bajo HTTPS. Una de esas piezas imprescindibles en cualquier servidor doméstico que se amortiza desde el primer día.