Instalar Paperless-ngx en un VPS con Docker Compose
Instala Paperless-ngx en un VPS con Docker Compose y Postgres. Configura PAPERLESS_URL, la 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. El servidor ejecuta OCR (reconocimiento óptico de caracteres), extrae el texto, intenta determinar una fecha y un corresponsal, y archiva el documento. La instalación usa un único archivo de Docker Compose con cuatro servicios. Después de eso, todo es configuración. Esta guía dedica la mayor parte de su contenido a esa configuración porque es donde fallan las instalaciones.
Paperless-ngx es la bifurcación comunitaria mantenida del proyecto original Paperless. Es gratuito, se aloja de forma autónoma y almacena los documentos como archivos normales en el disco. Por tanto, siempre puede acceder 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 doméstico. Además, se integra bien con una instancia privada de Nextcloud para los archivos que no están en papel.
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 imagen de paperless-ngx. Ejecuta la interfaz web, la API, el consumidor que supervisa la carpeta de entrada y los trabajadores 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 trabajadores.gotenbergytika: opcionales, solo en las variantes de compose-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 y acceso mediante sudo, con Docker y el complemento Compose ya instalados. Si esta parte es nueva para ti, empieza por los fundamentos de Docker Compose para un VPS y vuelve después.
- Un nombre de dominio con un registro A que apunte al VPS. Paperless se niega a servir contenido en un nombre de host que no se haya configurado, por lo que esto es importante antes de lo que esperas.
- La memoria es la principal limitación. PostgreSQL, Valkey, gunicorn y un worker de OCR de Tesseract pueden ejecutarse al mismo tiempo en 2 GB para un uso ligero. Asigna 4 GB si vas a importar cientos de documentos pendientes, porque el OCR de un PDF grande de varias páginas provoca 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. Reserva aproximadamente el doble del tamaño de tus escaneos.
Obtenga los archivos oficiales de compose
Hay 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á todo, lo cual es importante en un servidor que deberá 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/.envLas 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 es adecuado para 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.
Configure docker-compose.env antes del primer inicio
Dos configuraciones son obligatorias. Genere la clave secreta con el comando que documenta 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=1000PAPERLESS_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 inicio, porque cambiarla después cierra la sesión de todos los usuarios.
PAPERLESS_URL es la configuración que evita perder una hora. Paperless es una aplicación Django, y Django valida el encabezado Host de cada solicitud. Establezca PAPERLESS_URL y se rellenarán 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. Escríbalo sin una barra final y sin una ruta.
USERMAP_UID y USERMAP_GID establecen el usuario con el que se ejecuta el contenedor. Haga que coincidan con su propia cuenta, comprobándolo 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 una importación.
Iniciar la pila y crear el primer usuario
docker compose pull
docker compose up -d
docker compose run --rm webserver createsuperuser
docker compose logs -f webservercreatesuperuser solicita un nombre de usuario, un correo electrónico y una contraseña. No hay credenciales de inicio de sesión predeterminadas. Si omite este paso, llegará a una página de inicio de sesión que nunca aceptará ninguna credencial. Espere a que el registro indique que el servidor está escuchando en el puerto 8000 antes de abrir el navegador. El primer inicio también ejecuta las migraciones de la base de datos, lo que tarda uno o dos minutos.
Compruébelo localmente antes de configurar un dominio:
curl -I http://127.0.0.1:8000Una redirección de 302 a /accounts/login/ indica que la pila funciona correctamente.
Colocar HTTPS delante de la aplicación
El archivo compose predeterminado 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 cifrado a cualquiera que encuentre la dirección. Cambie la línea del puerto para que se enlace únicamente a loopback:
ports:
- "127.0.0.1:8000:8000"Termine TLS (seguridad de la capa de transporte) en un proxy inverso y reenvíe las solicitudes 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 proxy inverso 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 este encabezado, Django considera que la solicitud llegó mediante HTTP, falla la comprobación del origen en el formulario de inicio de sesión y aparece CSRF verification failed. Request aborted. en una página que parece correcta. La otra parte de esta corrección consiste en establecer PAPERLESS_URL en la dirección https:// exacta que escribe en el navegador.
También aumente el límite de tamaño de carga del proxy. Un escaneo de 40 MB enviado mediante 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 de consumo
El archivo de Compose monta ./consume desde el directorio de Compose en el contenedor. Todo lo que coloque allí se importa y luego se elimina del directorio, porque el archivo pasa a estar en el volumen de medios administrado por paperless.
cp ~/scan-2026-07-14.pdf ~/paperless/consume/
docker compose logs -f webserverDebería ver que el consumidor detecta el nombre de archivo, ejecuta OCR y termina con una línea que indica que el documento se agregó. 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 subdirectorios, y PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=true convierte el nombre de cada subdirectorio en una etiqueta. Por lo 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 crear.
La detección es la otra parte. De forma predeterminada, PAPERLESS_CONSUMER_POLLING_INTERVAL es 0, lo que significa que paperless usa notificaciones del sistema de archivos del kernel, que se activan de inmediato. Esas notificaciones no atraviesan un sistema de archivos de red. Si el directorio de consumo 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 solucionarlo, establezca el intervalo en un número positivo de segundos para que paperless explore el directorio.
Idiomas de OCR y su costo
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 una VPS con vCPU compartida, esa diferencia puede hacer que un escaneo tarde diez segundos o un minuto. Incluya solo 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, agréguelo 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 durante el arranque, por lo que el primer arranque posterior a ese cambio es más lento.
Hacer 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. Paperless incluye su propio exportador, que escribe los documentos y un manifiesto JSON con todos los metadatos en el montaje bind ./export:
docker compose exec webserver document_exporter ../export --delete --no-progress-bar--delete elimina los archivos exportados que ya no coinciden con ningún documento actual, para que la carpeta siga siendo un espejo en lugar de crecer indefinidamente. --no-progress-bar mantiene limpia la salida cuando se ejecuta desde cron.
La restauración se realiza con document_importer sobre esa misma carpeta en una pila nueva. Por tanto, solo tiene que proteger el directorio de exportación. Envíelo fuera del sitio según una programación mediante copias de seguridad restic cifradas y con deduplicación 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 existe export/manifest.json y que el número de archivos coincide con el número de documentos de la interfaz. Una copia de seguridad que nunca ha probado mediante una lista no es una copia de seguridad.
FAQ
¿Por qué todas las páginas devuelven "Bad Request (400)" después de apuntar mi dominio a ellas?
Django rechazó el encabezado Host porque tu dominio no está incluido en ALLOWED_HOSTS. Define PAPERLESS_URL=https://paperless.example.com en docker-compose.env, sin una barra final, y luego ejecuta docker compose up -d para volver a crear el contenedor. Editar solo el archivo de entorno no tiene efecto, porque el contenedor en ejecución conserva el entorno con el que se inició.
Dejé un PDF en la carpeta de consumo y no pasó nada. ¿Cuál es el problema?
Comprueba primero docker compose logs webserver. Un error de permisos significa que USERMAP_UID y USERMAP_GID no coinciden con la cuenta propietaria del archivo. Corrige esos valores y vuelve 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. Define PAPERLESS_CONSUMER_POLLING_INTERVAL con un valor como 30 y paperless explorará la carpeta cada 30 segundos.
¿Puedo ejecutar paperless-ngx con SQLite en lugar de PostgreSQL?
Sí, docker-compose.sqlite.yml es compatible y usa menos memoria, por lo que resulta adecuado para un VPS pequeño. La desventaja aparece cuando crece el archivo: la búsqueda de texto completo y las modificaciones masivas de etiquetas se vuelven notablemente más lentas cuando hay miles de documentos. Migrar más adelante requiere una exportación y una importación. Por eso, elige PostgreSQL ahora si esperas que el archivo siga creciendo.
¿Cuánto espacio en disco necesita realmente un archivo de documentos escaneados?
Aproximadamente el doble del tamaño de los archivos de origen. Paperless conserva el original sin modificar y almacena un segundo PDF con OCR y una capa de texto consultable, además de miniaturas pequeñas. Un escaneo de solo texto de 200 KB ocupa poco espacio. Un escaneo en color de 30 MB de un contrato extenso ocupa unos 60 MB. Añade el directorio de exportación si lo conservas en el mismo disco. En ese caso, el mismo archivo ocupa tres veces ese espacio en disco.
¿Necesito los contenedores de Tika y Gotenberg?
Solo si quieres indexar archivos de Word, Excel u OpenDocument junto con tus PDF. Esos 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ítelos en un equipo pequeño si todo lo que archivas ya es un PDF o una imagen.