SSD Nodes Learn 🎉 VPS từ $5.50/tháng
Hướng dẫn Matt ConnorBởi Matt Connor

Tự host music streaming bằng Navidrome trên VPS

Chạy Navidrome trên VPS để nghe thư viện nhạc riêng trên mọi điện thoại. Tính dung lượng, chọn Subsonic client, offline sync, TLS và backup an toàn.

Tự host music streaming trên VPS mang lại gì

Tự host music streaming trên VPS nghĩa là bạn tự chạy player và cung cấp nhạc. Server lưu các file bạn đã sở hữu, và mọi điện thoại đều có thể truy cập qua Internet bằng thông tin đăng nhập thông thường. Hãy xác định rõ điểm đánh đổi trước khi bắt đầu: cách này thay thế player của một dịch vụ streaming, không thay thế catalog của dịch vụ đó. Thư viện của bạn sẽ không có thêm nội dung mới cho đến khi bạn mua hoặc rip nội dung đó, rồi copy file lên server.

Audio ít gây tải cho server hơn video rất nhiều. Các file audio nhỏ, điện thoại giải mã được mọi format phổ biến mà không cần hỗ trợ thêm, và một người nghe sử dụng ít bandwidth hơn một cuộc gọi video. CPU không phải vấn đề đáng lo ở đây. Dung lượng disk mới là giới hạn thực tế. Hai yếu tố khác quyết định việc này có hoạt động tốt hay không là chất lượng tag và việc app trên điện thoại có thể download nhạc để nghe offline hay không.

Dùng máy chủ nhạc nào: Navidrome, Jellyfin hay Funkwhale?

Navidrome là lựa chọn mặc định nếu bạn chỉ quan tâm đến âm thanh. Đây là một binary Go duy nhất chạy trong một container duy nhất, đồng thời lưu trạng thái trong một cơ sở dữ liệu SQLite duy nhất. Navidrome cung cấp Subsonic API, nhờ đó mới có một hệ sinh thái lớn các ứng dụng điện thoại bên thứ ba. Phiên bản 0.63.2 là phiên bản hiện tại vào tháng 8 năm 2026. Dự án cho biết phần mềm chạy tốt trên phần cứng nhỏ như Raspberry Pi Zero, nên phần mềm máy chủ không phải là khoản bạn phải chi trả.

Jellyfin đáng dùng nếu bạn đã chạy Jellyfin làm máy chủ media trên một VPS để xem video. Thư viện nhạc của Jellyfin hoạt động tốt, còn Finamp là ứng dụng nhạc Jellyfin cho Android và iOS, có thể tải track để nghe offline. Giới hạn nằm ở API. Jellyfin không có Subsonic endpoint tích hợp sẵn, còn plugin cộng đồng từng bổ sung endpoint này đã ngừng được cập nhật từ năm 2022. Vì vậy, hệ sinh thái ứng dụng Subsonic không hỗ trợ Jellyfin. Thay vào đó, bạn dùng các ứng dụng giao tiếp với API riêng của Jellyfin, nhưng số lượng các ứng dụng này ít hơn.

Funkwhale là lựa chọn liên kết liên máy chủ, và phiên bản 2.0 được phát hành vào tháng 3 năm 2026. Một máy chủ Funkwhale được gọi là pod. Các pod liên kết với nhau qua ActivityPub, là protocol đứng sau Mastodon, và người dùng trên một pod có thể theo dõi thư viện public trên pod khác. Funkwhale cũng cung cấp một phần của Subsonic API, nhưng có một điểm khác biệt cần biết: mỗi người dùng phải đặt một mật khẩu Subsonic riêng trong phần cài đặt của mình, vì protocol Subsonic yêu cầu mật khẩu mà máy chủ có thể đọc lại. Funkwhale phức tạp hơn khi cài đặt vì cần PostgreSQL và task queue cùng với web app.

Hãy chọn Navidrome, trừ khi bạn cần federation hoặc đã chạy Jellyfin. Phần còn lại của hướng dẫn này sẽ thiết lập Navidrome bằng Docker.

API Subsonic quyết định ứng dụng điện thoại bạn dùng như thế nào

