Como instalar Paperless-ngx em um VPS com Docker
Veja como instalar Paperless-ngx em um VPS com Docker Compose, Postgres, PAPERLESS_URL, pasta consume, OCR, HTTPS e backups dos documentos.
O que você vai criar
O Paperless-ngx em um VPS transforma uma pasta de documentos digitalizados em um arquivo pesquisável. Você coloca um PDF em um diretório monitorado. O servidor executa OCR (reconhecimento óptico de caracteres), extrai o texto, estima uma data e um correspondente e arquiva o documento. A instalação usa um único arquivo do Docker Compose com quatro serviços. Depois disso, tudo é configuração. Este guia dedica a maior parte do conteúdo à configuração, porque é nessa etapa que as instalações falham.
O Paperless-ngx é o fork comunitário mantido do projeto Paperless original. Ele é gratuito, auto-hospedado e armazena seus documentos como arquivos comuns no disco. Assim, você nunca perde o acesso ao próprio arquivo. Executá-lo em um VPS, em vez de um computador doméstico, permite acessar seus documentos digitalizados de qualquer lugar sem abrir uma porta no roteador de casa. Ele também funciona bem com uma instância privada do Nextcloud para os arquivos que não são de papel.
O que a pilha realmente executa
O arquivo compose oficial inicia quatro contêineres. Saber a função de cada um facilita a leitura dos logs.
webserver: a própria imagem do paperless-ngx. Ela executa a interface web, a API, o consumer que monitora a pasta de entrada e os workers de tarefas do Celery que fazem o OCR.db: PostgreSQL. Ele armazena metadados, tags, correspondentes e as tabelas do índice de pesquisa de texto completo. Ele não armazena seus PDFs.broker: Valkey, um armazenamento de chave-valor compatível com Redis. Ele funciona como a fila de tarefas entre o processo web e os workers.gotenbergetika: opcionais, presentes apenas nas variantes de compose-tika. Eles convertem documentos do Office (.docx,.xlsx,.odt) para PDF, para que o paperless possa indexá-los.
Em julho de 2026, o arquivo compose do postgres fixa docker.io/library/postgres:18 e docker.io/valkey/valkey:9-alpine e obtém o aplicativo de ghcr.io/paperless-ngx/paperless-ngx:latest.
Pré-requisitos
- Um VPS KVM com Ubuntu 24.04 e acesso sudo, com Docker e o plugin Compose já instalados. Se essa parte for nova para você, comece pelos fundamentos do Docker Compose para um VPS e depois volte.
- Um nome de domínio com um registro A apontando para o VPS. O Paperless se recusa a atender em um nome de host que não tenha sido configurado, portanto isso é importante antes do que você imagina.
- A memória é a principal limitação. PostgreSQL, Valkey, gunicorn e um worker do Tesseract OCR, todos residentes ao mesmo tempo, cabem em 2 GB para uso leve. Use 4 GB se planeja importar um backlog de centenas de digitalizações, porque o OCR de um PDF grande com várias páginas causa o pico de memória que faz o kernel encerrar um worker por falta de memória.
- Disco: seu arquivo é armazenado duas vezes: o arquivo original e um PDF de arquivo com OCR. Portanto, reserve aproximadamente o dobro do tamanho das suas digitalizações.
Obtenha os arquivos oficiais do compose
Existe um instalador interativo:
bash -c "$(curl --location --silent --show-error https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"Ele faz perguntas e grava os arquivos para você. Fazer isso manualmente exige quatro comandos e permite saber onde tudo está, o que é importante em um servidor que você vai manter.
mkdir -p ~/paperless && cd ~/paperless
curl -fsSL -o docker-compose.yml https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/docker-compose.postgres.yml
curl -fsSL -o docker-compose.env https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/docker-compose.env
curl -fsSL -o .env https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/.envAs variantes ficam no mesmo diretório: docker-compose.sqlite.yml, docker-compose.mariadb.yml e uma versão -tika de cada uma. Escolha postgres para uma instalação nova. SQLite funciona bem para algumas centenas de documentos, mas o índice de pesquisa de texto completo fica lento muito antes do PostgreSQL.
O arquivo .env contém uma linha, COMPOSE_PROJECT_NAME=paperless. Esse nome se torna o prefixo de cada container e volume. Portanto, não o exclua para depois se perguntar por que docker compose down -v não consegue encontrar seus dados.
Configure docker-compose.env antes da primeira inicialização
Duas configurações são obrigatórias. Gere a chave secreta com o comando documentado pelo projeto:
python3 -c "import secrets; print(secrets.token_urlsafe(64))"Depois edite docker-compose.env:
PAPERLESS_SECRET_KEY=<the long string you just generated>
PAPERLESS_URL=https://paperless.example.com
PAPERLESS_TIME_ZONE=Europe/Berlin
PAPERLESS_OCR_LANGUAGE=deu+eng
USERMAP_UID=1000
USERMAP_GID=1000PAPERLESS_SECRET_KEY é fornecido com o valor literal change-me. Ele assina os cookies de sessão. Se você o deixar assim, qualquer pessoa que conheça o valor padrão poderá forjar uma sessão. Defina-o antes da primeira inicialização, porque alterá-lo depois desconecta todos os usuários.
PAPERLESS_URL é a configuração que economiza uma hora de trabalho. Paperless é uma aplicação Django, e o Django valida o cabeçalho Host de cada solicitação. Defina PAPERLESS_URL, e ele preencherá ALLOWED_HOSTS, CORS_ALLOWED_HOSTS e CSRF_TRUSTED_ORIGINS automaticamente. Se deixá-lo vazio, apontar um domínio para o servidor fará todas as páginas retornarem Bad Request (400), com DisallowedHost no log do container. Escreva o valor sem barra final e sem caminho.
USERMAP_UID e USERMAP_GID definem o usuário com o qual o container é executado. Use os valores da sua própria conta, verificados com id -u e id -g. Se eles não corresponderem, os arquivos copiados para a pasta consume não poderão ser lidos pelo consumer, e o log exibirá um erro de permissão em vez de uma importação.
Inicie a stack e crie o primeiro usuário
docker compose pull
docker compose up -d
docker compose run --rm webserver createsuperuser
docker compose logs -f webservercreatesuperuser solicita um nome de usuário, um e-mail e uma senha. Não há login padrão. Se você ignorar esta etapa, verá uma página de login que nunca aceitará nenhuma credencial. Aguarde a linha do log informando que o servidor está escutando na porta 8000 antes de abrir o navegador. A primeira inicialização também executa as migrações do banco de dados, o que leva um ou dois minutos.
Verifique localmente antes de usar um domínio:
curl -I http://127.0.0.1:8000Um redirecionamento de 302 para /accounts/login/ indica que a stack está funcionando corretamente.
Coloque HTTPS na frente
O arquivo compose padrão publica 8000:8000, que aceita conexões em todas as interfaces. Em um VPS público, isso disponibiliza todo o seu arquivo de documentos por HTTP sem criptografia para qualquer pessoa que descubra o endereço. Altere a linha da porta para aceitar conexões somente no loopback:
ports:
- "127.0.0.1:8000:8000"Em seguida, termine o TLS (segurança da camada de transporte) em um proxy reverso e encaminhe as solicitações para 127.0.0.1:8000. Se este for o único aplicativo no servidor, qualquer proxy com um cliente ACME (ambiente de gerenciamento automático de certificados) será suficiente. Se você estiver executando vários contêineres atrás de uma única configuração de certificados, siga o padrão de proxy reverso Traefik para vários aplicativos Docker Compose e conecte o serviço webserver à rede do proxy sem publicar nenhuma porta.
Independentemente do proxy usado, ele deve enviar X-Forwarded-Proto: https. Sem esse cabeçalho, o Django considera que a solicitação chegou por HTTP, a verificação de origem do formulário de login falha e você recebe CSRF verification failed. Request aborted. em uma página que parece correta. A outra parte da correção é definir PAPERLESS_URL exatamente como o endereço https:// que você digita no navegador.
Aumente também o limite de upload do proxy. Uma digitalização de 40 MB enviada por um proxy que limita o corpo das solicitações a 1 MB é rejeitada antes que o paperless possa recebê-la, e o navegador informa uma falha genérica de upload.
Como o diretório consume funciona
O arquivo compose monta ./consume como bind mount, a partir do diretório do compose, dentro do contêiner. Tudo o que você colocar nesse diretório será importado e depois excluído da pasta, porque o arquivo passa a existir no volume de mídia gerenciado pelo paperless.
cp ~/scan-2026-07-14.pdf ~/paperless/consume/
docker compose logs -f webserverVocê deverá ver o consumer processar o nome do arquivo, executar o OCR e terminar com uma linha informando que o documento foi adicionado. Todo o ciclo leva alguns segundos para uma digitalização de uma página e pode levar um minuto ou mais para um documento longo.
Duas configurações alteram a forma como os arquivos são encontrados. PAPERLESS_CONSUMER_RECURSIVE=true faz o paperless procurar em subpastas, e PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=true transforma o nome de cada subpasta em uma tag. Assim, colocar um arquivo em consume/invoices/2026/ atribui a ele as tags invoices e 2026. Esse é o sistema de arquivamento mais simples que você poderá montar.
A detecção é a outra parte. Por padrão, PAPERLESS_CONSUMER_POLLING_INTERVAL é 0, o que significa que o paperless usa notificações do sistema de arquivos fornecidas pelo kernel, disparadas imediatamente. Essas notificações não atravessam um sistema de arquivos de rede. Se o diretório consume for um compartilhamento NFS ou SMB para que um scanner de rede possa gravar nele, nada será detectado. Para corrigir isso, defina o intervalo como um número positivo de segundos, para que o paperless examine o diretório.
Idiomas de OCR e seus custos
PAPERLESS_OCR_LANGUAGE aceita um código Tesseract de três letras, eng por padrão. Combine idiomas com um sinal de mais, como em deu+eng. O Tesseract tenta cada um deles e mantém o melhor resultado, portanto cada idioma adicional multiplica o tempo de CPU gasto em cada página. Em uma VPS com vCPU compartilhada, essa é a diferença entre uma digitalização terminar em dez segundos ou em um minuto. Liste apenas os idiomas nos quais seus documentos realmente estão escritos.
A imagem inclui inglês, alemão, italiano, espanhol e francês. Para qualquer outro idioma, adicione-o a PAPERLESS_OCR_LANGUAGES como uma lista separada por espaços, por exemplo, PAPERLESS_OCR_LANGUAGES=tur ces, e reinicie. O contêiner baixa os pacotes de dados do Tesseract na inicialização, portanto a primeira inicialização após essa alteração é mais lenta.
Faça backup do banco de dados e da mídia
Copiar os volumes do Docker enquanto o PostgreSQL está em execução gera um backup que pode não ser restaurado. O Paperless inclui seu próprio exportador, que grava os documentos e um manifesto JSON com todos os metadados no bind mount ./export:
docker compose exec webserver document_exporter ../export --delete --no-progress-bar--delete remove os arquivos exportados que não correspondem mais a um documento atual. Assim, a pasta permanece como um espelho, em vez de crescer indefinidamente. --no-progress-bar mantém a saída limpa quando isso é executado pelo cron.
A restauração é feita com document_importer usando a mesma pasta em uma stack nova. Portanto, o diretório de exportação é o único item que você precisa manter protegido. Envie-o para outro local em uma agenda usando backups restic criptografados e com deduplicação do seu VPS e execute a exportação primeiro, para que o restic nunca capture um arquivo parcialmente gravado.
Verifique um backup confirmando que export/manifest.json existe e que a quantidade de arquivos corresponde à quantidade de documentos exibida na interface. Um backup que nunca foi listado não é um backup.
FAQ
Por que todas as páginas retornam "Bad Request (400)" depois que aponto meu domínio para elas?
O Django rejeitou o cabeçalho Host porque seu domínio não está em ALLOWED_HOSTS. Defina PAPERLESS_URL=https://paperless.example.com em docker-compose.env, sem uma barra final, e execute docker compose up -d para recriar o container. Editar apenas o arquivo de ambiente não altera nada, porque o container em execução mantém o ambiente com o qual foi iniciado.
Coloquei um PDF na pasta de consumo, mas nada aconteceu. Qual é o problema?
Verifique docker compose logs webserver primeiro. Um erro de permissão significa que USERMAP_UID e USERMAP_GID não correspondem à conta proprietária do arquivo. Corrija esses valores e recrie o container. A ausência total de uma linha no log significa que o evento do arquivo nunca chegou. Isso ocorre em compartilhamentos de rede porque as notificações do kernel não atravessam esses compartilhamentos. Defina PAPERLESS_CONSUMER_POLLING_INTERVAL como algo semelhante a 30, e o paperless fará a varredura da pasta a cada 30 segundos.
Posso executar o paperless-ngx com SQLite em vez de PostgreSQL?
Sim, docker-compose.sqlite.yml é compatível e usa menos memória, o que é adequado para um VPS pequeno. O impacto aparece conforme o arquivo cresce: a pesquisa de texto completo e as edições em massa de tags ficam visivelmente mais lentas quando há milhares de documentos. Migrar posteriormente exige uma exportação e uma importação. Portanto, escolha PostgreSQL agora se espera que o arquivo continue crescendo.
De quanto espaço em disco um arquivo de documentos digitalizados realmente precisa?
Aproximadamente o dobro do tamanho dos arquivos de origem. O Paperless mantém o original inalterado e armazena um segundo PDF processado por OCR, com uma camada de texto pesquisável, além de miniaturas pequenas. Um documento digitalizado somente com texto, de 200 KB, continua pequeno. Um documento digitalizado em cores, de 30 MB, com um contrato extenso, ocupa cerca de 60 MB. Se você mantiver o diretório de exportação no mesmo disco, inclua esse espaço também. Nesse caso, o mesmo arquivo ocupará três vezes esse espaço no disco.
Preciso dos containers Tika e Gotenberg?
Somente se quiser indexar arquivos do Word, Excel ou OpenDocument junto com seus PDFs. Eles convertem esses formatos para PDF para que o paperless possa executar OCR e pesquisá-los. Eles também adicionam mais dois containers em execução e algumas centenas de megabytes de memória. Portanto, não os use em um host pequeno se tudo o que você arquiva já for PDF ou imagem.