Paperless Sync is a native Nextcloud app that mirrors finalized Paperless-ngx documents into a structured Nextcloud archive and can optionally submit files from a Nextcloud inbox to Paperless.
Paperless remains the source of truth. Nextcloud provides convenient access through Files, desktop and mobile clients, sharing, and its viewer.
- Native Nextcloud filesystem operations without WebDAV credentials
- Configurable target user and base, archive, inbox, error, and deleted folders
- Configurable archive path template
- Correspondent-first folder hierarchy by default
- Archive PDF or original-file export
- Metadata-aware renames and moves
- Paperless inbox detection and configurable excluded tags, including removal of previously mirrored copies
- Recursive Nextcloud inbox import with Paperless task tracking
- Paperless trash mirroring and optional permanent deletion
- Configurable missing-document confirmation runs
- Empty-folder pruning
- Conflict policy, batch size, and interval controls
- Dry-run, manual execution, status, and error summaries
- Server-side token storage through Nextcloud's credentials manager
- Automated semantic releases and signed App Store packages
After installation, open Administration settings → Paperless Sync. Configure and test the connection while synchronization remains disabled. Run a dry-run, review the summary, and only then enable scheduled synchronization.
The default archive path template is:
{{ correspondent }}/{{ document_type }}/{{ created_year }}/{{ created }} - {{ title }} [P{{ id }}]{{ extension }}
This produces paths such as:
Dokumente/Paperless/Archiv/Example GmbH/Invoice/2026/2026-08-26 - Example invoice [P123].pdf
Stable markers such as [P123] are compatible with the independent Paperless Unified Search app.
Use a dedicated Paperless service account. It needs view and download access to every document that should be exported. Enable documents.add_document only when Nextcloud inbox import is used. Trash synchronization requires visibility of the corresponding trashed documents.
The API token is stored in Nextcloud's credentials manager and never returned to the browser.
The configured Nextcloud target user owns the synchronized folders. The app operates through Nextcloud's internal filesystem API and therefore does not need that user's password or an app password.
{{ id }}{{ title }}{{ correspondent }}{{ document_type }}{{ storage_path }}{{ created }},{{ created_year }},{{ created_month }}{{ added }},{{ added_year }}{{ original_filename }}{{ extension }}
The template must contain {{ id }}. Path components are normalized and sanitized for Nextcloud, macOS, and Windows clients.
Moving Paperless documents to its trash can be mirrored into the configured _Gelöscht folder. Permanent Nextcloud deletion is disabled by default. When enabled, a document must be absent from both the active Paperless API and its trash for the configured number of consecutive complete scans.
The app uses Nextcloud's native cron scheduler. System cron must run reliably. The configured interval is enforced by the app; each run limits modifications to the configured batch size.
Unit tests cover path generation, state transitions, export, metadata moves, exclusions, trash handling, guarded deletion, inbox success and failure, dry-run behavior, and release version management.
The Docker end-to-end suite mounts this checkout into real Nextcloud containers and uses a deterministic Paperless API mock:
bash tests/e2e/run.shCI runs the suite against Nextcloud 33 and the current stable Nextcloud 34 release. See tests/e2e/README.md for details and tests/e2e/MANUAL_ACCEPTANCE_TESTS.md for the release matrix.
Requirements: PHP 8.2+, Composer, Node.js, Docker, Krankerl, and a Nextcloud 33+ development instance.
composer install
composer lint
composer l10n:check
composer test
composer cs:check
composer psalm
composer version:check
bash tests/e2e/run.sh
krankerl package
composer package:checkThe app ID is paperless_sync and the PHP namespace is OCA\PaperlessSync. Psalm analyzes the PHP code, CodeQL scans the JavaScript, and the SBOM workflow continuously inventories dependencies.
The protected main branch accepts changes only through pull requests after CI, dependency review, Docker E2E, CodeQL, SBOM generation, and secret scanning succeed. Dependabot checks Composer, GitHub Actions, and Docker Compose weekly. Grouped patch and minor updates are queued for automatic squash merge only after those protected checks pass; Nextcloud major compatibility changes and releases remain manual. Dependency maintenance never starts a release.
The manually dispatched Release workflow accepts patch, minor, or major. It validates the project, prepares a signed-off version commit on release/vX.Y.Z, opens a protected pull request through a repository-scoped GitHub App, and waits for every required pull-request check and GitHub auto-merge. Only the exact merged commit is then built, package-checked, signed, supplied with a detached signature, SPDX SBOM, and public Sigstore provenance, tagged, published as a GitHub release with every verification asset, and submitted to the Nextcloud App Store.
An interrupted run resumes an existing release branch, merged release PR, tag, or incomplete GitHub release instead of incrementing again.
Release pull requests use a short-lived GitHub App installation token limited to the current repository and to Contents and Pull requests write access. The token is revoked when the job finishes. The App client ID is stored as the RELEASE_AUTOMATION_CLIENT_ID repository variable; its private key is stored only as the protected RELEASE_AUTOMATION_PRIVATE_KEY environment secret.
Dependency Review blocks newly introduced vulnerable or unapproved dependencies. OpenSSF Scorecard audits the repository's supply-chain security every week.
Private signing material and App Store credentials exist only as protected GitHub environment secrets and are never committed.
Project decisions and support expectations are documented in GOVERNANCE.md, SUPPORT.md, and CODE_OF_CONDUCT.md.
AGPL-3.0-or-later