Subsonic từng là một music server có HTTP API trở thành ngôn ngữ chung cho audio tự host, còn OpenSubsonic là dự án cộng đồng tiếp tục mở rộng API đó. Vì vậy điện thoại của bạn có nhiều lựa chọn. Navidrome không cung cấp mobile app riêng và cũng không cần làm vậy, vì mọi Subsonic client đều đăng nhập bằng địa chỉ server và thông tin tài khoản của bạn.

Điều này quan trọng nhất với tính năng đồng bộ offline, vì đây là tính năng quyết định hệ thống có đáp ứng được nhu cầu sử dụng hằng ngày hay không. Điện thoại trong đường hầm không thể kết nối đến server, nên client phải sao chép các file vào local storage từ trước. Client nào cũng stream được. Chỉ một số client hỗ trợ download. Danh mục client tại navidrome.org/apps cho biết client nào hỗ trợ tính năng này. Cả hai nền tảng đều có nhiều lựa chọn: Substreamer và Ultrasonic trên Android, Amperfy và play:Sub trên iOS. Một số client tốt nhất là ứng dụng trả phí, trong đó Symfonium trên Android là ứng dụng được nhắc đến thường xuyên nhất. Hãy cài thử 2 ứng dụng trước khi quyết định, vì đây là phần của hệ thống bạn sẽ sử dụng mỗi ngày.

Thư viện nhạc cần bao nhiêu dung lượng lưu trữ?

ChartStorage per 1,000 albums by audio format
The data behind this chart
[
  {
    "label": "Opus 128k",
    "kbps": 128,
    "gb_per_1000_albums": 43
  },
  {
    "label": "MP3 320k",
    "kbps": 320,
    "gb_per_1000_albums": 108
  },
  {
    "label": "FLAC 16/44.1",
    "kbps": 900,
    "gb_per_1000_albums": 304
  },
  {
    "label": "FLAC 24/96",
    "kbps": "3,000",
    "gb_per_1000_albums": "1,013"
  }
]

Đây là các con số theo bậc độ lớn, được suy ra từ bitrate chứ không phải số đo của một bộ sưu tập thực tế. Phép tính đủ đơn giản để bạn đối chiếu với các file của mình. Coi một album dài 45 phút, tức 2,700 giây. Nhân bitrate tính bằng kilobit mỗi giây với 2,700, rồi chia cho 8,000 để ra megabyte. Ở mức 320 kbps, một album chiếm 108 MB, nên một nghìn album chiếm khoảng 108 GB.

Lossless cho kết quả khác. FLAC chất lượng CD thường có bitrate trung bình gần 900 kbps với các bản nhạc thông thường, nên một nghìn album như vậy cần khoảng 304 GB. Thư viện 24 bit, 96 kHz cần khoảng 1,013 GB, tức tròn một terabyte cho một bộ sưu tập mà bạn có thể liệt kê trên giấy. Bản Opus tối ưu cho điện thoại ở mức 128 kbps chứa cùng một nghìn album trong 43 GB. Chạy du -sh /path/to/music trên thư viện hiện có, vì bitrate trung bình của chính bạn là con số duy nhất cần quan tâm khi chọn gói.

Bandwidth là phần nhỏ hơn trong tổng chi phí. Một luồng 320 kbps tương đương 40 kilobyte mỗi giây, nên một giờ nghe nhạc truyền khoảng 144 MB. Một trăm giờ mỗi tháng vào khoảng 14 GB, thường không đáng kể so với hạn mức truyền dữ liệu của VPS. Ngoại lệ là lần đồng bộ offline đầu tiên trên điện thoại, có thể đẩy hàng chục gigabyte trong một buổi tối.

Tầng lưu trữ hay tầng compute?

Music server là một khối dữ liệu cold lớn, gần như không cần xử lý. Đọc file ở tốc độ 40 kilobyte mỗi giây khiến mọi disk gần như không hoạt động, còn CPU chỉ làm việc khi scan thư viện hoặc transcode, những việc mà bạn hầu như không thực hiện. Vì vậy, NVMe nhanh trong gói compute không mang lại lợi ích ở đây, trong khi giá trên mỗi gigabyte lại là thứ khiến bạn không thể upload các bản FLAC. Đây là trường hợp storage VPS phù hợp hơn VPS thông thường, vì các gói này được tính giá theo terabyte thay vì theo core.

