SSD Nodes Learn Hosting plans →
Guías Matt ConnorPor Matt Connor · Actualizado 2026-08-28

Instalar paperless-ngx en un VPS con Docker Compose

Guía para desplegar paperless-ngx en un VPS con Docker Compose: Postgres oficial, PAPERLESS_URL, carpeta consume, OCR, HTTPS y copias de seguridad.

Qué va a crear

Paperless-ngx en un VPS convierte una carpeta de documentos escaneados en un archivo con búsqueda. Coloque un PDF en un directorio supervisado y el servidor ejecutará OCR (reconocimiento óptico de caracteres), extraerá el texto, estimará una fecha y un corresponsal, y archivará el documento. La instalación consta de un archivo de Docker Compose con cuatro servicios. Después sólo hay que configurar el sistema. Esta guía dedica la mayor parte de su contenido a esa configuración, porque es donde suelen fallar las instalaciones. No es una biblioteca de fotos: OCR y la estimación del corresponsal no sirven para una carpeta de JPEG de vacaciones. Guárdelos en un servidor de fotos diseñado para ello y reserve paperless para los documentos. Con el vídeo ocurre lo mismo: una colección de películas ripeadas corresponde a un servidor multimedia, donde algo como una interfaz de Jellyfin ambientada como un videoclub de los años 90 hace que explorar sea el objetivo, en lugar de buscar.

Paperless-ngx es la bifurcación comunitaria mantenida del proyecto original Paperless. Es gratuito, se aloja en sus propios sistemas y almacena los documentos como archivos normales en el disco, por lo que nunca pierde el acceso a su propio archivo. Ejecutarlo en un VPS en lugar de un equipo doméstico permite acceder a los documentos escaneados desde cualquier lugar sin abrir un puerto en el router de casa. Además, se integra bien con una instancia privada de Nextcloud para los archivos que no están en papel. La misma lógica se aplica al equipo al que está conectado el escáner: un relay propio de RustDesk en ese VPS permite controlar ese equipo desde otro lugar sin abrir tampoco un puerto en el router.

Qué ejecuta realmente la pila

El archivo compose oficial inicia cuatro contenedores. Saber qué hace cada uno facilita la interpretación de los registros.

  • webserver: la propia imagen de paperless-ngx. Ejecuta la interfaz web, la API, el consumidor que supervisa la carpeta de entrada y los workers de tareas de Celery que realizan el OCR.
  • db: PostgreSQL. Almacena los metadatos, las etiquetas, los corresponsales y las tablas del índice de búsqueda de texto completo. No almacena los archivos PDF.
  • broker: Valkey, un almacén de pares clave-valor compatible con Redis. Es la cola de tareas entre el proceso web y los workers.
  • gotenberg y tika: opcionales, sólo en las variantes compose de -tika. Convierten documentos de Office (.docx, .xlsx, .odt) a PDF para que paperless pueda indexarlos.

En julio de 2026, el archivo compose de postgres fija docker.io/library/postgres:18 y docker.io/valkey/valkey:9-alpine, y obtiene la aplicación de ghcr.io/paperless-ngx/paperless-ngx:latest.

Requisitos previos

  • Un VPS KVM con Ubuntu 24.04, acceso mediante sudo y Docker con el complemento Compose ya instalado. Si esta parte es nueva para usted, empiece por los fundamentos de Docker Compose para un VPS y vuelva aquí.
  • Un nombre de dominio con un registro A que apunte al VPS. Paperless no acepta solicitudes en un nombre de host que no se haya configurado, por lo que esto es importante antes de lo esperado.
  • La memoria es la principal limitación. PostgreSQL, Valkey, gunicorn y un worker de OCR de Tesseract pueden permanecer en ejecución al mismo tiempo dentro de 2 GB para un uso ligero. Asigne 4 GB si planea importar cientos de documentos pendientes, porque el OCR de un PDF grande de varias páginas es el pico de memoria que puede hacer que el kernel termine un worker mediante el OOM killer.
  • Disco: el archivo se almacena dos veces: el archivo original y un PDF de archivo con OCR. Reserve aproximadamente el doble del tamaño de sus escaneos.

Obtenga los archivos oficiales de Compose

Existe un instalador interactivo:

bash -c "$(curl --location --silent --show-error https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"

Hace preguntas y escribe los archivos por usted. Hacerlo manualmente requiere cuatro comandos y le permite saber dónde está cada elemento. Eso es lo que necesita en un servidor que va a mantener.

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/.env

Las variantes están en el mismo directorio: docker-compose.sqlite.yml, docker-compose.mariadb.yml y una versión -tika de cada una. Elija postgres para una instalación nueva. SQLite funciona bien con unos cientos de documentos, pero el índice de búsqueda de texto completo se vuelve lento mucho antes que PostgreSQL.

El archivo .env contiene una línea: COMPOSE_PROJECT_NAME=paperless. Ese nombre se convierte en el prefijo de cada contenedor y volumen. No lo elimine y luego se pregunte por qué docker compose down -v no puede encontrar sus datos.

Configurar docker-compose.env antes del primer arranque

