Skip to content

Installation

Paperless Starfruit ships as one container: a Node process plus a volume for its SQLite database. No Postgres, no Redis. Pre-built images are published to GitHub Container Registry, so there is nothing to clone or build:

ghcr.io/sabbaken/paperless-starfruit/api:latest # or a release version, e.g. :1.1.0, to pin

All you need is Docker with Compose v2. Provider keys can be added later from the UI.

Two compose files, depending on where you start:

Save as docker-compose.yml (this is docker/docker-compose.yml from the repo, shown verbatim):

docker-compose.yml
# Standalone compose for Paperless Starfruit: a single container, no external
# services. Uses the pre-built image from ghcr.io, so this file works on its
# own: no repo checkout or local build needed. The SQLite DB lives on the named
# volume; the paperless connection and provider keys are configured from the
# web UI (not here).
#
# ENCRYPTION_KEY is the ONE required secret: it encrypts stored credentials and
# signs admin sessions, so it must be set and kept stable (rotating it logs the
# admin out and makes saved credentials undecryptable). Provide it via the shell
# or a `.env` file next to this compose file. Generate one with:
# openssl rand -base64 32
services:
paperless-starfruit:
# Published by CI on every release (root package.json version bump). Tags:
# `latest`, or the release version (e.g. `1.1.0`) to pin. To build from a
# repo checkout instead, replace `image:` with:
# build:
# context: ..
# dockerfile: docker/Dockerfile
image: ghcr.io/sabbaken/paperless-starfruit/api:latest
labels:
# Opt this container in to the Watchtower auto-updater defined below.
# Remove this label to keep the container on its current image.
- 'com.centurylinklabs.watchtower.enable=true'
ports:
# Host port 7827 ("star" on a phone keypad), deliberately uncommon so
# this file works as-is, unlike 3000 which is usually taken. The app
# inside the container still listens on 3000.
- '7827:3000'
volumes:
- paperless_starfruit_data:/data
environment:
DATABASE_PATH: /data/app.db
ENCRYPTION_KEY: ${ENCRYPTION_KEY:?ENCRYPTION_KEY must be set, see the comment above}
# Optional: lock CORS to specific origins (comma-separated). Unset reflects
# the request origin, which is fine since auth is a bearer token, not a cookie.
# CORS_ORIGIN: https://paperless-ai.home.lan
restart: unless-stopped
# Auto-updates (optional). Watchtower watches the registry and, when a newer
# image is published, pulls it and recreates the container with the same
# config. `--label-enable` means it only touches containers carrying the
# watchtower label above, so nothing else on the host (e.g. your existing
# paperless-ngx stack) is affected. Delete this whole service to opt out.
watchtower:
image: nickfedor/watchtower:latest # maintained fork (containrrr/watchtower is archived)
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --label-enable --cleanup --interval 300
# --label-enable only update containers with the enable label (see above)
# --cleanup delete the old image after a successful update
# --interval 300 check the registry every 5 min (seconds; raise for fewer checks)
# To run alongside an existing paperless-ngx stack, attach this service to that
# stack's Docker network so it can reach paperless by its service name. Find the
# network with `docker network ls` (usually "<paperless-project>_default"), then:
#
# services:
# paperless-starfruit:
# networks: [default, paperless]
# networks:
# paperless:
# external: true
# name: paperless_default # <- your paperless stack's network
#
# Then in the web UI set the paperless URL to the in-network service name, e.g.
# http://webserver:8000, NOT http://localhost:8000 (localhost is this container).
volumes:
paperless_starfruit_data:

Put the one required secret in a .env next to it and start:

Terminal window
echo "ENCRYPTION_KEY=$(openssl rand -base64 32)" > .env
docker compose up -d

The container has to reach paperless over the network:

  • If paperless has a LAN address or published port, just enter that URL in the UI later, e.g. http://192.168.1.10:8000 or https://paperless.home.lan.

  • To use the Docker service name instead, attach the container to your paperless stack’s network. Find it with docker network ls (usually <project>_default), then:

    services:
    paperless-starfruit:
    networks: [default, paperless]
    networks:
    paperless:
    external: true
    name: paperless_default # your paperless stack's network

    and use http://webserver:8000 as the paperless URL, not localhost, which is this container.

Then open http://localhost:7827. The container serves the API and the web UI on the same port. On first run the UI shows a “create admin” card; once you set the admin username and password, registration closes and only login works. There is no default password.

These are the only runtime variables. Everything else lives in the UI.

Variable Required Default Purpose
ENCRYPTION_KEY yes (none) 32-byte key (base64 or hex). Encrypts stored credentials and signs admin sessions.
DATABASE_PATH no /data/app.db SQLite file path. Its directory is created if missing.
PORT no 3000 Port the app listens on inside the container (the compose files publish it as 7827).
CORS_ORIGIN no reflect origin Comma-separated allowed origins. Leave unset unless you want to lock it down.
  • Persistence. Everything (config, prompts, job queue, review items, audit history) is one SQLite file at /data/app.db on a named volume. Back that volume up and you have a complete snapshot.
  • Reverse proxy. Auth is a bearer token, not a cookie, so no extra CORS or cookie configuration is needed. Put your TLS terminator (Caddy, Traefik, nginx) in front of the published port (7827 by default).
  • Health. GET /api/health backs the image’s HEALTHCHECK. Migrations apply automatically on boot; there is no separate migrate step.
  • Automatic updates. The compose files include a Watchtower service that checks the registry and, when a newer image is published, pulls it and recreates the Starfruit container in place. It only updates containers carrying the com.centurylinklabs.watchtower.enable=true label, so nothing else on the host is touched. Remove that label (or the watchtower service) to stay on a fixed image, and pin a version tag like :1.1.0 instead of :latest if you’d rather upgrade deliberately.
  • Building from source (optional). Clone the repo and swap image: for the commented build: block in docker/docker-compose.yml.

Connect your paperless instance →