Nhu cầu RAM không cao. Navidrome phục vụ thư viện cá nhân chỉ với vài trăm megabyte, và quá trình scan mới là lúc sử dụng nhiều RAM nhất, không phải lúc phát nhạc. Cấp cho máy 1 GB hoặc 2 GB RAM, rồi dùng phần ngân sách còn lại cho disk.

Cài đặt Navidrome bằng Docker Compose

Trước tiên, hãy tạo các thư mục và đặt quyền sở hữu cho user id mà container sẽ chạy dưới đó. Nếu bạn chưa quen với Compose, Docker Compose trên VPS giải thích các giả định của file này.

sudo install -d -m 755 -o 1000 -g 1000 /srv/navidrome /srv/music

Chỉ cần ghi docker-compose.yml, không thêm gì đáng kể, trong thư mục riêng của nó:

services:
  navidrome:
    image: deluan/navidrome:0.63.2
    user: "1000:1000"
    ports:
      - "127.0.0.1:4533:4533"
    restart: unless-stopped
    environment:
      ND_LOGLEVEL: "info"
      ND_SESSIONTIMEOUT: "24h"
      ND_SCANNER_SCHEDULE: "@every 24h"
      ND_BACKUP_PATH: "/data/backup"
      ND_BACKUP_SCHEDULE: "0 4 * * *"
      ND_BACKUP_COUNT: "7"
    volumes:
      - /srv/navidrome:/data
      - /srv/music:/music:ro
docker compose up -d
docker compose ps

docker compose ps phải hiển thị service đang chạy thay vì liên tục restart. Container restart theo vòng lặp gần như luôn là do lỗi quyền trên /srv/navidrome, và docker compose logs navidrome cho biết tên file mà container không thể ghi.

Có 4 chi tiết trong file này cần giải thích. Port chỉ được publish trên 127.0.0.1, nên server chỉ có thể được truy cập thông qua reverse proxy, không phải trực tiếp trên port 4533 từ Internet công cộng: Docker tự tạo rule firewall, vì vậy một 4533:4533 thông thường vẫn bị expose ngay cả trên máy mà ufw báo mọi kết nối đều bị deny. Volume chứa nhạc được mount ở chế độ chỉ đọc, nghĩa là lỗi trong scanner không thể xóa bản sao duy nhất của bạn. ND_SCANNER_SCHEDULE bị disable mặc định, và các hướng dẫn viết trước Navidrome 0.55 gọi nó là ND_SCANSCHEDULE, một tên không còn tồn tại. 3 thiết lập backup bật tính năng backup database tích hợp mà phần backup bên dưới sử dụng.

Đưa thư viện nhạc lên server

Dùng rsync để sao chép thư viện lên server. rsync có thể tiếp tục từ vị trí dừng thay vì bắt đầu lại khi kết nối bị ngắt.

rsync -av --info=progress2 ~/Music/ user@music.example.com:/srv/music/

Dấu gạch chéo ở cuối source rất quan trọng. Nếu thiếu, bạn sẽ nhận được /srv/music/Music. Khi sao chép xong, sửa lại quyền sở hữu:

sudo chown -R 1000:1000 /srv/music
id -u

Container chạy bằng user id 1000 và mount ở chế độ chỉ đọc, vì vậy mọi file phải cho id đó quyền đọc. Nếu tài khoản SSH của bạn trên VPS không có uid 1000, các file được sao chép lên sẽ thuộc về user khác. Khi đó quá trình quét không tìm thấy track nào, còn web interface vẫn trống. id -u in ra id thực của bạn. Bài PUID và PGID hoạt động thế nào trong Docker container giải thích đầy đủ cách ánh xạ này.

Reverse proxy và TLS để điện thoại hoạt động từ mọi nơi

Trỏ bản ghi DNS A đến VPS, sau đó cấu hình Caddy bằng 3 dòng:

music.example.com {
    reverse_proxy 127.0.0.1:4533
}
sudo systemctl reload caddy