Hay dos ajustes obligatorios. Genere la clave secreta con el comando documentado por el proyecto:

python3 -c "import secrets; print(secrets.token_urlsafe(64))"

Después, 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=1000

PAPERLESS_SECRET_KEY se distribuye con el valor literal change-me. Esta clave firma las cookies de sesión. Si la deja sin cambiar, cualquiera que conozca el valor predeterminado puede falsificar una sesión. Establézcala antes del primer arranque, porque cambiarla después cierra la sesión de todos los usuarios.

PAPERLESS_URL es el ajuste que evita perder una hora. Paperless es una aplicación Django, y Django valida la cabecera Host de cada petición. Establezca PAPERLESS_URL y rellenará ALLOWED_HOSTS, CORS_ALLOWED_HOSTS y CSRF_TRUSTED_ORIGINS automáticamente. Si lo deja vacío, apunta un dominio al servidor y cada página devuelve Bad Request (400); el registro del contenedor muestra DisallowedHost. Escriba el valor sin barra final ni ruta.

USERMAP_UID y USERMAP_GID establecen el usuario con el que se ejecuta el contenedor. Ajústelos a su propia cuenta, comprobada con id -u y id -g. Si no coinciden, el consumidor no puede leer los archivos que copie en la carpeta consume, y el registro muestra un error de permisos en lugar de realizar la importación.

Inicie la pila y cree el primer usuario

docker compose pull
docker compose up -d
docker compose run --rm webserver createsuperuser
docker compose logs -f webserver

createsuperuser solicita un nombre de usuario, un correo electrónico y una contraseña. No existe un inicio de sesión predeterminado, por lo que omitir este paso le deja en una página de inicio de sesión que nunca aceptará ninguna credencial. Espere al mensaje del registro que indica que el servidor escucha en el puerto 8000 antes de probar el navegador. El primer inicio también ejecuta las migraciones de la base de datos, que tardan uno o dos minutos.

Compruébelo localmente antes de usar un dominio:

curl -I http://127.0.0.1:8000

Una redirección de 302 a /accounts/login/ indica que la pila funciona correctamente.

Anteponer HTTPS al servicio

El archivo compose incluido publica 8000:8000, que se enlaza a todas las interfaces. En un VPS público, esto expone todo el archivo de documentos mediante HTTP sin cifrar a cualquiera que encuentre la dirección. Cambie la línea del puerto para que se enlace sólo a loopback:

    ports:
      - "127.0.0.1:8000:8000"

Termine TLS (seguridad de la capa de transporte) en un reverse proxy y reenvíe las peticiones a 127.0.0.1:8000. Si esta es la única aplicación del servidor, puede usar cualquier proxy con un cliente ACME (entorno de gestión automática de certificados). Si ejecuta varios contenedores detrás de una única configuración de certificados, siga el patrón de reverse proxy Traefik para varias aplicaciones de Docker Compose y conecte el servicio webserver a la red del proxy sin publicar ningún puerto.

Independientemente del proxy que use, debe enviar X-Forwarded-Proto: https. Sin esta cabecera, Django considera que la petición llegó mediante HTTP, la comprobación del origen del formulario de inicio de sesión falla y aparece CSRF verification failed. Request aborted. en una página que parece correcta. La otra parte de la solución consiste en establecer PAPERLESS_URL en la dirección https:// exacta que escribe en el navegador.

Aumente también el límite de tamaño de carga del proxy. Un escaneo de 40 MB enviado a través de un proxy que limita los cuerpos a 1 MB se rechaza antes de que paperless lo reciba, y el navegador muestra un error de carga genérico.

Cómo funciona el directorio consume

El archivo compose monta ./consume desde el directorio de compose en el contenedor mediante un bind mount. Todo lo que coloque allí se importa y después se elimina del directorio, porque el archivo pasa a estar en el volumen de medios gestionado por paperless.

cp ~/scan-2026-07-14.pdf ~/paperless/consume/
docker compose logs -f webserver

Debería ver que el consumidor detecta el nombre del archivo, ejecuta el OCR y termina con una línea que indica que el documento se añadió. Todo el ciclo tarda unos segundos para un escaneo de una página y puede tardar un minuto o más para un documento largo.

Dos opciones cambian la forma en que se encuentran los archivos. PAPERLESS_CONSUMER_RECURSIVE=true hace que paperless busque en los subdirectorios, y PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=true convierte el nombre de cada subdirectorio en una etiqueta. Por tanto, colocar un archivo en consume/invoices/2026/ le asigna las etiquetas invoices y 2026. Es el sistema de clasificación más sencillo que puede implementar.

La detección es la otra parte. De forma predeterminada, PAPERLESS_CONSUMER_POLLING_INTERVAL es 0, lo que significa que paperless utiliza notificaciones del sistema de archivos del kernel, que se generan de inmediato. Esas notificaciones no atraviesan un sistema de archivos de red. Si el directorio consume es un recurso compartido NFS o SMB para que un escáner de red pueda escribir en él, no se detectará ningún archivo. Para corregirlo, establezca el intervalo en un número positivo de segundos para que paperless examine el directorio.

Idiomas de OCR y su coste

