Jellyfin na VPS: transmita sua mídia com Docker
Configure o Jellyfin em Docker numa VPS com armazenamento em blocos, permissões corretas, reprodução direta e acesso remoto seguro, evitando transcodificação sem GPU.
O que você vai criar
Um servidor de multimédia Jellyfin numa VPS: um contentor, três volumes e um disco de armazenamento em blocos com os seus filmes e séries, acessível a partir de qualquer navegador ou aplicação Jellyfin. A instalação é feita com um ficheiro compose de quinze linhas. Tudo o que falha depois disso resulta de dois problemas: permissões de ficheiros que impedem o contentor de os ler e pedidos de transcodificação de vídeo a uma VPS sem GPU, que não tem capacidade para os processar. Este guia dedica a maior parte do conteúdo a esses dois problemas, porque é aí que surgem os pedidos de suporte.
O Jellyfin é gratuito e totalmente open source, sem conta, funcionalidades pagas ou telemetria. Por isso, aparece em quase todas as listas de coisas que vale a pena alojar por conta própria em 2026. Reproduz os conteúdos multimédia que possui. Não inclui conteúdo, e este guia não aborda a obtenção de conteúdos.
A realidade da transcodificação, antes de contratar qualquer serviço
Leia isto primeiro, porque muda o que deve contratar. Um servidor multimédia faz uma de duas coisas quando prime o botão de reprodução. A reprodução direta transmite o ficheiro tal como está: o VPS lê os bytes do disco e envia-os pela rede, consumindo quase nenhum CPU. A transcodificação volta a codificar o vídeo em tempo real, seja para alterar a resolução, o codec ou incluir legendas queimadas no vídeo. Isto consome essencialmente CPU.
Um VPS normal não tem GPU. Por isso, todas as transcodificações são executadas no CPU com libx264/libx265, e a codificação por software é dispendiosa. Uma única transcodificação de 1080p para H.264 pode saturar vários vCPUs partilhados. Uma transcodificação de 4K ou HEVC normalmente não consegue acompanhar o tempo real, pelo que a reprodução para e fica continuamente em buffer. A transcodificação por hardware, que torna este processo barato num computador doméstico com uma iGPU Intel ou uma placa Nvidia, simplesmente não está disponível, a menos que o fornecedor disponibilize instâncias com GPU.
Por isso, toda a estratégia num VPS é evitar a transcodificação. Mantenha a biblioteca em codecs que os clientes reproduzam nativamente: vídeo H.264, áudio AAC ou AC3, num contentor MP4 ou MKV. Escolha aplicações cliente que suportem reprodução direta: as aplicações nativas do Jellyfin para Android TV, iOS e Roku, além do Infuse, Kodi e Jellyfin Media Player para desktop. Assim, o VPS nunca precisa de executar ffmpeg, e uma máquina modesta com 2 vCPU consegue transmitir conteúdo para várias pessoas em simultâneo. Se planear transcodificar, precisará de uma máquina muito maior e mais cara. Mesmo nesse caso, 4K continua a ser uma má escolha.
Faça também as contas de largura de banda, porque esse é o outro fator surpreendente. A reprodução direta envia o ficheiro à própria taxa de bits. Um ficheiro 1080p comprimido utiliza 8-12 Mbps; um remux de Blu-ray 1080p, 20-30 Mbps; e conteúdo 4K HDR, 40-80 Mbps. Três pessoas a reproduzir diretamente ficheiros a 10 Mbps geram 30 Mbps de upload contínuo no seu VPS. Verifique dois valores no seu plano: a velocidade da porta (consegue enviar 30 Mbps?) e o limite mensal de transferência. Um filme de duas horas a 10 Mbps corresponde a cerca de 9 GB de saída. Assim, uma franquia medida de 1 TB/mês permite pouco mais de cem filmes desse tipo por mês, ou três ou quatro por dia. Uma casa a ver conteúdo 4K, com uma taxa de bits quatro a oito vezes superior, esgota essa franquia muito mais depressa. Inclua no mesmo orçamento todo o tráfego de saída gerado pelo mesmo servidor, incluindo um relay RustDesk autoalojado, que transporta uma sessão completa de ambiente de trabalho remoto sempre que dois pares não conseguem estabelecer uma ligação direta.
Pré-requisitos
- Um VPS KVM novo com Ubuntu 24.04, acesso root ou sudo, e Docker com o plugin Compose instalado.
- Um volume de armazenamento em bloco para os ficheiros multimédia, com capacidade adequada ao tamanho da sua biblioteca (consulte a secção sobre dimensionamento abaixo). O pequeno disco raiz incluído num VPS não é o local adequado para guardar os seus filmes.
- Um nome de domínio se quiser acesso HTTPS público, ou uma VPN WireGuard no mesmo VPS se preferir manter todo o serviço privado.
- Conteúdo multimédia que tenha autorização legal para transmitir, como cópias feitas por si, gravações suas ou ficheiros de que é proprietário.
Monte primeiro o armazenamento em bloco
Associe o volume no painel do seu provedor. Em seguida, localize-o e monte-o. Obtenha o nome do dispositivo com 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 pelo UUID, não por /dev/sdb, porque as letras dos dispositivos podem mudar entre reboots. Caso contrário, pode acabar por formatar ou montar o disco errado. Adicione uma linha a /etc/fstab:
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /mnt/media ext4 defaults,nofail 0 2sudo mount -a
df -h /mnt/medianofail é importante. Sem essa opção, se o volume em bloco for desassociado, o servidor não arranca e entra numa shell de emergência. O maior erro aqui é executar mkfs.ext4 num volume que já contém dados, porque isso apaga o conteúdo. Formate apenas volumes novos. Se o disco já tiver a sua biblioteca, avance diretamente para a linha do fstab.
Organize as mídias da forma esperada pelo Jellyfin
O Jellyfin associa metadados com base nos nomes das pastas e dos ficheiros. Se a estrutura estiver errada, os filmes aparecem como ficheiros sem título e sem poster, ou um episódio é associado à série errada. Existem exatamente três regras: cada filme fica na sua própria pasta Name (Year), com um nome de ficheiro correspondente; as pastas de temporada usam o nome Season 01, não S01; os ficheiros de episódios usam S01E01; e os especiais ficam 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) nos filmes não é decorativo. Ele diferencia remakes para que o sistema de correspondência selecione o título correto. Mantenha Movies e Shows como pastas de nível superior separadas, porque cada uma se torna uma biblioteca Jellyfin de um tipo de conteúdo específico. Misturá-las confunde o fornecedor de metadados. O Jellyfin também indexa uma terceira pasta de fotografias, mas a experiência é limitada em comparação com a de um servidor de fotografias dedicado. Se os seus álbuns forem importantes, coloque-os no seu próprio servidor, com PhotoPrism ou Immich, e deixe este servidor para filmes e televisão.
Permissões: a principal causa de bibliotecas vazias
Este é o equívoco que faz perder uma noite. A imagem oficial jellyfin/jellyfin não respeita as variáveis de ambiente PUID/PGID. Essas variáveis pertencem à imagem LinuxServer.io (lscr.io/linuxserver/jellyfin). Na imagem oficial, o utilizador é definido com a chave user: no compose. Se a chave for omitida, o contentor é executado como root. Em qualquer dos casos, a regra é a mesma: o uid/gid com que o contentor é executado tem de conseguir ler e atravessar todos os diretórios de multimédia.
Vamos executar o serviço com o uid/gid 1000, o primeiro utilizador não root numa instalação padrão do Ubuntu. Confirme o seu e defina o proprietário:
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 ~/jellyfinOs diretórios precisam do bit execute (o x em 755), e não apenas de permissão de leitura. Sem esse bit, o contentor não consegue entrar na pasta, mesmo que consiga listar o nome. O problema que esvazia uma biblioteca inteira está no diretório pai: se o uid do contentor não conseguir atravessar o próprio ponto de montagem, nunca chega a /media/Movies ou /media/Shows. Nesse caso, todas as bibliotecas ficam vazias de uma só vez e Access to the path ... is denied aparece no log. Qualquer pasta de multimédia que não consiga ler é registada e ignorada. Por isso, um conjunto de ficheiros copiados como root desaparece silenciosamente da biblioteca. É por isso que alteramos o proprietário recursivamente e definimos o bit execute em todos os diretórios, em vez de corrigir apenas uma pasta.
O ficheiro 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 a linha: user: "1000:1000" é o que define efetivamente as permissões dos ficheiros, em conjunto com o proprietário indicado acima. /config contém todo o servidor, incluindo contas, bibliotecas, metadados e o estado de monitorização, por isso tem de permitir escrita e é o que deve incluir nas cópias de segurança. /cache é espaço de trabalho temporário. A montagem de media é :ro, apenas para leitura, de forma deliberada: o Jellyfin armazena por predefinição as capas e os metadados em /config, por isso nunca precisa de escrever na sua biblioteca. O modo apenas para leitura também protege os seus ficheiros contra uma eliminação acidental ou um plugin com problemas. A porta está deliberadamente associada a 127.0.0.1. O acesso web do Jellyfin usa HTTP simples, por isso nunca publicamos a porta 8096 na Internet pública. JELLYFIN_PublishedServerUrl é o endereço que o servidor anuncia para a descoberta automática local, através de uma difusão UDP na LAN. Assim, os clientes na Internet nunca o veem e usam simplesmente o URL introduzido na aplicação. Defina-o como o endereço que deve ser comunicado aos clientes e conte com a possibilidade de ter de introduzir esse URL manualmente nos dispositivos remotos.
Inicie-o a partir do diretório do compose:
docker compose up -d
docker logs -f jellyfinPrimeira execução: o assistente de configuração e as suas bibliotecas
Como a porta está associada a localhost, aceda ao assistente através de um túnel SSH a partir do seu portátil, em vez de abrir uma porta na firewall:
ssh -L 8096:127.0.0.1:8096 you@your-vps-ipAgora aceda a http://localhost:8096. O assistente guia-o pela seleção do idioma e, depois, pela criação de um utilizador administrador com uma palavra-passe forte. Esta conta dá acesso ao servidor, por isso não reutilize uma palavra-passe descartável. Adicione a sua primeira biblioteca: escolha o tipo de conteúdo Filmes, indique /media/Movies (o caminho dentro do contentor, não o caminho no host) e repita o processo com Séries em /media/Shows. Conclua o assistente para que o Jellyfin faça a leitura dos ficheiros. Numa biblioteca pequena, o resultado esperado é ver posters e títulos preenchidos dentro de um ou dois minutos. Mais tarde, pode adicionar ou editar bibliotecas em Dashboard → Libraries e forçar uma nova leitura com Scan All Libraries. Esses posters também são o que torna interessante experimentar um frontend alternativo quando o funcionamento básico estiver confirmado, porque Halcyon reconstrói a mesma biblioteca como uma videolocadora dos anos 90 que pode percorrer a partir das imagens que o Jellyfin já obteve.
Se utilizar qualquer transcodificação, abra Dashboard → Playback → Transcoding e defina o caminho temporário da transcodificação como /cache/transcodes. Assim, os ficheiros temporários ficam no volume de cache, em vez de aumentarem /config. Mantenha a aceleração de hardware definida como None, porque não existe uma GPU para a executar.
Acesso remoto: reverse proxy TLS ou mantê-lo na VPN
Há duas formas seguras de aceder ao Jellyfin a partir do exterior e uma forma insegura que deve evitar. A forma insegura é publicar diretamente a porta 8096 na internet: as credenciais de início de sessão seguem em texto simples e a porta é alvo de ataques de força bruta em poucas horas.
Opção A, reverse proxy TLS. Coloque o Jellyfin num subdomínio atrás do Traefik com TLS automático para as suas aplicações Docker ou atrás do nginx com um certificado Let's Encrypt emitido pelo Certbot. O Jellyfin utiliza WebSockets para atualizações em tempo real, por isso o proxy tem de encaminhar os cabeçalhos de upgrade. O Traefik faz isso automaticamente; no nginx, é necessário especificá-los e utilizar HTTP/1.1 para o upstream, caso contrário o upgrade não ocorre:
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";
}Defina JELLYFIN_PublishedServerUrl com o endereço https:// para que a autodeteção local anuncie o URL correto e para que as aplicações remotas utilizem o endereço fornecido. Adicione também o fail2ban para abrandar tentativas de força bruta contra o início de sessão. Quando o servidor estiver público, configure o Uptime Kuma para monitorizar o URL, para saber de uma indisponibilidade antes dos seus utilizadores. Configure as notificações para um servidor ntfy autoalojado; assim, uma verificação falhada chega como notificação push no telemóvel, em vez de como um email que só leria na manhã seguinte. Publicar uma página de início de sessão na internet também é uma boa oportunidade para auditar o restante sistema, em vez de assumir que apenas a porta 443 está a responder. O open-kritt executa essa verificação a partir do seu próprio contentor no mesmo VPS.
Opção B, mantê-lo privado numa VPN. Não publique a porta 8096; aceda ao Jellyfin apenas através de um túnel WireGuard terminado no mesmo servidor. Para uma rede doméstica, esta é a opção segura mais simples: não requer certificado, não expõe o serviço publicamente e não cria uma superfície para ataques de força bruta. Faça o bind do contentor ao endereço do túnel ou a localhost e estabeleça a ligação através da VPN. Consulte o guia de configuração da VPN WireGuard para um VPS privado para configurar o túnel.
Dimensionamento do armazenamento e backups
Dimensione pelo nível de qualidade, não pela quantidade de ficheiros. Filmes 1080p comprimidos ocupam 4-15 GB cada; um remux 1080p ocupa 20-40 GB; uma temporada de TV em 1080p ocupa 15-40 GB; em 4K, qualquer filme ocupa 40-100 GB. Uma biblioteca com algumas centenas de filmes e algumas séries precisa de um volume de 2-4 TB. É mais barato sobredimensionar o volume de blocos uma vez do que migrar os dados mais tarde.
/config contém todo o estado do servidor. Por isso, é o único elemento que tem obrigatoriamente de incluir nos backups. Crie um snapshot ou pare o serviço e crie um arquivo tar. Mantenha a cópia fora do servidor:
docker compose down
sudo tar czf jellyfin-config-$(date +%F).tgz -C ~/jellyfin config
docker compose up -d/cache e a pasta de transcodificação podem ser eliminados. Faça um backup separado dos conteúdos multimédia em /mnt/media ou aceite que terá de os extrair novamente. A maioria dos utilizadores escolhe a segunda opção devido ao tamanho dos ficheiros. As atualizações são docker compose pull && docker compose up -d. A tag :10 acima mantém-se na versão principal 10.x. Por isso, passar para a versão principal seguinte exige uma alteração deliberada da tag. Consulte as notas de lançamento do Jellyfin antes de fazer essa alteração, porque as migrações do esquema da biblioteca ocorrem entre versões principais. Uma tag fixa e um diretório de estado incluído nos backups são toda a receita necessária para qualquer contentor sempre ativo. É o mesmo padrão usado para manter a memória e os agendamentos de um agente autoalojado disponíveis após reinícios.
Modos de falha e mensagens apresentadas
A biblioteca fica vazia depois de uma análise. 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 contentor não consegue ler esse caminho. Causa: os ficheiros multimédia pertencem a root ou a um uid diferente do valor user:, um diretório não tem o bit de execução, ou o próprio mount pai não pode ser percorrido por esse uid. Correção: chown -R 1000:1000 /mnt/media, diretórios 755, ficheiros 644 e, depois, execute uma nova análise.
A reprodução mantém a CPU ocupada e fica a carregar. docker stats jellyfin mostra a CPU perto de 100% vezes o número de cores, e Dashboard → Playback apresenta a sessão como Transcode, com uma velocidade inferior a 1.0x. O cliente não está a reproduzir diretamente. Por isso, o VPS está a fazer transcodificação pela CPU a uma velocidade inferior à reprodução em tempo real e não consegue acompanhar. Causa: um codec ou contentor não suportado, incorporação de legendas na imagem ou conversão de tons HDR. Correção: mude para um cliente com reprodução direta, mantenha as fontes em H.264/AAC, use legendas de texto (SRT) em vez de legendas de imagem (PGS/VOBSUB), que obrigam à incorporação na imagem, e não use 4K HDR num servidor que dependa apenas da CPU.
"Não existem streams compatíveis disponíveis." A mensagem completa costuma ser "Este cliente não é compatível com o conteúdo multimédia e o servidor não está a enviar um formato multimédia compatível." O cliente rejeitou a fonte e a transcodificação alternativa também não iniciou. Causa: um comando ffmpeg incorreto, um ficheiro ilegível ou o perfil do utilizador bloqueia a conversão de vídeo. Correção: leia a linha do ffmpeg em Dashboard → Logs, confirme que o ficheiro é reproduzido, verifique as permissões de reprodução do utilizador se depender de transcodificação e tente um segundo cliente para excluir problemas de codecs do browser.
Os filmes não têm poster ou têm o poster errado. Os metadados não corresponderam. Causa: um filme não está na sua própria pasta Name (Year), uma pasta de temporada tem o nome S01 em vez de Season 01, os episódios não seguem o formato S01E01 ou falta o ano. Correção: mude os nomes para o esquema acima e, depois, selecione Refresh metadata → Replace all, ou use Identify num único item para fixar a entrada correta do TMDB/TVDB.
FAQ
Um VPS pode transcodificar vídeo sem uma GPU?
Sim, mas apenas na CPU, e isso é dispendioso. Uma única transcodificação por software de 1080p pode saturar vários vCPUs, e 4K ou HEVC normalmente não acompanha o tempo real, causando buffering durante a reprodução. A melhor opção é evitar a transcodificação: mantenha a biblioteca em H.264/AAC e use aplicações cliente que façam reprodução direta, para que o VPS se limite a transmitir bytes. Alugue uma instância com GPU apenas se precisar realmente de transcodificação em tempo real.
Por que motivo a minha biblioteca Jellyfin está vazia depois de uma verificação?
Quase sempre, o problema são as permissões. A imagem oficial jellyfin/jellyfin é executada com o user: que definiu, ou como root, e se os ficheiros não forem legíveis por esse uid, os logs da verificação registam Access to the path ... is denied e ignoram-nos. Corrija o proprietário com chown -R 1000:1000 /mnt/media, atribua o bit de execução aos diretórios com 755 e faça uma nova verificação. Verifique também o diretório pai, porque, se o uid do contentor não puder atravessar o próprio /mnt/media, nunca chegará às pastas da biblioteca e tudo ficará vazio. A segunda causa mais comum é uma estrutura de diretórios que não corresponde ao formato esperado pelo Jellyfin.
Como posso aceder ao Jellyfin remotamente e em segurança?
Há duas boas opções. Coloque-o atrás de um reverse proxy TLS num subdomínio, para que o início de sessão e a transmissão sejam cifrados, e adicione fail2ban. Nunca exponha a porta simples 8096, porque isso envia a sua palavra-passe em texto simples. Em alternativa, mantenha o serviço totalmente privado e aceda a ele apenas através de uma VPN, que é a opção segura mais simples para uma casa. Configure nas aplicações o endereço público diretamente. A autodeteção é uma transmissão na rede local, por isso não chega aos clientes que entram pela Internet.
De quanto espaço em disco e largura de banda precisa um VPS Jellyfin?
O espaço em disco depende da qualidade: reserve 4-15 GB por filme 1080p comprimido, 20-40 GB por remux e 40-100 GB para 4K. Assim, a maioria das bibliotecas precisa de um volume de bloco de 2-4 TB. A largura de banda é determinada pelo bitrate da reprodução direta: 8-12 Mbps por transmissão 1080p e muito mais para 4K. Confirme se a velocidade da porta suporta o número de espectadores simultâneos e monitorize o limite mensal de transferência. Reserve capacidade adicional de CPU se planear transcodificar. Se planear fazer reprodução direta, dê prioridade à largura de banda em vez do número de cores.
É legal executar o Jellyfin num VPS?
O Jellyfin é software gratuito e de código aberto, e a sua execução é totalmente legal. O que importa é o conteúdo: transmita apenas meios que possui ou que está autorizado a armazenar, como cópias dos seus próprios discos, gravações ou ficheiros para os quais tem direitos. O Jellyfin não inclui meios nem fornece qualquer forma de os obter. É um leitor para uma biblioteca que já possui.