Automatically rename PDFs based on their contents using OCR and Claude AI.
Drop any PDF into the watched folder and it gets renamed to something like 2025-03-27 Cadence Bank Lien Release.pdf and moved into a named/ subdirectory. Files that can't be processed (blank pages, no readable text) are moved to failed/.
- Watches a folder for any PDF files
- Uses OCR (Tesseract) to extract text from the pages
- Sends the text to Claude AI to determine a descriptive filename with the document date
- Moves the renamed file into the
named/subdirectory - Files that can't be read or named are moved to
failed/
mkdir -p /volume1/docker/scan-namer
cd /volume1/docker/scan-namerservices:
scan-namer:
image: ghcr.io/NolanT/scan-namer:main
container_name: scan-namer
restart: unless-stopped
environment:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
volumes:
- /volume1/your-scan-folder:/scansReplace /volume1/your-scan-folder with your actual scan folder path.
echo "ANTHROPIC_API_KEY=sk-ant-api03-your-key-here" > .envGet an API key at console.anthropic.com.
docker compose up -ddocker compose logs -fOr check scan_namer.log in your scan folder.
If you prefer to build the image yourself instead of pulling from GHCR:
git clone https://github.com/NolanT/scan-namer.git
cd scan-namer
docker compose up -d --buildOr edit docker-compose.yml — comment out image: and uncomment build: ..
This repo includes a GitHub Actions workflow (.github/workflows/build-and-publish.yml) that automatically builds and publishes the Docker image to GitHub Container Registry.
What triggers a build:
- Push to
mainbranch - Creating a version tag (e.g.,
v1.0.0) - Pull requests (build only, no push)
Multi-architecture: Images are built for both linux/amd64 and linux/arm64, so they work on Synology NAS models with Intel or ARM processors.
To set up:
- Push this repo to GitHub
- Go to Settings > Actions > General and ensure "Read and write permissions" is enabled under "Workflow permissions"
- Push a commit or tag — the image will be published to
ghcr.io/nolant/scan-namer - The package may initially be private. To make it public: go to your GitHub profile > Packages > scan-namer > Package settings > Change visibility
Pulling a specific version:
# Latest from main branch
docker pull ghcr.io/NolanT/scan-namer:main
# Specific release
docker pull ghcr.io/NolanT/scan-namer:1.0.0
# Specific commit
docker pull ghcr.io/NolanT/scan-namer:sha-abc1234Runs continuously, processing existing PDFs on startup then checking for new ones every 30 seconds:
docker compose up -dProcess all current PDFs and exit:
# In docker-compose.yml, add:
command: ["/scans"]Preview what would be renamed without actually renaming:
command: ["/scans", "--dry-run"]command: ["/scans", "--watch", "--settle-time", "15", "--watch-interval", "60", "--model", "claude-sonnet-4-5"]| Flag | Default | Description |
|---|---|---|
--watch |
off | Watch folder continuously for new files |
--dry-run |
off | Preview renames without executing |
--model |
claude-haiku-4-5 |
Claude model (~$0.001/file with Haiku) |
--dpi |
200 |
OCR resolution (higher = more accurate, slower) |
--settle-time |
10 |
Seconds to wait for file to finish writing |
--watch-interval |
30 |
Seconds between folder checks |
--verbose |
off | Show debug output |
Using Claude Haiku (default), each file costs roughly $0.001 to process. A batch of 100 files would cost about $0.10.
After processing, your scan folder will look like:
/scans/
named/ # Successfully renamed files
2025-03-27 Cadence Bank Lien Release.pdf
2025-01-15 Chase Credit Card Statement.pdf
failed/ # Files that couldn't be processed
blank_page.pdf
scan_namer.log # Processing log
new_scan.pdf # Unprocessed files waiting in root
Only PDFs in the root of the watched folder are processed. Files already in named/ or failed/ are left alone.
No text extracted: The PDF might be blank or contain only photos. These are moved to failed/.
Wrong date picked: Claude uses the document's content date (invoice date, letter date, etc.), not the scan date. Occasionally OCR errors can cause misreads — check the log for the OCR text.
Container won't start: Make sure your .env file has a valid ANTHROPIC_API_KEY and the volume path in docker-compose.yml exists on your Synology.
GHCR image is private: After the first build, go to GitHub > your profile > Packages > scan-namer > Package settings > Change visibility to Public.