Skip to main content

Command Palette

Search for a command to run...

Deploying Paperless-ngx Open-Source Document Management System on Ubuntu 24.04

Step-by-step guide to deploy Paperless-ngx with PostgreSQL, Redis, Docker Compose, and Traefik on Ubuntu 24.04 for OCR-powered document search secured by automatic HTTPS.

Updated
3 min read
Deploying Paperless-ngx Open-Source Document Management System on Ubuntu 24.04
S
A Developer Advocate with a focus on improving the developer experience through clear communication, technical enablement, and community engagement.
A
DevOps Engineer with experience in Kubernetes, automation, cloud infrastructure, and observability. I work in Developer Relations, contribute to technical documentation, and collaborate on engineering-focused projects.

Paperless-ngx is an open-source document management system that converts scans and PDFs into a fully searchable archive using Tesseract OCR, with tags, custom fields, and automated processing rules. This guide deploys Paperless-ngx using Docker Compose with PostgreSQL, Redis, and Traefik handling automatic HTTPS, then uploads a document and verifies OCR extraction, following document management system deployment practices documented in Vultr Docs.


Set Up the Directory Structure

1. Create the project directories:

mkdir -p ~/paperless-ngx/{data,media,export,consume,pgdata}
cd ~/paperless-ngx

2. Create the environment file:

nano .env
DOMAIN=paperless.example.com
LETSENCRYPT_EMAIL=admin@example.com

PAPERLESS_SECRET_KEY=CHANGE_TO_RANDOM_STRING
PAPERLESS_URL=https://paperless.example.com
PAPERLESS_TIME_ZONE=UTC
PAPERLESS_OCR_LANGUAGE=eng
PAPERLESS_DBPASS=STRONG_PASSWORD_HERE

POSTGRES_DB=paperless
POSTGRES_USER=paperless
POSTGRES_PASSWORD=STRONG_PASSWORD_HERE

Use the same value for PAPERLESS_DBPASS and POSTGRES_PASSWORD. PAPERLESS_SECRET_KEY should be a 32+ character random string.


Deploy with Docker Compose

1. Create the Compose manifest:

nano docker-compose.yaml
services:
  traefik:
    image: traefik:v3.6
    container_name: traefik
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL}"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    restart: unless-stopped

  db:
    image: postgres:16
    container_name: paperless-db
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - "./pgdata:/var/lib/postgresql/data"
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "\({POSTGRES_USER}", "-d", "\){POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  redis:
    image: redis:7
    container_name: paperless-redis
    restart: unless-stopped

  paperless:
    image: ghcr.io/paperless-ngx/paperless-ngx:2.20.6
    container_name: paperless
    depends_on:
      db:
        condition: service_healthy
    environment:
      PAPERLESS_REDIS: redis://redis:6379
      PAPERLESS_DBHOST: db
      PAPERLESS_SECRET_KEY: ${PAPERLESS_SECRET_KEY}
      PAPERLESS_URL: ${PAPERLESS_URL}
      PAPERLESS_TIME_ZONE: ${PAPERLESS_TIME_ZONE}
      PAPERLESS_OCR_LANGUAGE: ${PAPERLESS_OCR_LANGUAGE}
      PAPERLESS_DBPASS: ${PAPERLESS_DBPASS}
    volumes:
      - "./data:/usr/src/paperless/data"
      - "./media:/usr/src/paperless/media"
      - "./export:/usr/src/paperless/export"
      - "./consume:/usr/src/paperless/consume"
    expose:
      - "8000"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.paperless.rule=Host(`${DOMAIN}`)"
      - "traefik.http.routers.paperless.entrypoints=websecure"
      - "traefik.http.routers.paperless.tls.certresolver=letsencrypt"
      - "traefik.http.services.paperless.loadbalancer.server.port=8000"
    restart: unless-stopped

2. Start the services:

docker compose up -d
docker compose ps
docker compose logs

3. Create a superuser inside the Paperless container:

docker compose exec -it paperless python3 manage.py createsuperuser

Follow the prompts for username, email, and password.


Upload a Document and Verify OCR

  1. Open https://paperless.example.com and sign in with the superuser.

  2. Click Upload documents at the top right and pick a PDF or image.

  3. Wait 5–30 seconds for Paperless to OCR the document.

  4. Click the thumbnail, then the Content tab — the extracted text appears alongside the document preview.


Next Steps

Paperless-ngx is running with PostgreSQL persistence and Tesseract OCR. From here you can:

  • Set up the Consume folder for automatic ingestion of scans dropped via SFTP or a scanner watch folder

  • Add tags, correspondents, document types, and storage paths for auto-classification

  • Enable additional OCR languages by adding ISO codes to PAPERLESS_OCR_LANGUAGE (e.g. eng+deu+fra)

For the full guide with additional tips, visit the original article on Vultr Docs.

The Self-Hosted Stack

Part 1 of 50

The Self-Hosted Stack is a developer-focused series exploring open-source tools you can deploy, run, and manage on your own infrastructure. From AI platforms and databases to developer tools, observability stacks, and authentication systems, each guide walks through deploying production-ready open-source software on Vultr cloud infrastructure.