FileFolio helps privacy-conscious professionals keep large PDF collections searchable and organized using local AI. No cloud, no telemetry, all on your machine.
Status: Actively maintained, used on my own 1,000+ PDF collection. Expect breaking changes before v1.0, but I'm responsive to issues and feedback.
- You have hundreds of PDF bills, reports, or research papers scattered in folders.
- You care about privacy and do not want to upload them to cloud AI services.
- You still want smart search, auto-tagging, and reasonable file names.
FileFolio watches a folder, uses a local LLM via Ollama to analyze each PDF, and keeps everything searchable in one interface.
Paperless-ngx is the most popular self-hosted alternative. Here's how they compare:
| Feature | FileFolio | Paperless-ngx |
|---|---|---|
| AI tagging and naming | 🟢 Local LLM via Ollama, zero config | 🔴 ML classifier available, but requires manual training; no LLM-level understanding |
| Setup | 🟢 Single Python process + SQLite | 🔴 Docker Compose: web, worker, Redis, PostgreSQL |
| Resource footprint | 🟢 Single process + Ollama | 🔴 Multi-service, heavier |
| Multi-user | 🔴 No | 🟢 Yes |
| Feature scope | 🔴 Focused: upload, search, tag, organize | 🟢 Broader: email ingestion, custom fields, workflow automation |
| Best for | 🟢 Personal libraries, privacy-first, low setup | 🟢 Power users, teams, complex workflows |
- Automatic organization – drop PDFs in a watched folder and Ollama names, tags, and categorizes them automatically
- Privacy-first – every byte stays on your machine, no cloud calls, no telemetry
- Fast retrieval – full-text search across content and metadata with thumbnail previews
- Disaster-proof – back up and restore your entire library as a single ZIP
- Git
- Python 3.10+
- Ubuntu/Debian:
apt-get install git python3-venv(Python there ships withoutvenvandpip, so step 2 of the manual setup fails without it)
- Ubuntu/Debian:
- Ollama installed locally, with a model pulled:
Without a model, FileFolio still files and searches your documents, but tagging and naming fall back to simple keyword rules. See Custom Ollama model to use a different one.
ollama pull llama3.2
- Poppler (for PDF processing)
- macOS:
brew install poppler - Ubuntu/Debian:
apt-get install poppler-utils - Windows: download the latest
Release-*.zipfrom poppler releases, extract it (e.g. toC:\poppler) and add itsLibrary\binfolder to PATH
- macOS:
- Tesseract (for OCR on scanned documents)
- macOS:
brew install tesseract - Ubuntu/Debian:
apt-get install tesseract-ocr - Windows: run the installer from Tesseract releases,
then add
C:\Program Files\Tesseract-OCRto PATH
- macOS:
- Windows only: Poppler, Tesseract and Ghostscript (see OCR dependencies)
are not added to PATH for you. Open Start, search for "Edit environment variables for
your account", select
Path, click New for each folder, then open a new terminal.
Uploaded PDFs are filed by category and year:
uploads/
Invoice/
2026/
20260731_101500_bill.pdf
Receipt/
2025/
20251102_090000_lidl.pdf
Changing a document's category in the interface moves the file to the matching folder.
An existing flat uploads/ directory is reorganised automatically the next time FileFolio
starts.
Thumbnails stay flat in thumbnails/ — they are generated artifacts, not files you browse.
- Clone the repository
git clone https://github.com/imkrishsub/filefolio.git
cd filefolio- Start Ollama and pull the model (if not already done)
ollama serve
ollama pull llama3.2- Start FileFolio
docker compose up- Open your browser Navigate to: http://localhost:8000
Ollama runs on your host machine; the container connects to it automatically via host.docker.internal. On Linux, set OLLAMA_HOST=http://172.17.0.1:11434 if host.docker.internal is not available.
- Clone the repository
git clone https://github.com/imkrishsub/filefolio.git
cd filefolio- Create and activate virtual environment
macOS and Linux:
python3 -m venv venv
source venv/bin/activateWindows (PowerShell or Command Prompt):
python -m venv venv
venv\Scripts\activateIf PowerShell refuses with "running scripts is disabled on this system", run
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned once, or use Command Prompt.
- Install dependencies
pip install -r requirements.txt- Start Ollama and pull the model (in a separate terminal)
ollama serve
ollama pull llama3.2- Run the application
python backend/main.py- Open your browser Navigate to: http://127.0.0.1:8000
Set a custom port using the PORT environment variable:
PORT=8080 python backend/main.py
# or with Docker:
PORT=8080 docker compose upFileFolio uses llama3.2 by default. Point it at any model you have pulled:
OLLAMA_MODEL=qwen2.5 python backend/main.py
# or with Docker:
OLLAMA_MODEL=qwen2.5 docker compose upIf Ollama is unreachable or the configured model is not installed, FileFolio shows a banner in the interface and falls back to rule-based tagging rather than failing the upload.
OLLAMA_HOST=http://192.168.1.10:11434 docker compose upThe container runs in UTC unless told otherwise, so upload times and the date
prefix of stored filenames would be off by your offset. Set TZ to your
time zone name.
The easiest way on every platform is a file named .env next to
docker-compose.yml, which Docker Compose reads automatically. Put this line in
it, then restart with docker compose up -d:
TZ=Europe/Berlin
A manual install uses your computer's time zone and needs no setting.
pytestFull API and functionality coverage including unit tests, integration tests, and frontend tests.
Same operations as the web UI, against a running instance. Needs only the
normal venv/ — no MCP SDK.
python backend/cli.py search rent --category Invoice
python backend/cli.py get 7
python backend/cli.py upload ~/Downloads/receipt.pdf
python backend/cli.py update 7 --tags finance,rent --category Tax
python backend/cli.py download 7 ~/Desktop/rent.pdfAdd --json to search, get, upload, or update to get the raw API
response for scripting. Errors go to stderr and exit non-zero. Set
FILEFOLIO_URL to reach a non-default instance:
FILEFOLIO_URL=http://127.0.0.1:8080 python backend/cli.py search rentInstalling the package (pip install -e .) gives you a bare filefolio
command that does the same thing from any directory.
Lets other Claude Code sessions search, read, add, and re-tag documents in a running FileFolio instance over the Model Context Protocol — without needing to be in this repo's directory.
The MCP server needs its own virtual environment: the mcp SDK requires newer
anyio/starlette versions than FileFolio's pinned FastAPI supports, so it can't
share venv/ with the backend.
- Create the MCP virtual environment and install its dependencies
python3 -m venv venv-mcp
venv-mcp/bin/pip install -r requirements-mcp.txt # On Windows: venv-mcp\Scripts\pip install -r requirements-mcp.txt-
Register it with Claude Code, with FileFolio already running (
python backend/main.py). Run this from the FileFolio repo root so the paths resolve to absolute paths —claude mcp addneeds them, since the server may be invoked from any directory:macOS/Linux (bash/zsh):
claude mcp add filefolio -s user -- "$(pwd)/venv-mcp/bin/python" "$(pwd)/backend/mcp_server.py"Windows (PowerShell):
claude mcp add filefolio -s user -- "$PWD\venv-mcp\Scripts\python.exe" "$PWD\backend\mcp_server.py"-
Optional: point it at a non-default FileFolio instance
macOS/Linux (bash/zsh):
claude mcp add filefolio -s user -e FILEFOLIO_URL=http://127.0.0.1:8080 -- "$(pwd)/venv-mcp/bin/python" "$(pwd)/backend/mcp_server.py"Windows (PowerShell):
claude mcp add filefolio -s user -e FILEFOLIO_URL=http://127.0.0.1:8080 -- "$PWD\venv-mcp\Scripts\python.exe" "$PWD\backend\mcp_server.py"Available tools: filefolio_search, filefolio_get_document, filefolio_download,
filefolio_upload, filefolio_update, filefolio_pdf_merge, filefolio_pdf_split,
filefolio_pdf_extract, filefolio_pdf_delete_pages, filefolio_pdf_rotate,
filefolio_pdf_ocr.
FileFolio can merge, split, rotate, OCR, and trim pages from stored PDFs. The same operations are reachable from the web UI, the command line, and the MCP server.
Select two or more documents and a Merge button (a floating action button) files them into one new document. Each document also has a Tools menu with Rotate, Split, Extract pages, Delete pages, and Make searchable. The operations that produce a new document offer a "Download only, do not file" checkbox.
filefolio pdf merge 12 15 3
filefolio pdf split 12 "1-3,4-"
filefolio pdf extract 12 "2"
filefolio pdf delete-pages 12 "1"
filefolio pdf rotate 12 --degrees 90
filefolio pdf ocr 12Page ranges accept comma-separated single pages and spans, with an open-ended
upper bound ("1-3,5,8-"). Add --download OUT.pdf (merge, extract, delete-pages)
or --download-dir DIR (split) to save the result instead of filing it, and
--json to print the raw API payload.
filefolio_pdf_merge— merge documents into one new filed document.filefolio_pdf_split— split a document by page ranges; files each part.filefolio_pdf_extract— keep only the given pages as a new document.filefolio_pdf_delete_pages— drop the given pages and file the rest.filefolio_pdf_rotate— rotate pages 90, 180, or 270 degrees in place.filefolio_pdf_ocr— add a searchable text layer in place.
Merge, split, extract, and delete-pages create new documents that are filed through the same AI tagging pipeline as uploads (or, with the download option, just hand you the file). Rotate and Make searchable (OCR) edit the document in place, keeping its id, tags, category, and upload date.
Split files its parts one at a time. If a later part fails to file (for example because it duplicates an existing document), the earlier parts stay filed and the response is the error for the failing part.
Make searchable / filefolio pdf ocr uses ocrmypdf, which requirements.txt
installs into the virtual environment. FileFolio runs it with its own Python
interpreter, so the virtual environment does not have to be activated. It also
needs Tesseract (see Prerequisites) and Ghostscript on PATH.
The Docker image bundles them.
- macOS:
brew install ghostscript - Debian/Ubuntu:
apt-get install ghostscript - Windows: install Ghostscript from ghostscript.com,
then add its
binfolder (C:\Program Files\gs\gs<version>\bin) to PATH
filefolio/
├── backend/
│ ├── main.py # FastAPI server
│ ├── client.py # HTTP client shared by the CLI and MCP server
│ ├── cli.py # Command line
│ ├── mcp_server.py # MCP server (own venv, see below)
│ └── sync_service.py # Folder sync service
├── frontend/
│ ├── static/
│ │ ├── app.js # Frontend JavaScript
│ │ ├── style.css # Styles
│ │ └── i18n.json # Translations
│ └── templates/
│ └── index.html # Main interface
├── tests/ # Test suite
├── uploads/ # PDF storage (created on first run)
├── thumbnails/ # Document thumbnails (created on first run)
├── data/ # Database (created on first run)
├── setup.cfg # Linting and tool configuration
├── pytest.ini # Test configuration
├── requirements.txt
└── requirements-mcp.txt # MCP server dependencies (separate venv-mcp/)
- Upload - Drag and drop a PDF file into the web interface, or sync a local folder to automatically import new files
- Extract - Text is extracted from the PDF (with OCR fallback for scanned documents)
- Analyze - A local LLM analyzes the content to determine category, tags, and suggest a filename.
Suggested names follow one pattern: issuer, document type, and the month the document was
issued, e.g.
stadtwerke-musterstadt-electricity-bill-september-2026.pdf. Invoice numbers, amounts and other identifiers are left out - Organize - The document is saved with metadata in a local SQLite database. The file on disk keeps its original name; the suggested name is stored alongside it and is what you see in the interface, so nothing is lost if a suggestion is wrong. Every suggestion is editable
- Search - Find documents by content, category, tags, or filename
- Backend: FastAPI (Python)
- Frontend: Vanilla JavaScript
- Database: SQLite
- AI/LLM: Ollama
- PDF Processing: PyPDF, pdf2image, pytesseract
- Styling: Custom CSS
Contributions are welcome! Please feel free to submit a pull request or open an issue.
MIT License - see LICENSE file for details.