Caddy sẽ yêu cầu certificate ở request đầu tiên. Phải mở cả cổng 80 và 443, vì thử thách ACME (môi trường quản lý certificate tự động) được trả lời trên cổng 80. Trên nginx, thêm proxy_buffering off; vào block location: Navidrome gửi các sự kiện tiến trình đến web interface qua một kết nối duy trì lâu dài. Khi bật buffering, interface sẽ chờ vì nginx vẫn đang giữ response. Nếu bạn chạy dịch vụ dưới một path như /music thay vì subdomain riêng, hãy đặt ND_BASEURL thành cùng path đó. Nếu không, interface sẽ tải thành một trang trắng. So sánh 3 proxy phổ biến trong Nginx, Caddy và Traefik trên VPS.

Mở site và tạo account đầu tiên. Không có password mặc định. Visitor đầu tiên sẽ được yêu cầu tạo user admin, vì vậy hãy làm việc này trước khi cung cấp địa chỉ cho người khác. Sau đó kiểm tra đúng path mà phone client sử dụng:

SALT=$(openssl rand -hex 6)
TOKEN=$(printf '%s%s' 'YOUR_PASSWORD' "$SALT" | md5sum | cut -d' ' -f1)
curl -s "https://music.example.com/rest/ping.view?u=YOUR_USER&t=$TOKEN&s=$SALT&v=1.16.1&c=curl&f=json"

