Dockerized script that automatically OCRs PDF documents and syncs them to cloud storage
6.0K
Dockerized OCR processor with AI-powered document detection. Automatically processes PDFs, detects and splits multi-document scans, and syncs to cloud storage.
Key Features:
2025-01-15_Comcast_Bill.pdf)# Create directory structure
mkdir -p ~/pdfautomagic/{unprocessed,config}
cd ~/pdfautomagic
# Configure cloud storage
rclone config --config config/rclone.conf
# Follow prompts for Dropbox, Google Drive, S3, etc.
# Create .env file
cat > .env << 'EOF'
SCAN_DIR=./
CONFIG_DIR=./config
RCLONE_REMOTE=YourRemote:Path/To/Folder
TIMEZONE=America/Chicago
PUID=1000
PGID=1000
EOF
# Download compose file
curl -O https://raw.githubusercontent.com/csmarshall/docker-pdfautomagic/main/docker-compose.yml
# Start the service (AI model is included - no extra setup needed)
docker compose up -d
Drop PDFs in ~/pdfautomagic/unprocessed/ and watch them get processed and synced!
| Tag | Description | Size |
|---|---|---|
latest | Full featured - OCR + AI detection + smart naming (model included) | ~7GB |
latest-lite | OCR only, no AI features | ~800MB |
The latest image includes the vision AI model baked in - no additional setup required. See ADR-010 for details on this design decision.
The full image uses a vision AI model for document detection:
No GPU? AI detection will automatically fall back to CPU with a prominent warning. CPU processing is ~10x slower.
If you don't have a GPU and don't want slow CPU processing:
docker compose -f docker-compose.lite.yml up -d (OCR only, no AI)ENABLE_DETECTION=false in .envIf you don't need AI detection or don't have a GPU:
docker compose -f docker-compose.lite.yml up -d
Or change your docker-compose.yml:
image: chasmarshall/pdfautomagic:latest-lite
Scan a stack of mail as a single PDF. PDFAutomagic uses vision AI to:
Enable in .env:
ENABLE_DETECTION=true
Optionally extract document info for intelligent file naming:
ENABLE_CLASSIFICATION=true
Results in filenames like:
2025-01-15_Comcast_Bill_acct1234.pdf2025-01-12_IRS_Tax_Notice_1234567.pdf2025-01-10_Bank_Statement.pdfIf AI detection fails repeatedly (e.g. the GPU driver drops out or the model
won't load), PDFAutomagic doesn't get stuck retrying on every scan. After
DETECTION_FAILURE_THRESHOLD consecutive failed runs (default 3), detection
auto-disables and processing falls back to plain OCR so documents keep
flowing. It auto-recovers the moment a GPU re-check passes, and the warning
is rate-limited by DETECTION_BACKOFF_MINUTES so logs/notifications aren't
spammed. Post-scan hooks receive SCAN_STATUS and DETECTION_AUTO_DISABLED so
you can be notified. To reset manually, delete the state file inside the
container: /tmp/pdfautomagic.detection-failures.
YYYY/MM/DD/| Variable | Default | Description |
|---|---|---|
SCAN_DIR | - | Directory containing unprocessed/, processed/, originals/ |
CONFIG_DIR | - | Directory with rclone.conf and post-scan-commands/ |
RCLONE_REMOTE | - | Cloud destination (e.g., Dropbox:Documents) |
TIMEZONE | UTC | Container timezone |
PUID / PGID | 1000 | User/group ID for file ownership |
INTERVAL_MINUTES | 1 | How often to check for new files |
MAX_PARALLEL_JOBS | 1 | Number of PDFs to process simultaneously |
MINUTES_OLD | 1 | Only process files older than this (minutes) |
| OCR Settings | ||
ROTATE_PAGES_THRESHOLD | 7.5 | Confidence threshold for auto-rotation (0-15, lower=more aggressive) |
| AI Detection | ||
ENABLE_DETECTION | true | Enable AI document detection |
ENABLE_CLASSIFICATION | true | Enable smart document naming |
| Detection Reliability | ||
DETECTION_FAILURE_THRESHOLD | 3 | Consecutive failed runs before detection auto-disables and falls back to plain OCR (0 disables this safety net) |
DETECTION_BACKOFF_MINUTES | 15 | Once auto-disabled, minimum gap between re-logging the warning (avoids spamming a per-minute loop) |
Create executable scripts in {CONFIG_DIR}/post-scan-commands/ that run after processing.
Available environment variables:
# Core variables
$FILES_PROCESSED # Number of input PDF files
$RCLONE_REMOTE # Cloud storage destination
$OUTPUT_DIR # Where processed PDFs were saved
$ORIGINALS_DIR # Where original PDFs were moved
$SCAN_DIR # Base scan directory
# Timestamps
$TZ # Container timezone (e.g., America/Chicago)
$DATE # ISO date (YYYY-MM-DD)
$TIME # ISO time (HH:MM:SS)
$DATETIME # Full ISO8601 with timezone offset
$PROCESSING_DATE # Date for directory structure (YYYY/MM/DD)
# Detection metrics (when ENABLE_DETECTION=true)
$DETECTION_ENABLED # "true" or "false"
$PAGES_SCANNED # Total pages in all input PDFs
$BLANK_PAGES_REMOVED # Blank pages detected and removed
$DOCUMENTS_CREATED # Output documents after splitting
$CLASSIFICATION_ENABLED # "true" or "false"
# Detection reliability status (for success vs failure notifications)
$SCAN_STATUS # "success", "partial", or "failure"
$DETECTION_FAILURES # Detection failures in this run
$DETECTION_CONSECUTIVE_FAILURES # Running consecutive-failure count
$DETECTION_AUTO_DISABLED # "true" when detection is currently auto-disabled
# Timing metrics
$PROCESSING_DURATION_SECONDS # Total processing time in seconds
$PROCESSING_DURATION # Human readable (e.g., "2m 34s")
$SECONDS_PER_PAGE # Average seconds per page
Example notification:
#!/bin/bash
if [[ "${DETECTION_ENABLED}" == "true" ]]; then
MSG="${FILES_PROCESSED} scans -> ${DOCUMENTS_CREATED} documents"
MSG="${MSG} (${BLANK_PAGES_REMOVED} blanks removed)"
else
MSG="${FILES_PROCESSED} files processed"
fi
# React to detection health (Phase 3 reliability)
PRIORITY="default"
if [[ "${SCAN_STATUS}" == "failure" ]]; then
PRIORITY="high"
MSG="⚠️ Detection FAILED (${DETECTION_CONSECUTIVE_FAILURES} in a row). ${MSG}"
fi
if [[ "${DETECTION_AUTO_DISABLED}" == "true" ]]; then
PRIORITY="high"
MSG="${MSG} — AI detection auto-disabled; running plain OCR until the GPU recovers."
fi
curl -X POST "https://ntfy.sh/mytopic" -H "Priority: ${PRIORITY}" -d "${MSG}"
git clone https://github.com/csmarshall/docker-pdfautomagic.git
cd docker-pdfautomagic
# Copy and configure
cp .env.example .env
cp -r config-example my-config
rclone config --config my-config/rclone.conf
# Build and run (model is included in the build)
docker compose -f docker-compose.build.yml build
docker compose -f docker-compose.build.yml up -d
docker build --target lite -t pdfautomagic:latest-lite .
/scans/unprocessed/ for PDFs older than MINUTES_OLD (default: 1 min)/scans/processed/YYYY/MM/DD//scans/originals/YYYY/MM/DD/PDFAutomagic processes documents entirely locally - no data is sent to external servers.
See ADR-008 for the full rationale on local-only AI processing.
See docs/adr/ for architecture decision records:
docker compose logs -f
docker compose run --rm pdfautomagic nvidia-smi # NVIDIA
docker compose run --rm pdfautomagic \
/app/detection/.venv/bin/python /app/detection/split_pdf.py \
--check-gpu
Dependencies are automatically tracked:
# Pull latest image (includes updated model)
docker pull chasmarshall/pdfautomagic:latest
docker compose down && docker compose up -d
For users running multiple AI tools, you can point PDFAutomagic at a shared Ollama server instead of using the bundled model:
environment:
- OLLAMA_HOST=http://your-ollama-server:11434
- ENABLE_DETECTION=true
This allows sharing models across tools while maintaining privacy (your server, your network).
PDFAutomagic builds on these excellent open-source projects:
MIT License - see LICENSE for details.
Content type
Image
Digest
sha256:0954380f7…
Size
7.1 GB
Last updated
27 days ago
docker pull chasmarshall/pdfautomagic