All apps · 0 apps
Jobify-JobSpy
Docker app from Esg450's Repository
Overview
Readme
View on GitHub
Jobify
A self-hosted tracker for your job applications.
Jobify keeps your whole job search in one place: the postings you are interested in, where each application stands, upcoming interviews and follow-ups, and your notes. It runs as a single Docker container with an embedded SQLite database, so there is nothing else to set up.
![]() |
![]() |
![]() |
Features
- Track everything that matters. Title, company, location, remote/hybrid/on-site, employment type, salary range, source, contact, tags, notes, an interest rating and the full description.
- Status pipeline. Saved → Applied → Screening → Interviewing → Offer → Accepted, plus Rejected, Withdrawn and Ghosted. Every status change is recorded on the job's timeline automatically.
- List, board and timeline views. Search, filter and sort a table of jobs, drag cards between columns on a full-width Kanban board, or chart your search: a Gantt-style timeline of every application coloured by status with interviews marked, and a flow (Sankey) diagram of how applications moved between statuses. Both can be saved as PNG or SVG.
- Timeline. Log interviews, notes and follow-ups against each job. Upcoming interviews, follow-up dates and deadlines show up on the dashboard.
- Dashboard. Active applications, response rate, offers, your pipeline at a glance and applications per week. Drag the cards into the order you like and hide the ones you don't need; the layout is saved to your account.
- Job hunts. Each search is its own hunt with its own list, board, charts and dashboard. Finish a hunt when you land a job and start a new one next time: you begin with a clean slate without deleting anything, and can look back at how every earlier hunt went (applications, response rate, interviews, offers and the job you accepted). Upgrading from an older version puts your existing jobs into a first hunt automatically.
- Import from job sites. Paste a link and Jobify fills in the form. Dedicated importers for LinkedIn, Workday, Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Rippling and Oracle career sites, plus generic importers that read the structured data, embedded job data and page content that most other career pages have. Pages pasted from Indeed, Glassdoor and other sites that block automated access are recognised too, and plain text works as well.
- Find jobs without leaving the app (beta). Connect the optional Jobify JobSpy container and search Indeed, LinkedIn, Glassdoor, ZipRecruiter, Google Jobs and other boards from a Find jobs page. Results that are already in your jobs are marked, and any other posting imports into your hunt with one click. Results are scraped from the boards, so expect the odd gap or stale posting.
- Optional AI assistant. Connect Anthropic (Claude), OpenAI, Google Gemini, a local Ollama model or any OpenAI-compatible API to summarize postings, tidy up descriptions, draft cover letters, prepare for interviews and extract job details from any page.
- Your data, portable. Export everything as JSON (and import it again) or as a CSV spreadsheet.
- Multiple users. Everyone gets their own private jobs, timeline and profile. Admins add people or open sign-up, and manage the shared AI provider.
- A dark mode, and a layout that works on phones.
Quick start
Docker Compose
services:
jobify:
image: ghcr.io/esg450/jobify:latest
container_name: jobify
restart: unless-stopped
ports:
- '3000:3000'
volumes:
- ./data:/data
environment:
PUID: 1000
PGID: 1000
docker compose up -d
Then open http://localhost:3000. The first person to open Jobify creates the admin account.
Docker
docker run -d --name jobify \
-p 3000:3000 \
-v /path/to/jobify-data:/data \
-e PUID=1000 -e PGID=1000 \
ghcr.io/esg450/jobify:latest
Unraid
Jobify is in Community Applications; search for Jobify in the Apps tab. The optional
Jobify JobSpy container for job board search is there too. If you prefer to
install by hand, a template is included at unraid/jobify.xml. Copy it to
/boot/config/plugins/dockerMan/templates-user/ on your server, then choose Add Container in
the Docker tab and pick Jobify from the template list. Data is stored in
/mnt/user/appdata/jobify and the container runs as 99:100 (nobody:users) by default.
Build from source
git clone https://github.com/esg450/jobify.git
cd jobify
docker build -t jobify .
Updating
Images are published to the GitHub Container Registry for amd64 and arm64:
| Tag | What it is |
|---|---|
latest |
The newest release. Recommended. |
1.2.3, 1.2 |
A specific release, if you want to pin a version. |
beta |
The newest pre-release, for trying features before they ship. |
edge |
The current main branch, for trying unreleased changes |
On Unraid, new releases appear as update ready in the Docker tab; click it to update. With
Compose, run docker compose pull && docker compose up -d. Your data is kept across updates and
database migrations run automatically. The running version is shown on the Settings page.
The optional JobSpy container (ghcr.io/esg450/jobify-jobspy) uses the same tags
but is only rebuilt by releases that change it, so it does not show an update every time Jobify
does. The two can be updated independently: the connection between them does not change from
release to release.
Configuration
All settings are optional environment variables.
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
Port the web server listens on. |
DATA_DIR |
/data |
Directory for the SQLite database. |
PUID / PGID |
1000 / 1000 |
User and group the container runs as. The data directory is handed to this user on startup. |
LOG_LEVEL |
log |
How much to log: error, warn, log, or debug to also record every request. |
AI_PROVIDER |
unset | Default AI provider: anthropic, openai, gemini, ollama or openai_compatible. |
AI_MODEL |
unset | Default model. Anthropic defaults to claude-opus-5-5; other providers need a model name. |
AI_API_KEY |
unset | API key for the provider. |
AI_BASE_URL |
unset | Custom API endpoint, e.g. http://192.168.1.10:11434 for Ollama. |
JOBSPY_URL |
unset | URL of a Jobify JobSpy container, which turns on the Find jobs page. See Job search. |
JOBSPY_API_KEY |
unset | The key set on that container. |
AI and job search settings can also be managed from the Settings page; settings saved there take precedence over the environment variables. Both pages let you test a connection before saving, and saving only succeeds once the provider or service answers.
AI providers
| Provider | What you need |
|---|---|
| Anthropic (Claude) | An API key from the Anthropic Console. |
| OpenAI | An API key and a model name. |
| Google Gemini | An API key from Google AI Studio and a model name. |
| Ollama | A running Ollama server and a model you have pulled. Point the base URL at it. |
| OpenAI-compatible API | The API's base URL (for example OpenRouter, Groq, LM Studio or vLLM), a model and usually a key. |
Add your resume and what you are looking for under Settings → Your profile to get cover letters and interview prep tailored to you. Job details and your profile are sent to the provider you choose, and nowhere else.
Importing jobs
Paste a posting URL on the Add job page. Jobify tries the importer for that site first, then
works through the fallbacks: the schema.org JobPosting data most career sites publish, the
markup of well-known job sites, job data embedded in the page's scripts, and finally the page's
title and the block of text that reads most like a description. Whatever was found, a last pass
reads the salary, location, remote/hybrid and employment type out of the text. Company careers
pages that embed their postings from Ashby, Greenhouse, Lever or Workday are followed to the
real posting. You always get to review the result before saving.
Some sites block automated requests or require you to sign in (Indeed, Glassdoor and LinkedIn when signed in, for example). In that case switch to Paste and paste the page source (right click → View Page Source → Select all → Copy, or from the browser's developer tools) or just the posting text. A copied posting usually comes through with its title, company, location and salary; with an AI provider configured, Jobify can fill in anything that is still missing.
Want to support another site? Importers are small, self-contained classes; see CONTRIBUTING.md.
Job search
Beta. Job board search is new. Results are scraped from the boards by JobSpy, so they can be incomplete, out of date or occasionally wrong (a salary read from the wrong line, a location that is really the company's HQ). Treat them as leads and check the posting before you apply.
Jobify can search the job boards for you through JobSpy,
which it runs as a second, optional container (ghcr.io/esg450/jobify-jobspy, source in
apps/jobspy). Keeping it separate keeps the scraping dependencies out of the main
image and lets you update or restart it on its own.
Start the container. On Unraid, install Jobify JobSpy from Community Applications, the same place you installed Jobify, and set an API key (if it is not in the store yet, use the
unraid/jobify-jobspy.xmltemplate the same way as Jobify's). With Compose, uncomment thejobspyservice indocker-compose.yml. By hand:docker run -d --name jobify-jobspy -p 8000:8000 -e API_KEYS=change-me ghcr.io/esg450/jobify-jobspy:latestConnect it. In Jobify open Settings → Job search, enable it, enter the container's URL as seen from the Jobify server (for example
http://192.168.1.10:8000) and the key, then Test connection. Saving checks the connection too, so a wrong address is never saved. A Find jobs page appears in the sidebar for everyone. Once connected, Search defaults on the same page sets how searches run: the boards selected by default, the country for Indeed and Glassdoor (they search one country at a time), the search radius, whether LinkedIn descriptions are fetched during the search or only on import, yearly salary figures, and proxies to spread searches across addresses.Search. Enter keywords, a location, which boards to search and how recent the postings should be. With an AI provider set up and something in your profile, Suggest from my profile fills in the keywords, location and remote preference for you (and offers a few alternative titles to try); nothing runs until you press Search, so you can adjust it first. Postings you already track are marked; Import on any other one runs it through Jobify's importers for the full description and opens the Add job page for you to review and save.
Searches scrape the boards live, so they take a while (up to a minute across several boards) and
the boards rate-limit addresses that search too often. A board that answers with nothing is
automatically tried once more a few seconds later; if LinkedIn or Glassdoor keep returning
nothing, wait a bit, search fewer boards at a time, or add proxies under Search defaults (or
on the container with DEFAULT_PROXIES). The service also works with the community
jobspy-api container, which uses the same endpoints.
Users and accounts
The first person to open a new Jobify creates the admin account. After that, admins can add people under Settings → Users, or turn on sign-up so people can create their own accounts. Each user's jobs, timeline and profile are private to them. The AI provider is shared and only admins can change it.
Upgrading from a version without accounts? Your jobs are kept. On first launch, Jobify asks you
to create the admin account and moves your existing jobs and profile into it. If you had set
JOBIFY_PASSWORD, you'll need to enter it on that screen; this stops someone else from claiming
your data first. You can remove the variable afterwards.
Forgot a password? Admins can set a new one for any user under Settings → Users. If you are locked out of the only admin account, reset it from the server:
docker exec jobify node apps/server/dist/cli.js list-users
docker exec jobify node apps/server/dist/cli.js reset-password <username>
Security
- If Jobify is reachable from outside your home network, put it behind a reverse proxy with HTTPS and leave sign-up turned off unless you need it.
- On a brand-new instance, create the admin account straight away: until you do, whoever opens Jobify first becomes the admin.
- The server fetches the URLs you import. Anyone with an account can make it request web pages.
- Passwords are hashed with scrypt, sessions are stored as hashes, and sign-in attempts are rate-limited. API keys are stored in the database in your data directory and are never sent to the browser.
Logs and troubleshooting
Jobify writes its log to logs/jobify.log in the data directory (on Unraid,
/mnt/user/appdata/jobify/logs/), alongside the container's console output. Server errors, failed
requests and errors from people's browsers all end up there. Admins can also download it under
Settings → System. Set LOG_LEVEL=debug to record every request while you track down a
problem. Log files roll over at 5 MB, and the three most recent old files are kept.
Backups
Everything lives in jobify.db in the data directory, so back up that directory to back up every
user. Each user can also download their own jobs as JSON or CSV under Settings → Your data.
Development
Requirements: Node.js 24 and npm.
npm install
npm run dev # API on :3000 and the web app on http://localhost:5173
| Command | What it does |
|---|---|
npm run dev |
Run the API and web app with live reload |
npm test |
Run the test suite |
npm run lint / npm run format |
Lint / format the code |
npm run typecheck |
Type-check both apps |
npm run build |
Production build of both apps |
npm run db:generate -w apps/server |
Create a migration after changing the schema |
npm run seed -w apps/server |
Fill your account with demo jobs (-- --remove deletes them) |
The stack is NestJS with Drizzle ORM on SQLite for the API, and React with Vite, TanStack Query and Tailwind CSS for the web app. See CONTRIBUTING.md for how the code is organised.
License
Jobify is free software released under the GNU Affero General Public License v3.0. You can use, self-host, modify and share it. If you distribute a modified version, or run one as a service for other people, you must make your source code available under the same license.
Requirements
Categories
Related apps
Explore more like this
Explore allDetails
ghcr.io/esg450/jobify-jobspy:latestRuntime arguments
- Network
bridge- Shell
sh- Privileged
- false
Template configuration
Port Jobify connects to. Use http://[unraid ip]:[this port] as the JobSpy URL in Jobify.
- Target
- 8000
- Default
- 8000
- Value
- 8000
A secret of your choosing. Enter the same value as the JobSpy API key in Jobify. Leave empty to allow anyone on your network to use the service.
- Target
- API_KEYS
Optional, comma-separated proxies (user:pass@host:port) to route searches through. The job boards rate-limit repeated searches from one address.
- Target
- DEFAULT_PROXIES
The most postings one search may request per board.
- Target
- MAX_RESULTS
- Default
- 100
- Value
- 100
How many searches run at the same time; others wait their turn.
- Target
- MAX_CONCURRENT_SEARCHES
- Default
- 2
- Value
- 2


