วิธีติดตั้ง Jellyfin บน VPS ด้วย Docker
เรียนรู้วิธีตั้งค่า Jellyfin บน VPS โดยใช้ Docker พร้อมวิธีแก้ปัญหาเรื่อง file permissions และการจัดการ CPU transcoding เมื่อไม่มี GPU บน VPS ของคุณ
สิ่งที่คุณกำลังสร้าง
Jellyfin media server บน VPS: ประกอบด้วยหนึ่ง container, สาม volumes และ block-storage disk สำหรับเก็บภาพยนตร์และรายการต่างๆ ซึ่งสามารถเข้าถึงได้ผ่าน browser หรือ Jellyfin app ใดก็ได้ การติดตั้งใช้เพียง compose file จำนวน 15 บรรทัด ปัญหาที่มักเกิดขึ้นภายหลังมาจากสองสาเหตุหลัก คือ สิทธิ์การเข้าถึงไฟล์ (file permissions) ที่ container ไม่สามารถอ่านได้ และการสั่งให้ VPS ที่ไม่มี GPU ทำการ transcode วิดีโอ คู่มือนี้จะเน้นเนื้อหาส่วนใหญ่ไปที่สองประเด็นนี้ เนื่องจากเป็นสาเหตุหลักของปัญหาที่พบบ่อย
Jellyfin เป็นซอฟต์แวร์ฟรีและเป็น open source อย่างเต็มรูปแบบ โดยไม่มีการบังคับให้สร้างบัญชี ไม่มีฟีเจอร์ที่ต้องจ่ายเงิน และไม่มีการเก็บข้อมูล telemetry ซึ่งเป็นเหตุผลที่ทำให้ Jellyfin ติดอันดับ สิ่งที่ควรทำ self-hosting ในปี 2026 โปรแกรมนี้ใช้สำหรับเล่นสื่อที่คุณเป็นเจ้าของเท่านั้น โดยไม่มีการจัดเตรียมเนื้อหาใดๆ และคู่มือนี้ไม่ได้มีวัตถุประสงค์เพื่อการจัดหาเนื้อหาเหล่านั้น
ความเป็นจริงของการ Transcoding ก่อนที่คุณจะเช่าบริการใดๆ
โปรดอ่านส่วนนี้ก่อน เพราะข้อมูลนี้จะเปลี่ยนการตัดสินใจเลือกซื้อของคุณ เมื่อคุณกดเล่นไฟล์ Media server จะทำงานอย่างใดอย่างหนึ่งจากสองอย่างนี้ Direct play คือการสตรีมไฟล์ตามสภาพเดิม โดย VPS จะอ่านข้อมูลจาก disk และส่งผ่านเครือข่าย ซึ่งแทบไม่ใช้ CPU เลย ส่วน Transcoding คือการเข้ารหัสวิดีโอใหม่แบบเรียลไทม์ เช่น การเปลี่ยนความละเอียด, เปลี่ยน codec หรือการฝังคำบรรยายลงในภาพ ซึ่งกระบวนการนี้ใช้พลังจาก CPU โดยตรง
VPS ทั่วไปจะไม่มี GPU ดังนั้นการ transcode ทุกครั้งจะทำงานผ่าน CPU ด้วย libx264/libx265 และการเข้ารหัสด้วย software นั้นใช้ทรัพยากรสูงมาก การ transcode ไฟล์ 1080p H.264 เพียงไฟล์เดียวอาจทำให้ shared vCPUs หลายตัวทำงานหนักจนเต็มพิกัด ส่วนการ transcode ไฟล์ 4K หรือ HEVC มักจะไม่สามารถประมวลผลได้ทันเวลา ส่งผลให้การเล่นวิดีโอหยุดชะงักและเกิดการ buffer ตลอดเวลา สำหรับ Hardware transcoding ซึ่งช่วยให้การประมวลผลบนเครื่องที่บ้านที่มี Intel iGPU หรือการ์ด Nvidia ทำได้ในราคาถูกนั้น จะไม่มีให้ใช้งานบน VPS เว้นแต่ผู้ให้บริการจะเปิดให้เช่า instance ที่มี GPU
ดังนั้น กลยุทธ์หลักบน VPS คือ: หลีกเลี่ยงการ transcoding ควรเก็บไฟล์ในรูปแบบ codec ที่อุปกรณ์ปลายทางสามารถเล่นได้โดยตรง เช่น วิดีโอ H.264, เสียง AAC หรือ AC3 ใน container แบบ MP4 หรือ MKV และควรเลือกแอปพลิเคชันปลายทางที่รองรับ direct-play เช่น แอป Jellyfin แบบ native สำหรับ Android TV, iOS และ Roku รวมถึง Infuse, Kodi และ Jellyfin Media Player บน desktop หากทำเช่นนี้ VPS จะไม่ต้องเรียกใช้ ffmpeg เลย และเครื่องที่มีเพียง 2 vCPU ก็สามารถสตรีมให้ผู้ใช้งานหลายคนพร้อมกันได้ หากคุณวางแผนที่จะใช้การ transcode คุณจำเป็นต้องใช้เครื่องที่มีขนาดใหญ่และราคาสูงกว่ามาก และถึงกระนั้น การใช้งาน 4K ก็ยังมีความเสี่ยงสูง
ควรคำนวณเรื่อง bandwidth ด้วย เพราะนี่คืออีกหนึ่งสิ่งที่ต้องระวัง Direct play จะส่งไฟล์ตาม bitrate ของไฟล์นั้นๆ ไฟล์ 1080p ที่ถูกบีบอัดจะใช้ bitrate ประมาณ 8-12 Mbps, ไฟล์ 1080p Blu-ray remux ใช้ 20-30 Mbps และ 4K HDR ใช้ 40-80 Mbps หากมีผู้ใช้งาน 3 คนเล่นไฟล์ขนาด 10 Mbps พร้อมกัน จะต้องใช้ bandwidth ขาออก (upload) จาก VPS อย่างต่อเนื่องถึง 30 Mbps โปรดตรวจสอบตัวเลขสองค่าในแพ็กเกจของคุณ: ความเร็วพอร์ต (สามารถส่งข้อมูลขาออกได้ถึง 30 Mbps หรือไม่?) และปริมาณการรับส่งข้อมูลต่อเดือน (monthly transfer cap) ภาพยนตร์ขนาด 10 Mbps ความยาว 2 ชั่วโมง จะใช้ข้อมูลประมาณ 9 GB ดังนั้นโควตา 1 TB ต่อเดือน จะรองรับภาพยนตร์ลักษณะนี้ได้เพียงประมาณหนึ่งร้อยเรื่องต่อเดือน หรือประมาณ 3-4 เรื่องต่อวัน และหากสมาชิกในบ้านดูไฟล์ 4K ซึ่งมี bitrate สูงกว่า 4-8 เท่า ข้อมูลจะถูกใช้หมดไปอย่างรวดเร็วกว่านั้นมาก
Prerequisites
- Ubuntu 24.04 KVM VPS ที่ติดตั้งใหม่ โดยมีสิทธิ์ root หรือ sudo และติดตั้ง Docker พร้อมกับ Compose plugin เรียบร้อยแล้ว
- Block-storage volume สำหรับเก็บไฟล์ media โดยเลือกขนาดให้เหมาะสมกับคลังไฟล์ของคุณ (ดูรายละเอียดการคำนวณขนาดด้านล่าง) ห้ามเก็บไฟล์ภาพยนตร์ไว้ใน root disk ขนาดเล็กที่มาพร้อมกับ VPS
- Domain name หากต้องการเข้าถึงผ่าน HTTPS จากภายนอก หรือ WireGuard VPN on the same VPS หากต้องการใช้งานแบบส่วนตัวเท่านั้น
- ไฟล์ media ที่คุณมีสิทธิ์ในการสตรีมตามกฎหมาย เช่น ไฟล์ที่ rip เอง ไฟล์ที่บันทึกเอง หรือไฟล์ที่คุณเป็นเจ้าของ
Mount block storage ก่อนเป็นอันดับแรก
ทำการ Attach volume ผ่านทาง control panel ของผู้ให้บริการ จากนั้นค้นหาและทำการ mount โดยดูชื่อ device จาก lsblk ซึ่งอาจจะเป็นชื่ออย่าง /dev/sdb หรือ /dev/vdb และห้ามใช้ root disk โดยเด็ดขาด
lsblk
sudo mkfs.ext4 /dev/sdb # ONLY on a new, empty volume — this ERASES it
sudo mkdir -p /mnt/media
sudo blkid /dev/sdb # copy the UUID shown for this deviceควร mount ด้วย UUID แทนการใช้ /dev/sdb เนื่องจากชื่อ device อาจเปลี่ยนแปลงลำดับใหม่เมื่อมีการ reboot ซึ่งอาจทำให้เกิดความผิดพลาดในการ format หรือ mount disk ผิดลูก ให้เพิ่มบรรทัดคำสั่งลงใน /etc/fstab ดังนี้:
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /mnt/media ext4 defaults,nofail 0 2sudo mount -a
df -h /mnt/medianofail มีความสำคัญมาก หากไม่มีค่านี้ หาก block volume ถูกถอดออก ระบบจะไม่สามารถ boot ได้และจะเข้าสู่ emergency shell ข้อผิดพลาดที่ร้ายแรงที่สุดคือการรัน mkfs.ext4 บน volume ที่มีข้อมูลอยู่แล้ว เพราะจะทำให้ข้อมูลทั้งหมดถูกลบ ให้ทำการ format เฉพาะ volume ใหม่เท่านั้น หาก disk มีข้อมูล library อยู่แล้ว ให้ข้ามไปทำขั้นตอนการเพิ่มบรรทัดใน fstab ได้ทันที
จัดระเบียบไฟล์สื่อตามรูปแบบที่ Jellyfin กำหนด
Jellyfin ใช้ชื่อโฟลเดอร์และชื่อไฟล์ในการจับคู่ metadata หากจัดวางโครงสร้างผิดพลาด ภาพยนตร์อาจแสดงผลเป็นไฟล์ที่ไม่มีชื่อและไม่มีโปสเตอร์ หรือตอนของซีรีส์อาจไม่ตรงกับชื่อเรื่องที่ถูกต้อง กฎมีเพียง 3 ข้อเท่านั้น คือ: ภาพยนตร์แต่ละเรื่องต้องอยู่ในโฟลเดอร์ Name (Year) ของตนเองและมีชื่อไฟล์ที่ตรงกัน; โฟลเดอร์ซีซันต้องใช้ชื่อ Season 01 ไม่ใช่ S01; ไฟล์ตอนต่างๆ ต้องใช้รูปแบบ S01E01; และตอนพิเศษ (specials) ต้องอยู่ในโฟลเดอร์ Season 00
/mnt/media
├── Movies
│ ├── Blade Runner (1982)
│ │ └── Blade Runner (1982).mkv
│ └── Arrival (2016)
│ └── Arrival (2016).mkv
└── Shows
└── Severance (2022)
├── Season 01
│ ├── Severance - S01E01.mkv
│ └── Severance - S01E02.mkv
└── Season 00
└── Severance - The Lexington Letter.mkvการใช้ (Year) สำหรับภาพยนตร์ไม่ใช่เพื่อความสวยงาม แต่เพื่อแยกแยะภาพยนตร์ฉบับรีเมค เพื่อให้ระบบดึงชื่อเรื่องที่ถูกต้องมาแสดงผล โปรดแยก Movies และ Shows ไว้ในโฟลเดอร์ระดับบนสุด (top-level folders) แยกจากกัน เนื่องจากแต่ละโฟลเดอร์จะกลายเป็น library ของ Jellyfin ตามประเภทเนื้อหา การผสมเนื้อหาทั้งสองประเภทเข้าด้วยกันจะทำให้ระบบดึงข้อมูล metadata ผิดพลาด
Permissions: สาเหตุหลักที่ทำให้ library ไม่แสดงข้อมูล
นี่คือความเข้าใจผิดที่ทำให้เสียเวลาไปทั้งคืน Image jellyfin/jellyfin อย่างเป็นทางการ ไม่ รองรับ environment variables แบบ PUID/PGID เนื่องจากตัวแปรเหล่านั้นเป็นของ image จาก LinuxServer.io (lscr.io/linuxserver/jellyfin) สำหรับ image อย่างเป็นทางการ คุณต้องควบคุม user ด้วย key user: ใน compose หากไม่ได้ระบุไว้ container จะทำงานด้วยสิทธิ์ root ไม่ว่าคุณจะใช้แบบใด กฎยังคงเหมือนเดิมคือ: uid/gid ที่ container ใช้ทำงานต้องสามารถอ่านและเข้าถึง (traverse) ทุก directory ของ media ได้
เราจะรันด้วย uid/gid 1000 ซึ่งเป็น user แรกที่ไม่ใช่ root บน Ubuntu มาตรฐาน โปรดตรวจสอบค่าของคุณและตั้งค่า ownership:
id # confirm your user is uid=1000 gid=1000
sudo chown -R 1000:1000 /mnt/media
sudo find /mnt/media -type d -exec chmod 755 {} \;
sudo find /mnt/media -type f -exec chmod 644 {} \;
mkdir -p ~/jellyfin/config ~/jellyfin/cache
sudo chown -R 1000:1000 ~/jellyfinDirectory จำเป็นต้องมี execute bit (คือ x ใน 755) ไม่ใช่แค่สิทธิ์อ่านอย่างเดียว หากไม่มีสิทธิ์นี้ container จะไม่สามารถเข้าสู่ folder ได้แม้ว่าจะสามารถแสดงรายชื่อไฟล์ได้ก็ตาม ข้อผิดพลาดที่ทำให้ library ว่างเปล่าทั้งชุดมักเกิดจาก directory ลำดับชั้นบน (parent): หาก uid ของ container ไม่สามารถ traverse ตัว mount ได้ จะทำให้ไม่สามารถเข้าถึง /media/Movies หรือ /media/Shows ได้ และ library ทุกตัวจะว่างเปล่าทันทีพร้อมข้อความ Access to the path ... is denied ใน log หากมี media folder ใดที่อ่านไม่ได้ ระบบจะบันทึก log และข้ามโฟลเดอร์นั้นไป ดังนั้นไฟล์จำนวนมากที่ถูก copy เข้ามาด้วยสิทธิ์ root จะหายไปจาก library โดยไม่ทราบสาเหตุ นี่คือเหตุผลที่เราต้องใช้ chown แบบ recursive และตั้งค่า execute bit ให้กับทุก directory แทนที่จะแก้ไขเพียงโฟลเดอร์เดียว
The docker-compose file
services:
jellyfin:
image: jellyfin/jellyfin:10
container_name: jellyfin
user: "1000:1000"
restart: unless-stopped
ports:
- "127.0.0.1:8096:8096"
volumes:
- ./config:/config
- ./cache:/cache
- /mnt/media:/media:ro
environment:
- JELLYFIN_PublishedServerUrl=https://jellyfin.example.comคำอธิบายทีละบรรทัด: user: "1000:1000" ใช้สำหรับกำหนดสิทธิ์การเข้าถึงไฟล์ (file permissions) ให้ตรงกับเจ้าของไฟล์ (ownership) ที่ระบุไว้ด้านบน /config คือส่วนที่เก็บข้อมูลทั้งหมดของ server ทั้งบัญชีผู้ใช้, libraries, metadata และสถานะการเฝ้าดู (watch state) ดังนั้นส่วนนี้จึงต้องตั้งค่าให้เขียนข้อมูลได้ (writable) และเป็นส่วนที่คุณต้องทำการสำรองข้อมูล (backup) /cache คือพื้นที่ทำงานชั่วคราว (throwaway working space) ส่วนการ mount media ถูกตั้งค่าเป็น :ro (read-only) โดยเจตนา เนื่องจาก Jellyfin จะเก็บ artwork และ metadata ไว้ที่ /config เป็นค่าเริ่มต้น จึงไม่จำเป็นต้องเขียนข้อมูลลงใน library ของคุณ การตั้งค่าแบบ read-only ช่วยป้องกันไฟล์จากการถูกลบโดยไม่ตั้งใจหรือเกิดจาก plugin ที่ทำงานผิดพลาด การกำหนด port ผูกไว้กับ 127.0.0.1 อย่างตั้งใจ เนื่องจากระบบ login ผ่านเว็บของ Jellyfin ใช้โปรโตคอล HTTP ธรรมดา เราจึงไม่เปิด port 8096 ออกสู่ internet สาธารณะ JELLYFIN_PublishedServerUrl คือที่อยู่ที่ server ใช้ประกาศสำหรับการค้นหาอัตโนมัติภายในเครือข่าย (local autodiscovery) โดยใช้การ broadcast ผ่าน UDP ใน LAN เพื่อไม่ให้ client จาก internet มองเห็น และให้ใช้ URL ที่คุณพิมพ์ลงในแอปพลิเคชันแทน ให้ตั้งค่าเป็นที่อยู่ที่ต้องการแจ้งแก่ client และคุณจะต้องพิมพ์ URL นั้นด้วยตนเองบนอุปกรณ์ที่เชื่อมต่อจากระยะไกล
เริ่มการทำงานจาก directory ของ compose:
docker compose up -d
docker logs -f jellyfinการใช้งานครั้งแรก: ตัวช่วยตั้งค่าและไลบรารีของคุณ
เนื่องจากพอร์ตถูกผูกไว้กับ localhost ให้เข้าถึงตัวช่วยตั้งค่าผ่าน SSH tunnel จากแล็ปท็อป แทนการเปิดช่องทางใน firewall:
ssh -L 8096:127.0.0.1:8096 you@your-vps-ipจากนั้นให้เปิดเบราว์เซอร์ไปที่ http://localhost:8096 ตัวช่วยตั้งค่าจะนำทางคุณในการเลือกภาษา และการสร้าง admin user พร้อมรหัสผ่านที่คาดเดายาก บัญชีนี้คือบัญชีหลักของเซิร์ฟเวอร์ ดังนั้นห้ามใช้รหัสผ่านชั่วคราว ให้เพิ่มไลบรารีแรกของคุณโดยเลือก content type เป็น Movies และระบุเส้นทางไปที่ /media/Movies (ต้องเป็น path ภายใน container ไม่ใช่ path ของ host) จากนั้นทำซ้ำขั้นตอนเดิมสำหรับ Shows ที่ /media/Shows เมื่อเสร็จสิ้น Jellyfin จะเริ่มทำการ scan หากไลบรารีมีขนาดเล็ก ข้อมูล poster และชื่อเรื่องควรปรากฏขึ้นภายใน 1 ถึง 2 นาที คุณสามารถเพิ่มหรือแก้ไขไลบรารีได้ภายหลังที่ Dashboard → Libraries และสามารถสั่งให้สแกนใหม่ได้ด้วยคำสั่ง Scan All Libraries
หากคุณมีการใช้งาน transcoding ให้เปิดไปที่ Dashboard → Playback → Transcoding และตั้งค่า transcode temp path เป็น /cache/transcodes เพื่อให้ไฟล์ชั่วคราวถูกเก็บไว้ใน cache volume แทนที่จะทำให้ /config เต็ม ให้ตั้งค่า hardware acceleration เป็น None เนื่องจากไม่มี GPU สำหรับช่วยประมวลผล
การเข้าถึงจากระยะไกล: TLS reverse proxy หรือใช้งานผ่าน VPN
คุณสามารถเข้าถึง Jellyfin จากภายนอกได้อย่างปลอดภัย 2 วิธี และมี 1 วิธีที่ไม่ปลอดภัยซึ่งควรหลีกเลี่ยง วิธีที่ไม่ปลอดภัยคือการเปิด port 8096 สู่อินเทอร์เน็ตโดยตรง เนื่องจากข้อมูลการเข้าสู่ระบบจะถูกส่งแบบ cleartext และ port จะถูกโจมตีแบบ brute-force ภายในไม่กี่ชั่วโมง
Option A — TLS reverse proxy ติดตั้ง Jellyfin ไว้บน subdomain ภายใต้ Traefik พร้อม automatic TLS สำหรับ Docker apps ของคุณ หรือภายใต้ nginx ที่ใช้ Let's Encrypt certificate ที่ออกโดย Certbot เนื่องจาก Jellyfin ใช้ WebSockets สำหรับการอัปเดตแบบ real-time ตัว proxy จึงต้องส่งต่อ upgrade headers โดย Traefik จะดำเนินการส่วนนี้ให้โดยอัตโนมัติ ส่วน nginx จำเป็นต้องระบุค่าเหล่านี้อย่างชัดเจน และต้องใช้ HTTP/1.1 ไปยัง upstream เพื่อให้การ upgrade ทำงานได้:
location / {
proxy_pass http://127.0.0.1:8096;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}ตั้งค่า JELLYFIN_PublishedServerUrl ให้เป็นที่อยู่ https:// เพื่อให้การทำ local autodiscovery ประกาศ URL ที่ถูกต้อง เนื่องจากแอปพลิเคชันที่เข้าถึงจากระยะไกลจะใช้ที่อยู่ที่คุณกำหนด และควรติดตั้ง fail2ban เพื่อชะลอการโจมตีแบบ brute-force สำหรับการเข้าสู่ระบบ เมื่อเซิร์ฟเวอร์ออนไลน์สู่สาธารณะแล้ว ให้ตั้งค่า Uptime Kuma ไปยัง URL ดังกล่าว เพื่อให้คุณทราบถึงปัญหา downtime ก่อนผู้ใช้งานคนอื่น
Option B — รักษาความเป็นส่วนตัวด้วย VPN ไม่ต้องเปิด port 8096 สู่สาธารณะ แต่ให้เข้าถึง Jellyfin ผ่านทาง WireGuard tunnel ที่เชื่อมต่อกับเครื่องเดียวกัน วิธีนี้เป็นทางเลือกที่ปลอดภัยและง่ายที่สุดสำหรับใช้งานภายในบ้าน เนื่องจากไม่ต้องใช้ certificate ไม่ต้องเปิดเผยสู่สาธารณะ และไม่มีช่องทางให้ถูกโจมตีแบบ brute-force ให้ทำการ bind container เข้ากับ tunnel address หรือ localhost และเชื่อมต่อผ่าน VPN ดูรายละเอียดการตั้งค่า tunnel ได้ที่ WireGuard VPN setup for a private VPS
การคำนวณขนาดพื้นที่จัดเก็บและสำรองข้อมูล
ควรคำนวณงบประมาณจากคุณภาพไฟล์ ไม่ใช่จำนวนไฟล์ ไฟล์ 1080p แบบบีบอัดมีขนาด 4-15 GB ต่อไฟล์; ไฟล์ 1080p remux มีขนาด 20-40 GB; ซีรีส์ 1080p หนึ่งซีซันมีขนาด 15-40 GB; ไฟล์ 4K ทุกประเภทมีขนาด 40-100 GB ต่อเรื่อง คลังข้อมูลที่มีภาพยนตร์ไม่กี่ร้อยเรื่องรวมกับรายการโทรทัศน์บางส่วน จำเป็นต้องใช้พื้นที่ขนาด 2-4 TB การจัดสรรพื้นที่เผื่อไว้ (over-provision) ตั้งแต่แรกมีราคาถูกกว่าการย้ายข้อมูลในภายหลัง
/config คือสถานะทั้งหมดของ server ดังนั้นนี่คือสิ่งเดียวที่คุณต้องสำรองข้อมูลไว้ ควรทำ snapshot หรือใช้คำสั่ง stop-and-tar แล้วเก็บสำเนาไว้ภายนอกเครื่อง:
docker compose down
sudo tar czf jellyfin-config-$(date +%F).tgz -C ~/jellyfin config
docker compose up -d/cache และโฟลเดอร์ transcode สามารถลบทิ้งได้ ข้อมูลสื่อใน /mnt/media ให้สำรองข้อมูลแยกต่างหาก หรือยอมรับความเสี่ยงในการดาวน์โหลดใหม่ (re-rippable) ซึ่งคนส่วนใหญ่เลือกวิธีหลังเนื่องจากขนาดไฟล์ การอัปเกรดคือ docker compose pull && docker compose up -d; แท็ก :10 ด้านบนจะยังคงอยู่ในเวอร์ชันหลัก 10.x ดังนั้นการขยับไปเวอร์ชันหลักถัดไปต้องอาศัยการแก้ไขแท็กโดยเจตนา โปรดอ่าน release notes ของ Jellyfin ก่อนดำเนินการ เนื่องจากมีการเปลี่ยนแปลงโครงสร้างฐานข้อมูล (schema migrations) เมื่อมีการเปลี่ยนเวอร์ชันหลัก
Failure modes, with the strings you will see
Library is empty after a scan. Log ที่ Dashboard → Logs (หรือ ~/jellyfin/config/log/log_*.log) แสดงข้อความ:
System.UnauthorizedAccessException: Access to the path '/media/Movies' is denied.uid ของ container ไม่สามารถอ่าน path ดังกล่าวได้ สาเหตุเกิดจาก media ถูกเป็นเจ้าของโดย root หรือ uid อื่นที่ไม่ใช่ค่า user: ของคุณ, directory ขาด execute bit หรือ mount point หลักไม่สามารถเข้าถึงได้ด้วย uid นั้น วิธีแก้ไข: chown -R 1000:1000 /mnt/media, directory 755, file 644 แล้วทำการ rescan
Playback pins the CPU and buffers. docker stats jellyfin แสดงค่า CPU ใกล้เคียง 100% คูณด้วยจำนวน core และ Dashboard → Playback ระบุ session เป็น Transcode โดยมีความเร็วต่ำกว่า 1.0x เนื่องจาก client ไม่ได้เล่นแบบ direct-play ทำให้ VPS ต้องทำ CPU-transcoding ช้ากว่าเวลาจริงและเกิดอาการ buffer สาเหตุเกิดจาก codec หรือ container ไม่รองรับ, การทำ subtitle burn-in หรือการทำ HDR tone-mapping วิธีแก้ไข: เปลี่ยนไปใช้ client ที่รองรับ direct-play, เก็บ source ไว้ในรูปแบบ H.264/AAC, ใช้ subtitle แบบ text (SRT) แทนแบบ image (PGS/VOBSUB) เพื่อหลีกเลี่ยงการ burn-in และหลีกเลี่ยงการเล่น 4K HDR บนเครื่องที่ใช้เฉพาะ CPU
"No compatible streams are available." ข้อความเต็มคือ "This client isn't compatible with the media and the server isn't sending a compatible media format." Client ปฏิเสธ source และการทำ fallback transcode ล้มเหลวเช่นกัน สาเหตุเกิดจากคำสั่ง ffmpeg ผิดพลาด, ไฟล์อ่านไม่ได้ หรือ profile ของผู้ใช้ไม่อนุญาตให้แปลงไฟล์วิดีโอ วิธีแก้ไข: ตรวจสอบบรรทัด ffmpeg ใน Dashboard → Logs, ตรวจสอบว่าไฟล์สามารถเล่นได้ปกติหรือไม่, ตรวจสอบสิทธิ์การเล่น (playback permissions) ของผู้ใช้หากต้องมีการ transcoding และทดสอบด้วย client อื่นเพื่อตัดปัญหาเรื่อง codec ของ browser
Films have no poster or the wrong one. Metadata ไม่ตรงกัน สาเหตุเกิดจากภาพยนตร์ไม่ได้อยู่ใน folder Name (Year) ของตัวเอง, folder season ใช้ชื่อ S01 แทนที่จะเป็น Season 01, episode ไม่ได้อยู่ในรูปแบบ S01E01 หรือไม่มีการระบุปี วิธีแก้ไข: เปลี่ยนชื่อให้เป็นไปตามรูปแบบด้านบน แล้วเลือก Refresh metadata → Replace all หรือใช้ Identify กับรายการนั้นๆ เพื่อเลือกข้อมูลจาก TMDB/TVDB ที่ถูกต้อง
FAQ
VPS สามารถ transcode วิดีโอโดยไม่ใช้ GPU ได้หรือไม่?
ทำได้ แต่ต้องใช้ CPU เท่านั้น ซึ่งมีค่าใช้จ่ายสูง การ transcode ไฟล์ 1080p ด้วย software เพียงไฟล์เดียวอาจทำให้ vCPU หลายตัวทำงานหนักจนเต็มขีดจำกัด ส่วนไฟล์ 4K หรือ HEVC มักไม่สามารถประมวลผลได้ทันเวลาจริง (real time) ทำให้เกิดการบัฟเฟอร์ขณะเล่น วิธีที่ดีที่สุดคือหลีกเลี่ยงการ transcode โดยเก็บไฟล์ในรูปแบบ H.264/AAC และใช้แอปพลิเคชันฝั่ง client ที่รองรับการ direct-play เพื่อให้ VPS ทำหน้าที่ส่งผ่านข้อมูล bytes เท่านั้น ควรเช่า instance ที่มี GPU เฉพาะเมื่อจำเป็นต้องทำ on-the-fly transcoding จริงๆ
ทำไมห้องสมุด Jellyfin ของฉันถึงว่างเปล่าหลังจากสั่ง scan?
สาเหตุเกือบทั้งหมดเกิดจากเรื่อง permissions หากใช้ image jellyfin/jellyfin อย่างเป็นทางการ ระบบจะทำงานด้วย user: ที่คุณตั้งค่าไว้ (หรือ root) หากไฟล์ไม่สามารถอ่านได้ด้วย uid ดังกล่าว log การ scan Access to the path ... is denied และข้ามไฟล์เหล่านั้นไป วิธีแก้ไขคือจัดการ ownership ด้วย chown -R 1000:1000 /mnt/media กำหนด execute bit ให้กับ directory (755) แล้วสั่ง rescan อีกครั้ง นอกจากนี้ควรตรวจสอบโฟลเดอร์หลักด้วย เพราะหาก uid ของ container ไม่สามารถเข้าถึง /mnt/media ได้ ระบบจะไม่สามารถเข้าถึงโฟลเดอร์ห้องสมุดได้และทำให้ทุกอย่างว่างเปล่า สาเหตุที่พบบ่อยเป็นอันดับสองคือโครงสร้างโฟลเดอร์ไม่ตรงตามที่ Jellyfin กำหนด
ฉันจะเข้าถึง Jellyfin จากระยะไกลอย่างปลอดภัยได้อย่างไร?
มี 2 วิธีที่แนะนำ วิธีแรกคือการใช้งานผ่าน TLS reverse proxy บน subdomain เพื่อเข้ารหัสข้อมูลการ login และการ stream พร้อมติดตั้ง fail2ban และห้ามเปิด port 8096 แบบ plain text เนื่องจากจะทำให้รหัสผ่านถูกส่งแบบ cleartext วิธีที่สองคือการใช้งานแบบส่วนตัวผ่าน VPN ซึ่งเป็นวิธีที่ปลอดภัยและง่ายที่สุดสำหรับใช้งานในบ้าน สำหรับแอปพลิเคชันต่างๆ ให้ระบุ public address โดยตรง เนื่องจากระบบ autodiscovery ใช้การ broadcast ภายในเครือข่ายท้องถิ่นเท่านั้น จึงไม่สามารถส่งข้อมูลไปยัง client ที่เชื่อมต่อผ่านอินเทอร์เน็ตได้
Jellyfin VPS ต้องใช้ disk และ bandwidth เท่าใด?
พื้นที่ disk ขึ้นอยู่กับคุณภาพไฟล์: เตรียมไว้ประมาณ 4-15 GB สำหรับไฟล์ 1080p ที่บีบอัดแล้ว, 20-40 GB สำหรับไฟล์ remux และ 40-100 GB สำหรับไฟล์ 4K ดังนั้นห้องสมุดส่วนใหญ่จึงต้องการ block volume ขนาด 2-4 TB ส่วน bandwidth ขึ้นอยู่กับ bitrate ของการ direct-play โดยอยู่ที่ประมาณ 8-12 Mbps ต่อหนึ่ง stream ของ 1080p และจะใช้มากกว่านี้มากสำหรับ 4K ดังนั้นควรตรวจสอบว่าความเร็วพอร์ตสามารถรองรับจำนวนผู้ใช้งานพร้อมกันได้ และควรระวังขีดจำกัดการรับส่งข้อมูลรายเดือน (monthly transfer cap) หากวางแผนจะ transcode ควรเลือก CPU ที่มีประสิทธิภาพสูง แต่หากเน้นการ direct-play ควรให้ความสำคัญกับ bandwidth มากกว่าจำนวน core
การรัน Jellyfin บน VPS ผิดกฎหมายหรือไม่?
Jellyfin เป็นซอฟต์แวร์ open-source ฟรี และการใช้งานนั้นไม่ผิดกฎหมาย สิ่งที่สำคัญคือเนื้อหา: ให้ stream เฉพาะสื่อที่คุณเป็นเจ้าของหรือได้รับอนุญาตให้ครอบครองเท่านั้น เช่น ไฟล์ที่ rip จากแผ่นของคุณเอง, ไฟล์บันทึก หรือไฟล์ที่คุณมีสิทธิ์ใช้งาน Jellyfin ไม่มีเนื้อหามาให้และไม่มีช่องทางในการจัดหาเนื้อหาใดๆ ระบบเป็นเพียงเครื่องเล่นสำหรับห้องสมุดที่คุณเป็นเจ้าของอยู่แล้วเท่านั้น