Skip to content
karl-ctaPublic

About

Self-hosted job application tracker. Single Go binary, SQLite database, zero cloud dependencies.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

JobCtrl

JobCtrl

Self-hosted job application tracker. Single Go binary, SQLite database, zero cloud dependencies.

Go SQLite Docker License

JobCtrl Demo

Features

  • Full pipeline tracking with statuses from Wishlist to Accepted, interviews, contacts, and timeline
  • Auto-fill from URL by pasting a job listing link (extracts company, title, salary via JSON-LD / Open Graph)
  • Dashboard with period-based indicators, an "in progress" card for live interview processes, a 12-week sent-vs-replies timeline, a six-month activity heatmap with per-day details, a journey funnel, status breakdown and sources
  • Follow-up reminders for applications with no response after an interview (snooze or dismiss)
  • Automatic "no reply" moves applications with no answer after 30 days out of your pending pile
  • Duplicate detection warns you when applying to a company you've already contacted
  • Bulk actions to change status or delete multiple applications at once
  • Sort and filter by date, company, status, confidence, interest, and source
  • Table and Kanban views with pagination
  • JSON backup and restore plus CSV export for spreadsheets
  • Bilingual French and English
  • Dark mode with warm stone palette
  • Self-hosted with your data on your machine, no accounts, no cloud

Quick start

Docker (recommended)

docker compose up -d

Open http://localhost:8080. Data is persisted in a Docker volume.

From source

Requires Go 1.26+ and Node.js 22+.

git clone https://github.com/karl-cta/jobctrl.git
cd jobctrl
make install   # frontend deps
make build     # frontend + Go binary
./job-ctrl     # starts on :8080

Configuration

Variable Default Description
JOB_CTRL_DB_PATH job-ctrl.db SQLite database path
JOB_CTRL_ADDR :8080 Listen address
JOB_CTRL_NO_REPLY_DAYS 30 Days before an unanswered application becomes "No reply" (0 disables)

JobCtrl has no built-in authentication. Keep it on your machine or local network, and do not expose it to the internet without a reverse proxy that adds authentication. The proxy must forward the original Host header, port included (nginx: proxy_set_header Host $http_host;), otherwise saving changes is rejected as a cross-origin request.

Upgrading

Your data lives in a single SQLite file and every upgrade is designed to keep it intact:

  • Automatic backup. Before a schema migration runs on an existing database, the app writes a consistent snapshot next to it (job-ctrl.db.backup-<timestamp>) and logs its path. Keep or delete these files as you like.
  • Migrations are additive. Your history is kept. The only rows ever removed are interviews, contacts and timeline entries that still belonged to applications you had deleted: older versions kept them by mistake, and they are cleaned up once. The one status that was removed in this version, "Withdrawn", is mapped back to "Applied" with a visible entry in each affected application's timeline. Applications left unanswered for more than 30 days then move to "No reply" on their own (see JOB_CTRL_NO_REPLY_DAYS).
  • Old exports still import. A JSON backup made with a previous version restores cleanly: legacy statuses are mapped, and interviews that older versions silently dropped ("Screening" type, "Rejected" outcome) are now kept. Anything the importer cannot take is logged rather than lost.

While the app runs, SQLite keeps two companion files next to the database, job-ctrl.db-wal and job-ctrl.db-shm. To roll back an upgrade, stop the app, delete job-ctrl.db-wal and job-ctrl.db-shm, then copy the backup file over job-ctrl.db. With Docker, the files live in the volume (docker volume ls shows its name):

docker compose down
docker run --rm -v <volume>:/data alpine sh -c \
  'rm -f /data/job-ctrl.db-wal /data/job-ctrl.db-shm && cp /data/job-ctrl.db.backup-<timestamp> /data/job-ctrl.db'

Then pin the previous image tag in docker-compose.yml before starting again, or the new version will migrate the database once more.

Development

make dev       # Go + Vite hot reload
go test ./...  # backend tests

Stack

Go + chi · SQLite (pure Go, no CGO) · Vanilla TypeScript + Vite + Tailwind · Docker

License

MIT

About

Self-hosted job application tracker. Single Go binary, SQLite database, zero cloud dependencies.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages