Bitflake logo

Self-host Paperless-ngx with Docker Compose

Self-hosted document management for turning paperwork into a searchable archive.

paperless-ngx

gotenberg

gotenberg/gotenberg:8.25

postgres

postgres:18.3-alpine3.23

postgres-postgres-data-init

busybox

postgres-postgres-socket-init

busybox

redis

redis:7

redis-redis-data-init

busybox

tika

apache/tika:latest

The steps below take a few minutes end to end.

  1. Docker compose setup and file
    save the generated compose and env files.
  2. Secret generation
    create the random passwords Paperless-ngx needs.
  3. Start Paperless-ngx with Docker Compose
    bring the stack up and check it's healthy.

Requirements

You'll need these installed on the machine you're deploying to:

Docker

Packages Paperless-ngx and everything it depends on into an isolated container, so it runs the same way on your machine as it does everywhere else.

Install guide

Docker Compose

Reads docker-compose.ymland starts everything in it together. Ships with Docker Desktop. On Linux servers it's usually a separate install.

Install guide

1. Docker compose setup and file

Create a folder for Paperless-ngx and save the file below into it as docker-compose.yml. It describes every container the stack needs — Paperless-ngxitself and any supporting services, such as its database — along with the ports, volumes and environment variables each one uses. You'll also need an empty .env file in the same folder; Docker Compose reads it automatically and uses it to fill in the ${VARIABLE}references you'll see in the file below.

docker-compose.yml

version: "3.9"
services:
  gotenberg:
    image: gotenberg/gotenberg:8.25
    restart: unless-stopped
    network_mode: service:paperless-ngx
    command:
      - gotenberg
      - --chromium-disable-javascript=true
      - --chromium-allow-list=file:///tmp/.*
  paperless-ngx:
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
    restart: unless-stopped
    ports:
      - 8000:8000
    environment:
      PAPERLESS_ADMIN_MAIL: admin@bitflake.app
      PAPERLESS_ADMIN_PASSWORD: Admin123
      PAPERLESS_ADMIN_USER: admin
      PAPERLESS_DBHOST: postgres
      PAPERLESS_DBNAME: app
      PAPERLESS_DBPASS: ${RANDOM_PG_PASSWORD}
      PAPERLESS_DBPORT: "5432"
      PAPERLESS_DBUSER: app
      PAPERLESS_REDIS: redis://:${RANDOM_REDIS_PASSWORD}@redis:6379/0
      PAPERLESS_SECRET_KEY: ${RANDOM_PAPERLESS_SECRET_KEY}
      PAPERLESS_TIKA_ENABLED: "1"
      PAPERLESS_TIKA_ENDPOINT: http://localhost:9998
      PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://localhost:3000
      PAPERLESS_URL: http://paperless-ngx.localhost:8000
      RANDOM_REDIS_PASSWORD: ${RANDOM_REDIS_PASSWORD}
    volumes:
      - data:/usr/src/paperless/data
      - media:/usr/src/paperless/media
    depends_on:
      postgres:
        condition: service_started
      redis:
        condition: service_started
  postgres:
    image: postgres:18.3-alpine3.23
    restart: unless-stopped
    environment:
      PGDATA: /var/lib/postgresql/data
      POSTGRES_PASSWORD: ${RANDOM_PG_PASSWORD}
      POSTGRES_USER: app
    volumes:
      - postgres-data:/var/lib/postgresql/data
      - postgres-socket:/var/run/postgresql
    depends_on:
      postgres-postgres-data-init:
        condition: service_completed_successfully
      postgres-postgres-socket-init:
        condition: service_completed_successfully
  postgres-postgres-data-init:
    image: busybox
    volumes:
      - postgres-data:/var/lib/postgresql/data
    command:
      - chown
      - 70:70
      - /var/lib/postgresql/data
  postgres-postgres-socket-init:
    image: busybox
    volumes:
      - postgres-socket:/var/run/postgresql
    command:
      - chown
      - 70:70
      - /var/run/postgresql
  redis:
    image: redis:7
    restart: unless-stopped
    environment:
      REDIS_PASSWORD: ${RANDOM_REDIS_PASSWORD}
    volumes:
      - redis-data:/data
    depends_on:
      redis-redis-data-init:
        condition: service_completed_successfully
    entrypoint:
      - sh
      - -c
    command:
      - redis-server --requirepass $$REDIS_PASSWORD
  redis-redis-data-init:
    image: busybox
    volumes:
      - redis-data:/data
    command:
      - chown
      - 999:999
      - /data
  tika:
    image: apache/tika:latest
    restart: unless-stopped
    network_mode: service:paperless-ngx
    volumes:
      - tika-tmp:/tmp
volumes:
  data: null
  media: null
  postgres-data: null
  postgres-socket: null
  redis-data: null
  tika-tmp: null

Volumes

Paperless-ngx stores its data in named Docker volumes, so it survives container restarts and updates:

  • data mounted at /usr/src/paperless/data: Search index, classification model, and application logs
  • media mounted at /usr/src/paperless/media: Original and archived (OCR'd PDF/A) versions of consumed documents

2. Secret generation

Paperless-ngx needs a few randomly generated secrets — for example, database passwords or an internal session key — before it can start. These are referenced from docker-compose.ymlabove but don't live in it, so they need to end up in your .env file. Pick one of the two options below.

Generating secrets…

3. Start Paperless-ngx with Docker Compose

From the folder with docker-compose.yml and .env, run:

Terminal

Start Paperless-ngx and all its supporting services in the background.

docker compose up -d

The -d flag runs the stack in the background so it keeps running after you close the terminal. Docker will pull the images the first time, which can take a minute or two.

To check on it afterwards: docker compose ps shows whether containers are healthy, and docker compose logs -f follows their logs if something looks wrong. Once it's running, open http://localhost:8000 in your browser.