Repository navigation
Getting Started
This page takes you from nothing to a first set of job matches on a self-hosted Job Matcher. It covers what you need, what the one-line installers do step by step, the difference between demo and live mode, a walkthrough of the first sign-in, and how to switch modes and upgrade. For running the code from a checkout instead, see Development Setup; for the full production stack, see Deployment.
| Requirement | Detail |
|---|---|
| Docker | Docker Engine with Compose v2. Docker Desktop includes both on Windows, macOS and Linux. |
| Memory and disk | About 2 GB of free memory and 3 GB of disk (self-hosting guide). |
| CPU architecture | amd64 or arm64. Images are published for both, so Intel and Apple Silicon Macs and a Raspberry Pi 4 or 5 work. |
| Shell tools (macOS/Linux) |
curl or wget. openssl is used for secrets when present, otherwise /dev/urandom. lsof, ss or netstat is used to find a free port when present. |
| API keys (live mode only) | An OpenRouter key for AI matching and a Firecrawl key for scraping. Demo mode needs neither. |
The installer never asks for admin rights and writes only to the install folder.
macOS, Linux or WSL:
curl -fsSL https://raw.githubusercontent.com/Karthik-beta/AI-Job-Resume-Matcher/master/install.sh | shWindows PowerShell:
irm https://raw.githubusercontent.com/Karthik-beta/AI-Job-Resume-Matcher/master/install.ps1 | iexTo read the scripts before running them: install.sh and install.ps1.
Both scripts follow the same five steps. install.ps1 wraps everything in a script block, so iex neither leaks variables nor exits your shell.
| Step | What happens | Failure message |
|---|---|---|
| 1. Check Docker | Checks that docker is on the path, that docker compose version works (Compose v2), and that docker info succeeds (the daemon is running). |
Docker is not installed, Docker Compose v2 is missing, Docker is installed but not running
|
| 2. Pick a version | Uses JOB_MATCHER_VERSION when set (a leading v is stripped). Otherwise reads tag_name from https://api.github.com/repos/Karthik-beta/AI-Job-Resume-Matcher/releases/latest. The compose files are downloaded from the git ref v<version> when the version looks like X.Y.Z, from master for any other value, or from JOB_MATCHER_REF when set. |
Could not find the latest release on GitHub. Set JOB_MATCHER_VERSION and run this again. |
| 3. Download the stack | Creates the install folder and downloads deploy/compose.yml and deploy/.env.example from that ref into it as compose.yml and .env.example. |
Download error from curl, wget or Invoke-WebRequest
|
| 4. Write settings |
If .env already exists (an upgrade), only JOB_MATCHER_VERSION is updated; every other value is kept. Otherwise, it first refuses to continue if the Docker volume job-matcher_postgres-data exists (an earlier install's database, created with a password that is now lost). Then it copies .env.example to .env and sets POSTGRES_PASSWORD and BETTER_AUTH_SECRET to 32 random bytes in hex, finds the first free TCP port from 8080 upward and writes it to WEB_PORT and BETTER_AUTH_URL=http://localhost:<port>, and writes JOB_MATCHER_VERSION. Finally it asks for the API keys (below) and sets PROVIDERS. |
A database from an earlier Job Matcher install exists, but its .env is gone. |
| 5. Start | Runs docker compose -f <dir>/compose.yml up -d --pull missing --quiet-pull --remove-orphans --wait --wait-timeout 300. The explicit -f overrides any COMPOSE_FILE set in your shell. It then prints the URL, the mode, and the commands for logs, stop, upgrade and uninstall. install.ps1 also opens the URL in your browser when it runs interactively. |
The stack did not become healthy. See the logs with: ... docker compose logs |
Key prompts. The prompts read from the terminal (/dev/tty on Unix), so they work under curl | sh. A key already present in the environment as OPENROUTER_API_KEY or FIRECRAWL_API_KEY is used instead of prompting. The Firecrawl prompt appears only after an OpenRouter key is given. With both keys the installer writes them to .env and sets PROVIDERS=live; with one or none it sets PROVIDERS=fake (printing "Live mode needs both keys; starting in demo mode." when only one was given).
File permissions. On macOS and Linux, .env is created with mode 600, and every rewrite goes through a temporary file created under umask 077, so a re-run also tightens a .env that an earlier install left world-readable. CI checks this (see CI/CD and Releases). On Windows, .env is written as UTF-8 without a BOM and with LF line endings, which Compose expects.
| Variable | Default | Effect |
|---|---|---|
JOB_MATCHER_DIR |
~/job-matcher ($HOME\job-matcher on Windows) |
Install folder for compose.yml, .env.example and .env. |
JOB_MATCHER_VERSION |
Latest GitHub release | Release to install, for example 1.2.0 or v1.2.0. Written to .env and used as the image tag. |
JOB_MATCHER_REF |
v<version>, or master for a non-X.Y.Z version |
Git ref the compose files are downloaded from. CI uses it to test the installer against a pull request's head commit. |
JOB_MATCHER_NONINTERACTIVE |
unset |
1 skips every prompt; keys come from OPENROUTER_API_KEY and FIRECRAWL_API_KEY in the environment. |
OPENROUTER_API_KEY, FIRECRAWL_API_KEY
|
unset | Used as the API keys on a fresh install, in place of the prompts. |
Example of a scripted install of a pinned version:
JOB_MATCHER_NONINTERACTIVE=1 JOB_MATCHER_VERSION=1.0.0 \
OPENROUTER_API_KEY=sk-or-... FIRECRAWL_API_KEY=fc-... \
sh -c "$(curl -fsSL https://raw.githubusercontent.com/Karthik-beta/AI-Job-Resume-Matcher/master/install.sh)"The self-hosting guide has the three manual commands (download compose.yml and .env.example, fill in the Required section, docker compose up -d --wait) and the from-source variant that builds images with deploy/compose.build.yml. Both are described in Deployment.
PROVIDERS in .env picks the mode. The API and worker read it at startup (config.ts).
Demo (PROVIDERS=fake) |
Live (PROVIDERS=live) |
|
|---|---|---|
| Keys | None |
OPENROUTER_API_KEY and FIRECRAWL_API_KEY (or a self-hosted Firecrawl, see Configuration Reference) |
| Scraping | Built-in fixture pages from apps/api/test/fixtures/. Every careers page serves the same sample board of three jobs. |
Firecrawl cloud by default; ATS boards through their public APIs |
| Resume | Every resume URL serves the fixture resume | The PDF or page at your resume URL |
| AI | A scripted evaluator: a job page's Verdict: line decides the result |
OpenRouter, openai/gpt-6-luna:floor on the flex tier by default |
| Discord | Logged only ("Fake Discord alert for ...") | Posted to your webhook |
| Cost | Free | Firecrawl credits (the free plan has 1,000 a month) and a fraction of a cent of AI per job |
The fake providers are defined in providers/fake.ts. In demo mode, add a plain careers page URL (any https:// URL that is not a Greenhouse, Lever, Ashby, SmartRecruiters or Recruitee board) or a search source. ATS boards are read over plain HTTP, which demo mode does not fake, so an ATS source fails to load there. See Glossary: fake providers.
The app's routes live in apps/web/app. Open the URL the installer printed (by default http://localhost:8080).
Choose Create an account on the landing page (/), or go to /sign-up. Enter a name, email and password and press Create account. Accounts are email and password only (auth.ts); there is no email verification. Anyone who can reach the address can sign up, so do not expose an instance you do not want others to use. See Authentication.
A new account lands on the setup wizard. The current step is kept in the URL as ?step= (welcome-steps.ts):
| Step | URL | Heading | What you do | Required |
|---|---|---|---|---|
| Welcome | ?step=welcome |
How it works | Read the overview, then press Let's set up | No |
| Resume | ?step=resume |
Add your resume | Paste a public link to a PDF (Google Drive and Dropbox share links work) or a page such as a Google Doc, then Save resume | Yes |
| Matching | ?step=preferences |
Matching basics | Set experience, cities and title keywords, or press Use defaults | No |
| Job sources | ?step=source |
Add a job source | Add a careers page, ATS board or search, then Next | Yes |
| First run | ?step=firstRun |
Start your first run | Press Start first run | Yes |
Steps you have not unlocked cannot be opened: matching and sources need a resume, and the first run needs a resume and a source. Finish later or Skip for now goes to /home, where a "Get set up" checklist shows your progress ("N of 3 required done") with a Continue setup link back into the wizard. The checklist can be hidden and brought back from the account menu's Setup guide item.
Starting the first run opens the run page with ?first=1, which adds a "What happens during a run" guide. The page polls the API while the worker reads your sources, filters listings, and evaluates each job. Each job shows how its text was read: From job board (ATS API, free), Saved copy (page cache, free), Read directly (self-hosted Firecrawl, free) or Read with Firecrawl (cloud, uses credits). When the run finishes, See your matches links to the matches list. The pipeline is described in Run Pipeline.
Everything the wizard collects can be changed later in Settings, which has these sections: Resume, Job sources, Matching, Runs (jobs per run, title triage), Schedule & alerts (check every N minutes, Discord webhook) and Data (clear the page cache). Each field is described in Configuration Reference: user settings.
The Analyze page lists your job sources with checkboxes and an Analyze button (Analyze again after a run), and shows the latest run and past runs. If the resume or sources are missing, it links to /settings#resume or /settings#sources. Only one run per user can be queued or running at a time; a second start returns "A run is already in progress".
Matches are listed newest first, filtered by Pending or Applied. Mark applied moves a match to Applied, and Move to pending moves it back. Matches are kept per job URL, so a job matched again on a later run keeps its status.
The header links to Home (/home), Analyze (/analyze, which also covers /runs/...), Matches (/matches) and Settings (/settings). A signed-out visitor who opens any of these is sent to /sign-in?next=<page> and returned there after signing in (proxy.ts). A signed-in visitor who opens /, /sign-in or /sign-up goes to /home.
-
Edit
~/job-matcher/.env(or yourJOB_MATCHER_DIR):PROVIDERS=live OPENROUTER_API_KEY=sk-or-... FIRECRAWL_API_KEY=fc-...
-
Apply it:
cd ~/job-matcher && docker compose up -d
The worker validates the keys when it starts: it exits if OPENROUTER_API_KEY is empty, or if the Firecrawl mode's key or URL is missing. Each run then checks the OpenRouter key and model before scraping anything (see Glossary: preflight). Check docker compose logs worker if a run never finishes. To go back to demo mode, set PROVIDERS=fake and run the same command.
Results evaluated in demo mode stay in the database. Live runs read real pages, so they produce new job results rather than reusing demo ones.
Run the installer again. It downloads the newest release's compose.yml and .env.example, updates JOB_MATCHER_VERSION in .env, keeps every other setting and all data, and restarts the stack. The migrate service applies any new database migrations before the API and worker start.
Note that an upgrade does not merge new variables from the new .env.example into your .env. Compare the two files after upgrading if a release notes a new setting.
For a by-hand install, set JOB_MATCHER_VERSION in .env, download that release's compose.yml, and run docker compose up -d --wait. See Deployment.
- Configuration Reference for every environment variable and user setting.
- Deployment for HTTPS, backups and uninstalling.
- Troubleshooting when something does not start.
Job Matcher · Website · Report an issue · MIT license
Using Job Matcher
System design
Matching engine
Interface and brand
Engineering