PAPERLESS_OCR_LANGUAGE acepta un código de Tesseract de tres letras, eng de forma predeterminada. Combine los idiomas con un signo más, como en deu+eng. Tesseract prueba cada uno y conserva el mejor resultado, por lo que cada idioma adicional multiplica el tiempo de CPU empleado en cada página. En un VPS con vCPU compartida, esa es la diferencia entre terminar un escaneo en diez segundos o en un minuto. Incluya sólo los idiomas en los que estén escritos realmente sus documentos.

La imagen incluye inglés, alemán, italiano, español y francés. Para cualquier otro idioma, añádalo a PAPERLESS_OCR_LANGUAGES como una lista separada por espacios, por ejemplo PAPERLESS_OCR_LANGUAGES=tur ces, y reinicie. El contenedor descarga los paquetes de datos de Tesseract al arrancar, por lo que el primer arranque posterior a ese cambio será más lento.

Haz una copia de seguridad de la base de datos y los archivos multimedia

Copiar los volúmenes de Docker mientras PostgreSQL está en ejecución produce una copia de seguridad que puede no restaurarse correctamente. Paperless incluye su propio exportador, que escribe los documentos y un manifiesto JSON con todos los metadatos en el bind mount ./export:

docker compose exec webserver document_exporter ../export --delete --no-progress-bar

--delete elimina los archivos exportados que ya no corresponden a un documento actual, por lo que la carpeta se mantiene como un espejo en lugar de crecer indefinidamente. --no-progress-bar mantiene limpia la salida cuando se ejecuta desde cron.

La restauración se realiza document_importer usando esa misma carpeta en una pila nueva. Por tanto, el directorio de exportación es lo único que debe conservar de forma segura. Envíelo a otra ubicación según un horario mediante copias de seguridad cifradas y con deduplicación de restic desde su VPS, y ejecute primero la exportación para que restic nunca capture un archivo escrito parcialmente.

Verifique una copia de seguridad comprobando que exista export/manifest.json y que el número de archivos coincida con el número de documentos de la interfaz. Una copia de seguridad que nunca ha enumerado no es una copia de seguridad. Una exportación nocturna que empieza a fallar sin avisar es aún peor. Configure el trabajo de cron para que envíe su estado de salida a su propio servidor ntfy y sabrá durante la semana en que falla, en lugar de enterarse el día que necesite restaurarla.

FAQ

¿Por qué todas las páginas devuelven "Bad Request (400)" después de apuntar mi dominio al servicio?

Django rechazó la cabecera Host porque el dominio no está incluido en ALLOWED_HOSTS. Configure PAPERLESS_URL=https://paperless.example.com en docker-compose.env, sin una barra final, y ejecute docker compose up -d para volver a crear el contenedor. Editar sólo el archivo de entorno no sirve, porque el contenedor en ejecución conserva el entorno con el que se inició.

Dejé un PDF en la carpeta de consumo y no ocurrió nada. ¿Cuál es el problema?

Compruebe primero docker compose logs webserver. Un error de permisos significa que USERMAP_UID y USERMAP_GID no coinciden con la cuenta propietaria del archivo. Corrija esos valores y vuelva a crear el contenedor. Si no aparece ninguna línea en el registro, el evento del archivo nunca llegó. Esto ocurre en recursos compartidos de red porque las notificaciones del kernel no atraviesan esos recursos. Configure PAPERLESS_CONSUMER_POLLING_INTERVAL con un valor como 30 y paperless analizará la carpeta cada 30 segundos.

¿Puedo ejecutar paperless-ngx con SQLite en lugar de PostgreSQL?

Sí, docker-compose.sqlite.yml es compatible y utiliza menos memoria, por lo que resulta adecuado para un VPS pequeño. La desventaja aparece cuando el archivo crece: la búsqueda de texto completo y las ediciones masivas de etiquetas se ralentizan de forma apreciable al alcanzar miles de documentos. Migrar más adelante requiere una exportación y una importación. Si espera que el archivo siga creciendo, elija PostgreSQL desde ahora.

¿Cuánto espacio en disco necesita realmente un archivo de documentos escaneados?

Aproximadamente el doble del tamaño de los archivos originales. Paperless conserva el original sin modificar y almacena un segundo PDF procesado con OCR y una capa de texto buscable, además de miniaturas pequeñas. Un escaneo de sólo texto de 200 KB sigue ocupando poco espacio. Un escaneo en color de 30 MB de un contrato largo ocupa unos 60 MB. Añada el directorio de exportación si lo mantiene en el mismo disco. En ese caso, el mismo archivo ocupa tres veces más espacio en disco.

¿Necesito los contenedores de Tika y Gotenberg?

Sólo si quiere indexar archivos de Word, Excel u OpenDocument junto con los PDF. Estos contenedores convierten esos formatos a PDF para que paperless pueda aplicar OCR y realizar búsquedas. También añaden dos contenedores en ejecución y unos cientos de megabytes de memoria. Omítalos en un sistema pequeño si todos los archivos que guarda ya son PDF o imágenes.

#paperless-ngx#documents#self-hosting#docker#ocr