Response bình thường bắt đầu bằng {"subsonic-response":{"status":"ok" và cho biết navidrome là server type. Nếu body chứa "status":"failed" và error code 40, proxy đang hoạt động bình thường nhưng credentials không đúng. Nếu xuất hiện lỗi certificate ở bước này, hãy xử lý trước tiên. Hầu hết phone client sẽ từ chối certificate không hợp lệ bằng một thông báo không cho người dùng biết nguyên nhân.

Vì sao thư viện hiển thị sai sau lần quét đầu tiên

Navidrome duyệt nhạc theo tag thay vì theo thư mục, nên tag quyết định nội dung bạn thấy. Track không có tag album artist sẽ được xếp theo track artist. Vì vậy, một album tổng hợp có artist khác nhau trên từng track sẽ bị tách thành hai mươi album, mỗi album chỉ có một track. Hãy sửa tag trong file, không sửa trong Navidrome: MusicBrainz Picard và beets đều tra album trong cơ sở dữ liệu MusicBrainz rồi ghi lại các tag chuẩn.

Navidrome cũng tách một tag chứa nhiều artist thành các artist riêng. Vì vậy, tên của một band có chứa ký tự phân tách có thể bị tách nhầm; AC/DC là ví dụ phổ biến nhất. ND_SCANNER_ARTISTSPLITEXCEPTIONS chứa những tên không bao giờ được tách.

File mới được file watcher phát hiện vài giây sau khi được thêm vào. Watcher phụ thuộc vào kernel change notification. Các notification này không được gửi cho file được ghi trên network share mount từ máy khác. Vì vậy, với kiểu triển khai này, ND_SCANNER_SCHEDULE là cơ chế giữ cho thư viện luôn được cập nhật. Full rescan đọc tag của mọi file, nên sẽ chậm với thư viện lớn. Đây là một lý do nên bảo vệ database được đề cập bên dưới.

Người dùng, playlist và chia sẻ

Quản trị viên tạo các tài khoản khác trong giao diện web; người dùng không thể tự đăng ký. Mỗi người dùng có số lượt phát, playlist, mục yêu thích và đánh giá riêng, nên các thành viên trong nhà không phải dùng chung một hồ sơ sở thích.

Playlist được thêm từ hai nguồn. Playlist tạo trong client được lưu trong database. Tệp .m3u được chép vào thư mục thư viện sẽ được import trong lúc scan. Đây là cách đơn giản để chuyển playlist từ trình phát trên desktop. Smart playlist là các tệp .nsp, tức các tệp JSON nhỏ chứa bộ quy tắc và được import theo cách tương tự. Chúng tự cập nhật khi thư viện thay đổi.

Tính năng chia sẻ được bật mặc định từ phiên bản 0.63.0, phát hành vào tháng 7 năm 2026. Tính năng này cho phép người dùng tạo public link đến một album mà bất kỳ ai cũng có thể mở mà không cần đăng nhập. Trên server chứa toàn bộ thư viện của bạn, đây có thể không phải điều bạn muốn; đặt ND_ENABLESHARING thành false sẽ tắt tính năng này.

Sao lưu database riêng với các file nhạc

Tính năng backup của Navidrome chỉ bao phủ database. Tài liệu nói rất rõ: quy trình backup sao lưu database, gồm users, play counts và các dữ liệu khác, nhưng không sao lưu music hoặc config. Đây là cách phân tách đúng, vì hai phần này có cách khôi phục khác nhau. Có thể chép lại các file nhạc từ ổ đĩa đã dùng để rip chúng. Play counts, ratings, favourites và playlists không tồn tại ở nơi nào khác, còn rescan không thể khôi phục chúng.

File compose đã ghi một bản sao hằng đêm vào /data/backup và giữ lại bảy bản. Hãy tạo thủ công một bản trước khi upgrade:

sudo docker compose run --rm navidrome backup create

Thao tác restore sẽ xóa database hiện tại rồi chép bản backup vào vị trí của nó. Navidrome phải được dừng trước khi chạy thao tác này. Không có cách restore nào an toàn trên server đang chạy.

Các file đó vẫn nằm trên cùng VPS, nên sẽ không tồn tại nếu VPS gặp sự cố. Hãy định kỳ đẩy /srv/navidrome sang máy khác hoặc object storage. Đây là mục đích của restic và BorgBackup. Toàn bộ directory này khá nhỏ, thường dưới một gigabyte, nên một bản sao được mã hóa hằng ngày và lưu ngoài máy gần như không tốn chi phí, nhưng vẫn khôi phục được mọi dữ liệu mà rescan không thể khôi phục.

FAQ

Tôi có cần transcode nhạc trên VPS không?

Hầu như không bao giờ. Điện thoại và trình duyệt tự giải mã MP3, AAC, Opus và FLAC, nên server gửi nguyên file và gần như không dùng CPU. Có một trường hợp đáng bật tính năng này: stream thư viện FLAC qua mạng di động. Khi đó, chuyển từ khoảng 900 kbps xuống Opus ở 128 kbps sẽ giảm lượng dữ liệu sử dụng khoảng 7 lần. Navidrome có thể thực hiện việc này theo từng user và player, và tính năng vẫn tắt cho đến khi bạn bật.

Tại sao app trên điện thoại của tôi không tải nhạc để nghe offline được?

Vì lưu trữ offline là tính năng của client, không phải của server. Subsonic API cho phép mọi client tải toàn bộ file, nhưng app quyết định có giữ một bản sao trên điện thoại hay không. Kiểm tra danh sách client tại navidrome.org/apps và chọn client có phần mô tả đề cập đến offline download hoặc caching. Một số client chỉ cache những nội dung bạn đã phát, việc này không giống với đồng bộ cả album trước một chuyến đi.

Tại sao một album lại bị tách thành nhiều album sau khi scan?

Tag album artist bị thiếu hoặc không nhất quán giữa các track. Navidrome nhóm theo tag thay vì theo folder, nên 12 track có 12 giá trị artist khác nhau và không có giá trị album artist chung sẽ bị nhận diện thành 12 album. Đặt tag album artist trên mọi track của album, thường là Various Artists đối với album tổng hợp, rồi cho Navidrome scan lại. MusicBrainz Picard hoặc beets có thể thực hiện việc này cho toàn bộ folder cùng lúc.

Streaming nhạc tự host có thay thế được Spotify không?

Nó thay thế player và thư viện nhạc, không thay thế catalogue. Bạn có bộ sưu tập riêng trên mọi thiết bị, cùng playlist và lượt phát mà bất kỳ thay đổi nào về licensing cũng không thể lấy đi. Bạn không có các bản phát hành mới và cũng không có các đề xuất được xây dựng từ thói quen nghe nhạc của người khác. Phần lớn người vận hành hệ thống này mua nhạc và vẫn giữ một tài khoản streaming giá rẻ để tìm nhạc mới.

#navidrome#music#streaming#tự lưu trữ#media-server