Como instalar Jellyfin no VPS com Docker
Aprenda a configurar Jellyfin via Docker em VPS. Evite erros de permissão de volume e problemas de CPU transcoding ao usar block-storage para sua mídia.
O que você está construindo
Um servidor de mídia Jellyfin em um VPS: um container, três volumes e um disco de block-storage contendo seus filmes e séries, acessível por qualquer navegador ou app Jellyfin. A instalação consiste em um arquivo compose de quinze linhas. Qualquer erro posterior ocorre por dois motivos — permissões de arquivo que o container não consegue ler, ou solicitar que um VPS sem GPU realize o transcoding de vídeos para os quais não possui recursos. Este guia foca principalmente nesses dois pontos, pois é onde surgem a maioria dos chamados de suporte.
O Jellyfin é gratuito e totalmente open source, sem necessidade de conta, sem recursos pagos e sem telemetria — o motivo de ele estar em quase todas as listas de coisas que valem a pena hospedar você mesmo em 2026. Ele reproduz mídias que você possui. Ele não fornece conteúdo, e este guia não trata de como adquirir nenhum.
A realidade do transcoding, antes de você alugar qualquer coisa
Leia isto primeiro, pois isso altera o que você deve comprar. Um servidor de mídia realiza uma de duas ações ao pressionar play. Direct play transmite o arquivo como ele é: o VPS lê os bytes do disco e os envia pela rede, consumindo quase nada de CPU. Transcoding re-codifica o vídeo em tempo real — nova resolução, novo codec ou legendas integradas — e isso é puro processamento de CPU.
Um VPS típico não possui GPU. Portanto, cada transcode roda na CPU com libx264/libx265, e a codificação via software é cara. Um único transcode 1080p H.264 pode saturar vários vCPUs compartilhados; um transcode 4K ou HEVC geralmente não consegue acompanhar o tempo real, fazendo com que a reprodução trave e fique em buffer constantemente. Transcoding via hardware — o que torna esse processo barato em um servidor doméstico com um Intel iGPU ou uma placa Nvidia — simplesmente não está disponível para você, a menos que seu provedor alugue instâncias com GPU.
A estratégia completa em um VPS é, portanto: evite o transcoding. Mantenha sua biblioteca em codecs que seus clientes reproduzam nativamente — vídeo H.264, áudio AAC ou AC3, em um container MP4 ou MKV — e escolha aplicativos de cliente que suportem direct-play: os apps nativos do Jellyfin para Android TV, iOS e Roku, além do Infuse, Kodi e o Jellyfin Media Player para desktop. Fazendo isso, o VPS nunca executará o ffmpeg, e uma máquina modesta de 2 vCPU transmite para várias pessoas simultaneamente. Se você planejar fazer transcoding, precisará de uma máquina muito maior e mais cara, e mesmo assim, 4K é uma escolha arriscada.
Calcule também a largura de banda, pois esta é a outra surpresa. O direct play envia o arquivo com seu próprio bitrate. Um arquivo 1080p comprimido opera entre 8-12 Mbps; um remux Blu-ray 1080p opera entre 20-30 Mbps; 4K HDR opera entre 40-80 Mbps. Três pessoas em direct-play com arquivos de 10 Mbps representam 30 Mbps de upload contínuo saindo do seu VPS. Verifique dois números em seu plano: a velocidade da porta (ela suporta 30 Mbps de upstream?) e o limite de transferência mensal. Um filme de duas horas a 10 Mbps consome cerca de 9 GB de saída; portanto, uma cota de 1 TB/mês permite pouco mais de cem filmes desse tipo por mês — três ou quatro por dia — e uma residência assistindo 4K, com quatro a oito vezes o bitrate, esgotará o limite muito mais rápido.
Pré-requisitos
- Um VPS Ubuntu 24.04 KVM recém-instalado com acesso root ou sudo, com Docker e o plugin Compose instalados.
- Um volume de block-storage para a mídia, dimensionado para sua biblioteca (veja o dimensionamento abaixo). O disco root pequeno que acompanha o VPS não é o local para seus filmes.
- Um nome de domínio se desejar acesso HTTPS público, ou um WireGuard VPN no mesmo VPS se preferir manter tudo privado.
- Mídia que você tem direito legal de transmitir — seus próprios rips, suas próprias gravações, arquivos de sua propriedade.
Monte o storage de bloco primeiro
Anexe o volume no painel do seu provedor, localize-o e monte-o. Obtenha o nome do dispositivo em lsblk — será algo como /dev/sdb ou /dev/vdb, nunca o disco raiz.
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 deviceMonte-o via UUID, não via /dev/sdb, pois as letras dos dispositivos mudam após o reboot e você pode acabar formatando ou montando o disco errado. Adicione uma linha ao /etc/fstab:
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /mnt/media ext4 defaults,nofail 0 2sudo mount -a
df -h /mnt/medianofail é importante: sem isso, se o volume de bloco for desanexado, o sistema não iniciará e cairá em um shell de emergência. O erro mais comum aqui é rodar mkfs.ext4 em um volume que já contém dados — isso apaga tudo. Formate apenas volumes novos; se o disco já possui sua biblioteca, pule direto para a linha no fstab.
Organize a mídia conforme o Jellyfin espera
O Jellyfin associa metadados através de nomes de pastas e arquivos. Se a estrutura estiver incorreta, filmes aparecerão sem título ou sem pôster, ou um episódio será associado à série errada. Existem exatamente três regras: cada filme deve estar em sua própria pasta Name (Year) com um nome de arquivo correspondente; pastas de temporadas devem ser nomeadas como Season 01, não S01; arquivos de episódios devem usar S01E01; e conteúdos especiais devem ficar em 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.mkvO (Year) em filmes não é apenas estética — ele diferencia remakes para que o buscador encontre o título correto. Mantenha Movies e Shows como pastas de nível superior separadas, pois cada uma se torna uma biblioteca Jellyfin de um tipo de conteúdo específico; misturá-las confunde o provedor de metadados.
Permissões: o principal motivo para bibliotecas vazias
Este é o erro conceitual que faz usuários perderem horas de trabalho. A imagem oficial do jellyfin/jellyfin não utiliza as variáveis de ambiente PUID/PGID — estas pertencem à imagem da LinuxServer.io (lscr.io/linuxserver/jellyfin). Na imagem oficial, você controla o usuário através da chave user: no compose; se você a omitir, o container rodará como root. Independentemente da imagem, a regra é a mesma: o uid/gid que o container utiliza deve ter permissão de leitura e execução em todos os diretórios de mídia.
Rodaremos com uid/gid 1000, o primeiro usuário não-root em uma instalação padrão do Ubuntu. Confirme o seu e ajuste o 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 ~/jellyfinDiretórios precisam do bit de execução (o x em 755), não apenas de leitura — sem isso, o container não consegue acessar a pasta, mesmo que consiga listar os nomes dos arquivos. O erro que esvazia uma biblioteca inteira ocorre no diretório pai: se o uid do container não conseguir atravessar o ponto de montagem, ele nunca alcançará /media/Movies ou /media/Shows, e todas as bibliotecas aparecerão vazias com o erro Access to the path ... is denied no log. Qualquer pasta de mídia que o container não consiga ler será registrada no log e ignorada; assim, um lote de arquivos copiados como root desaparece silenciosamente da biblioteca. É por isso que aplicamos o chown recursivamente e definimos o bit de execução em todos os diretórios, em vez de corrigir apenas uma pasta.
O arquivo docker-compose
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.comLinha por linha: user: "1000:1000" define as permissões de arquivo, correspondendo ao ownership acima. /config contém todo o servidor — contas, bibliotecas, metadados e estado de monitoramento — portanto deve ser gravável e é o item que você faz backup. /cache é o espaço de trabalho temporário. O mount de mídia é :ro (read-only) propositalmente: o Jellyfin armazena artwork e metadados em /config por padrão, então ele nunca precisa gravar na sua biblioteca; o modo read-only protege seus arquivos de exclusões acidentais ou plugins maliciosos. A porta é vinculada a 127.0.0.1 deliberadamente — o login web do Jellyfin é HTTP puro, então nunca expomos a porta 8096 para a internet pública. JELLYFIN_PublishedServerUrl é o endereço que o servidor anuncia para autodescoberta local — um broadcast UDP via LAN, portanto clientes na internet não o veem e utilizam apenas a URL digitada no app. Configure como o endereço que deve ser informado aos clientes e prepare-se para digitar essa URL manualmente em dispositivos remotos.
Inicie o serviço a partir do diretório compose:
docker compose up -d
docker logs -f jellyfinPrimeira execução: o assistente de configuração e suas bibliotecas
Como a porta está vinculada ao localhost, acesse o assistente através de um túnel SSH do seu laptop em vez de abrir uma regra no firewall:
ssh -L 8096:127.0.0.1:8096 you@your-vps-ipAgora acesse http://localhost:8096. O assistente guiará você pela escolha do idioma e depois pela criação de um usuário admin com uma senha forte — esta conta é o seu servidor, portanto não use uma senha temporária. Adicione sua primeira biblioteca: escolha o tipo de conteúdo Movies, aponte para /media/Movies (o caminho dentro do container, não o caminho do host) e repita o processo para Shows em /media/Shows. Finalize e o Jellyfin iniciará o scan. Um resultado correto é o preenchimento de pôsteres e títulos em um ou dois minutos para bibliotecas pequenas. Adicione ou edite bibliotecas posteriormente em Dashboard → Libraries, e force uma nova varredura com Scan All Libraries.
Se você utilizar qualquer tipo de transcoding, abra Dashboard → Playback → Transcoding e defina o caminho temporário de transcodificação para /cache/transcodes, para que o uso de disco ocorra no volume de cache em vez de inflar o /config. Deixe a aceleração de hardware configurada como None — não há GPU disponível para aceleração.
Acesso remoto: proxy reverso TLS ou manter na VPN
Você tem duas formas seguras de acessar o Jellyfin externamente e uma forma insegura para evitar. A forma insegura é publicar a porta 8096 diretamente na internet: o login viaja em texto puro e a porta sofre ataques de brute-force em poucas horas.
Opção A — proxy reverso TLS. Coloque o Jellyfin em um subdomínio atrás do Traefik com TLS automático para seus apps Docker, ou atrás do nginx com um certificado Let's Encrypt emitido pelo Certbot. O Jellyfin usa WebSockets para atualizações em tempo real, portanto o proxy deve encaminhar os headers de upgrade. O Traefik faz isso automaticamente; o nginx precisa que eles sejam declarados explicitamente e exige HTTP/1.1 para o upstream para que o upgrade funcione:
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";
}Configure o JELLYFIN_PublishedServerUrl com o endereço https:// para que qualquer autodescoberta local anuncie a URL correta — apps remotos usam o endereço fornecido a eles — e adicione o fail2ban para mitigar tentativas de brute-force no login. Assim que o servidor estiver público, aponte o Uptime Kuma para a URL para ser notificado sobre indisponibilidades antes dos seus usuários.
Opção B — manter privado via VPN. Não publique a porta 8096 de forma alguma; acesse o Jellyfin apenas através de um túnel WireGuard terminando na mesma máquina. Para uma residência, esta é a escolha segura mais simples — sem certificados, sem exposição pública e sem superfície de ataque para brute-force. Vincule o container ao endereço do túnel ou ao localhost e conecte-se via VPN. Veja a configuração de VPN WireGuard para um VPS privado para o túnel em si.
Dimensionamento de armazenamento e backups
Calcule o orçamento pela qualidade, não pela contagem de arquivos. Filmes 1080p comprimidos ocupam de 4 a 15 GB cada; um remux 1080p ocupa de 20 a 40 GB; uma temporada de TV 1080p ocupa de 15 a 40 GB; qualquer conteúdo 4K ocupa de 40 a 100 GB por filme. Uma biblioteca com algumas centenas de filmes e algumas séries exige um volume de 2 a 4 TB; é mais barato provisionar o volume de blocos com folga de uma vez do que realizar uma migração posteriormente.
/config é o estado completo do servidor, portanto é o único item que você deve fazer backup. Use snapshot ou execute o comando stop-and-tar e mantenha a cópia fora da máquina:
docker compose down
sudo tar czf jellyfin-config-$(date +%F).tgz -C ~/jellyfin config
docker compose up -d/cache e a pasta de transcode são descartáveis. A mídia em /mnt/media deve ser feita em backup separadamente ou aceita como re-rippable — a maioria das pessoas opta pela segunda opção devido ao tamanho. Upgrades são docker compose pull && docker compose up -d; a tag :10 acima permanece dentro da versão major 10.x, portanto mudar para a próxima major exige uma edição deliberada da tag — leia as notas de lançamento do Jellyfin antes de realizar a mudança, pois migrações de schema de biblioteca ocorrem em versões major.
Modos de falha e as strings que você verá
A biblioteca está vazia após o scan. O log em Dashboard → Logs (ou ~/jellyfin/config/log/log_*.log) mostra:
System.UnauthorizedAccessException: Access to the path '/media/Movies' is denied.O uid do container não consegue ler esse caminho. Causa: mídia pertencente ao root ou a um uid diferente do seu valor user:, um diretório sem o bit de execução, ou o próprio mount pai não é acessível por esse uid. Solução: chown -R 1000:1000 /mnt/media, diretórios 755, arquivos 644, e então realize o rescan.
Playback trava a CPU e o buffer. docker stats jellyfin mostra a CPU próxima de 100% vezes a contagem de núcleos, e Dashboard → Playback lista a sessão como Transcode com velocidade abaixo de 1.0x. O cliente não está em direct-play, então o VPS está fazendo CPU-transcoding mais devagar que o tempo real e perdendo frames. Causa: um codec ou container não suportado, burn-in de legendas ou HDR tone-mapping. Solução: mude para um cliente com direct-play, mantenha as fontes em H.264/AAC, use legendas de texto (SRT) em vez de legendas de imagem (PGS/VOBSUB) que forçam o burn-in, e evite conteúdo 4K HDR em máquinas que dependem apenas de CPU.
"No compatible streams are available." A mensagem completa geralmente é "This client isn't compatible with the media and the server isn't sending a compatible media format." O cliente rejeitou a fonte e o fallback transcode também falhou ao iniciar. Causa: um comando ffmpeg corrompido, um arquivo ilegível ou o perfil do usuário bloqueando a conversão de vídeo. Solução: leia a linha do ffmpeg em Dashboard → Logs, confirme se o arquivo reproduz, verifique as permissões de playback do usuário caso dependa de transcoding, e tente um segundo cliente para descartar problemas de codec do navegador.
Filmes sem poster ou com o poster errado. Os metadados não coincidiram. Causa: um filme fora de sua própria pasta Name (Year), uma pasta de temporada nomeada como S01 em vez de Season 01, episódios não formatados como S01E01, ou falta o ano. Solução: renomeie para o layout acima, então use Refresh metadata → Replace all, ou use Identify em um único item para fixar a entrada correta do TMDB/TVDB.
FAQ
Uma VPS pode transcodificar vídeo sem GPU?
Sim, mas apenas via CPU, e o custo é alto. Uma única transcodificação de software em 1080p pode saturar vários vCPUs. Transcodificações em 4K ou HEVC geralmente não conseguem manter o tempo real, causando buffer no playback. A melhor estratégia é evitar a transcodificação: mantenha sua biblioteca em H.264/AAC e use aplicativos clientes que suportem direct-play, para que a VPS apenas transmita os bytes. Alugue uma instância com GPU apenas se você realmente precisar de transcodificação on-the-fly.
Por que minha biblioteca Jellyfin está vazia após o scan?
Quase sempre é um problema de permissões. A imagem oficial do jellyfin/jellyfin roda com o user: que você definiu (ou como root). Se os arquivos não forem legíveis por esse uid, o scan gera logs no Access to the path ... is denied e os pula. Corrija o ownership com chown -R 1000:1000 /mnt/media, aplique o bit de execução nos diretórios (755) e faça um novo scan. Verifique também o diretório pai; se o uid do container não conseguir atravessar o /mnt/media, ele nunca alcançará as pastas da biblioteca e tudo aparecerá vazio. A segunda causa mais comum é uma estrutura de pastas que não segue o padrão esperado pelo Jellyfin.
Como acesso o Jellyfin remotamente e de forma segura?
Existem duas boas opções. Use um reverse proxy TLS em um subdomínio para criptografar o login e o stream, e instale o fail2ban — nunca exponha a porta 8096 pura, pois ela envia sua senha em texto claro. Ou mantenha o acesso totalmente privado via VPN, que é a escolha segura mais simples para uso doméstico. Forneça o endereço público diretamente aos aplicativos — o autodiscovery utiliza broadcast de rede local e não funciona para clientes via internet.
Quanto de disco e largura de banda uma VPS Jellyfin precisa?
O disco depende da qualidade: reserve de 4-15 GB para filmes 1080p compactados, 20-40 GB para remux e 40-100 GB para 4K. Portanto, a maioria das bibliotecas exige um volume de bloco de 2-4 TB. A largura de banda é definida pelo bitrate do direct-play — 8-12 Mbps por stream 1080p, sendo muito mais para 4K. Confirme se a velocidade da sua porta suporta o número de espectadores simultâneos e monitore o limite de transferência mensal. Adicione folga de CPU se planeja transcodificar; priorize largura de banda em vez de cores se o foco for direct-play.
É legal rodar Jellyfin em uma VPS?
O Jellyfin é um software livre e de código aberto, e rodá-lo é totalmente legal. O que importa é o conteúdo: transmita apenas mídia que você possui ou tenha licença para manter — seus próprios rips de disco, gravações ou arquivos que você tem direito de uso. O Jellyfin não fornece mídia nem meios para obtê-la; ele é apenas um player para uma biblioteca que você já possui.