Self-hosted, Apache-2.0 ATS tool that rewrites resumes to a job description locally with Ollama or your own LLM key, unlike Jobscan's paid cloud scanner.
Resume Matcher is a self-hosted tool that rewrites a resume against a specific job description instead of only scoring it for missing keywords. It parses both documents, scores the match with vector similarity, then hands the gap to an LLM you supply, local through Ollama or a cloud key, to actually edit the wording.
No commercial product does exactly this as a single self-hosted package; the closest neighbours are cloud ATS scanners like Jobscan and Teal. Both run as hosted SaaS, charge a monthly or per-scan fee past a free quota, and keep the resume and job postings on their own servers. Resume Matcher covers the same ground, keyword extraction, a match score, gap analysis, then rewrites the resume text itself instead of leaving you to work keywords back in by hand.
Running it changes who owns the infrastructure, not just the price. The Docker image bundles the FastAPI backend and Next.js frontend behind one port, and TinyDB writes straight to a JSON file instead of a separate database service, so there is nothing extra to provision. The tradeoff sits with the model: Jobscan's matching runs on infrastructure you never see, while Resume Matcher expects you to wire in Ollama for local inference or an API key for OpenAI, Anthropic, Gemini or DeepSeek, so whoever runs it also owns the uptime and per-resume cost of that model.
Is Resume Matcher free for commercial use? Yes. It is Apache-2.0 licensed with no paid tier, so a company can self-host it internally without a licensing conversation.
Resume Matcher vs Jobscan — which should I pick? Jobscan for a maintained SaaS with ATS-specific parsing rules and no setup; Resume Matcher if you would rather run it yourself and plug in your own LLM, local or via API key.
Can I run Resume Matcher without sending data to a cloud LLM? Yes. Point it at Ollama and the pipeline, parsing, matching and rewriting, stays on the host you control, though local models need more time than the current fixed timeout allows.
Resume Matcher is worth the Docker container if you want resume tailoring kept on infrastructure you control and don't mind proofreading the AI output; teams that want a quick match score with zero setup are better served by a hosted scanner. It is actively maintained, with commits pushed in September 2026, and Apache-2.0 licensed, permissive enough to bundle into an internal tool without asking legal first.
We started this exact setup from scratch in a clean Linux machine on Nov 1, 2026 and checked it through Resume Matcher's own API: every check passed. Recipes are re-checked regularly and removed if they stop working.
mkdir -p resume-matcher && cd resume-matcher
curl -fsSL https://open-source.store/tools/resume-matcher/compose.yml -o compose.ymlcat > .env <<EOF
FRONTEND_BASE_URL=http://localhost:3000
EOFFRONTEND_BASE_URL — Public URL where Resume Matcher is accessed; set to your domain or reverse proxy URL.docker compose up -dservices:
resume-matcher:
image: ghcr.io/srbhr/resume-matcher:1.4.0
ports:
- "127.0.0.1:3000:3000"
volumes:
- resume-data:/app/backend/data
environment:
- FRONTEND_BASE_URL=${FRONTEND_BASE_URL}
- LOG_LEVEL=INFO
- LOG_LLM=WARNING
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "curl -f http://127.0.0.1:8000/api/v1/health || exit 1"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
volumes:
resume-data:
Lint, codegen, breaking-change checks and dependency management in one Git-native CLI. Open source, no registry lock-in.
Show that Resume Matcher is listed as an open-source alternative. Paste into your README:
Ubuntu 24.04 · Apple container, Docker 29.1.3, Compose 2.40.3+ds1-0ubuntu1~24.04.1. Images pulled in 73 s: ghcr.io/srbhr/resume-matcher:1.4.0.
Based on the upstream setup. Official compose. Image pinned to 1.4.0; build/develop sections and host.docker.internal dropped, port bound to 127.0.0.1 because Resume Matcher has no authentication, and FRONTEND_BASE_URL moved to .env.
resume-matcher-1 | 2026-11-01T08:51:39.629819589Z [i] Routing configuration: resume-matcher-1 | 2026-11-01T08:51:39.629827589Z [i] Loading configuration from environment and *_FILE secrets... resume-matcher-1 | 2026-11-01T08:51:39.629833506Z resume-matcher-1 | 2026-11-01T08:51:39.629834256Z resume-matcher-1 | 2026-11-01T08:51:39.629834839Z ██████╗ ███████╗███████╗██╗ ██╗███╗ ███╗███████╗ resume-matcher-1 | 2026-11-01T08:51:39.629835922Z ██╔══██╗██╔════╝██╔════╝██║ ██║████╗ ████║██╔════╝ resume-matcher-1 | 2026-11-01T08:51:39.629836631Z ██████╔╝█████╗ ███████╗██║ ██║██╔████╔██║█████╗ resume-matcher-1 | 2026-11-01T08:51:39.629837256Z ██╔══██╗██╔══╝ ╚════██║██║ ██║██║╚██╔╝██║██╔══╝ resume-matcher-1 | 2026-11-01T08:51:39.629837839Z ██║ ██║███████╗███████║╚██████╔╝██║ ╚═╝ ██║███████╗ resume-matcher-1 | 2026-11-01T08:51:39.629838464Z ╚═╝ ╚═╝╚══════╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ resume-matcher-1 | 2026-11-01T08:51:39.629839089Z resume-matcher-1 | 2026-11-01T08:51:39.629839547Z ███╗ ███╗ █████╗ ████████╗ ██████╗██╗ ██╗███████╗██████╗ resume-matcher-1 | 2026-11-01T08:51:39.629840256Z ████╗ ████║██╔══██╗╚══██╔══╝██╔════╝██║ ██║██╔════╝██╔══██╗ resume-matcher-1 | 2026-11-01T08:51:39.629840881Z ██╔████╔██║███████║ ██║ ██║ ███████║█████╗ ██████╔╝ resume-matcher-1 | 2026-11-01T08:51:39.629841547Z ██║╚██╔╝██║██╔══██║ ██║ ██║ ██╔══██║██╔══╝ ██╔══██╗ resume-matcher-1 | 2026-11-01T08:51:39.629842172Z ██║ ╚═╝ ██║██║ ██║ ██║ ╚██████╗██║ ██║███████╗██║ ██║ resume-matcher-1 | 2026-11-01T08:51:39.629842756Z ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ resume-matcher-1 | 2026-11-01T08:51:39.629843381Z resume-matcher-1 | 2026-11-01T08:51:39.629843839Z resume-matcher-1 | 2026-11-01T08:51:39.629844339Z Crazy Stuff with Resumes and Cover letters resume-matcher-1 | 2026-11-01T08:51:39.629844922Z resume-matcher-1 | 2026-11-01T08:51:39.629845464Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ resume-matcher-1 | 2026-11-01T08:51:39.629847381Z resume-matcher-1 | 2026-11-01T08:51:39.629847881Z Public port: 3000 resume-matcher-1 | 2026-11-01T08:51:39.629848422Z Internal API: 8000 (proxied at /api) resume-matcher-1 | 2026-11-01T08:51:39.629850089Z resume-matcher-1 | 2026-11-01T08:51:39.631023297Z [i] Application log level: INFO resume-matcher-1 | 2026-11-01T08:51:39.631041339Z [i] LiteLLM log level: WARNING resume-matcher-1 | 2026-11-01T08:51:39.631064464Z [✓] Configuration loaded resume-matcher-1 | 2026-11-01T08:51:39.631075589Z [i] Checking data directory... resume-matcher-1 | 2026-11-01T08:51:39.631101047Z [✓] Data directory exists: /app/backend/data resume-matcher-1 | 2026-11-01T08:51:39.631112756Z [i] Checking Playwright browsers... resume-matcher-1 | 2026-11-01T08:51:39.631160172Z [✓] Playwright browsers found resume-matcher-1 | 2026-11-01T08:51:39.631169547Z resume-matcher-1 | 2026-11-01T08:51:39.631180422Z [i] Starting backend server on internal port 8000... resume-matcher-1 | 2026-11-01T08:51:39.631267797Z [i] Waiting for backend to be ready... resume-matcher-1 | 2026-11-01T08:51:41.321027631Z INFO: Started server process [13] resume-matcher-1 | 2026-11-01T08:51:41.321051673Z INFO: Waiting for application startup. resume-matcher-1 | 2026-11-01T08:51:41.342412798Z INFO: Application startup complete. resume-matcher-1 | 2026-11-01T08:51:41.342714006Z INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) resume-matcher-1 | 2026-11-01T08:51:41.662163048Z INFO: 127.0.0.1:60714 - "GET /api/v1/health HTTP/1.1" 200 OK resume-matcher-1 | 2026-11-01T08:51:41.663540132Z resume-matcher-1 | 2026-11-01T08:51:41.663546507Z [✓] Backend is ready (PID: 13) resume-matcher-1 | 2026-11-01T08:51:41.664012257Z [i] Starting frontend server on port 3000... resume-matcher-1 | 2026-11-01T08:51:41.664053257Z [✓] Frontend is running (PID: 22) resume-matcher-1 | 2026-11-01T08:51:41.772036798Z ▲ Next.js 16.4.0 resume-matcher-1 | 2026-11-01T08:51:41.772079132Z - Local: http://localhost:3000 resume-matcher-1 | 2026-11-01T08:51:41.772084548Z - Network: http://0.0.0.0:3000 resume-matcher-1 | 2026-11-01T08:51:41.772198840Z ✓ Ready in 0ms resume-matcher-1 | 2026-11-01T08:51:41.773074132Z ✓ Running next.config took 0.4ms resume-matcher-1 | 2026-11-01T08:51:42.776429590Z INFO: 127.0.0.1:60728 - "GET /api/v1/health HTTP/1.1" 200 OK resume-matcher-1 | 2026-11-01T08:51:42.817404799Z INFO: 127.0.0.1:60742 - "GET /api/v1/status HTTP/1.1" 200 OK resume-matcher-1 | 2026-11-01T08:51:42.834375965Z INFO: 127.0.0.1:60744 - "POST /api/v1/jobs/upload HTTP/1.1" 200 OK resume-matcher-1 | 2026-11-01T08:51:42.838388882Z INFO: 127.0.0.1:60750 - "GET /api/v1/jobs/9fa46f36-a959-4921-8d32-658d61fcea4d HTTP/1.1" 200 OK resume-matcher-1 | 2026-11-01T08:51:42.840163549Z INFO: 127.0.0.1:60764 - "POST /api/v1/jobs/parse-metadata HTTP/1.1" 200 OK resume-matcher-1 | 2026-11-01T08:51:44.677647300Z INFO: 127.0.0.1:38888 - "GET /api/v1/health HTTP/1.1" 200 OK