- Add remote/orchestrator filter by workplace (remote, not remote, unknown) with URL param - Expose isRemote on job list API; canonicalize URLs and source_job_id dedup on import - Onboarding: optional VITE_SKIP_RXRESUME_ONBOARDING for RxResume-free onboarding - Scoring UI + docs for company skip list; pipeline-run dedup note - Vitest: TZ=UTC for stable time-based tests - DEPLOY_GITEA_VM_CRON_TELEGRAM.md for VM/cron/Telegram ops Made-with: Cursor
5.9 KiB
Deploy on a VM or container, run the pipeline on a schedule, notify Telegram
This guide assumes you already pushed this repo to Gitea, for example:
git remote add gitea gitea@10.0.30.169:ilia/Jobber.git # or: git remote set-url gitea ...
git push -u gitea main
If you have uncommitted changes, commit them first, then push again:
git add -A && git commit -m "Your message" && git push gitea main
1. Deploy on a Linux VM (bare metal or cloud)
-
Install Docker and Docker Compose (plugin v2).
-
Clone from your Gitea server (SSH or HTTPS):
git clone gitea@10.0.30.169:ilia/Jobber.git cd Jobber # or job-ops if you kept that folder name -
Copy and edit environment:
cp .env.example .env # Edit .env: MODEL / LLM keys, RXRESUME_*, search settings, etc. -
Start the stack:
docker compose up -d -
Open the UI:
http://<VM-IP>:3005(port mapped indocker-compose.yml). -
Persist data: the compose file mounts
./data— back up that directory.
2. Deploy as a container (same image, any host)
Same as the VM path: only Docker is required. On the VM:
- Ensure port 3005 (or your chosen host port) is reachable if you use the UI from another machine.
- For only API/cron use from localhost, you can bind to
127.0.0.1:3005by changing theports:line indocker-compose.ymlif you edit it (e.g."127.0.0.1:3005:3001").
Inside the container the app listens on 3001; the host maps 3005 → 3001 by default.
Cron on the host should call the API on the host:
- UI:
http://127.0.0.1:3005(browser) - API (orchestrator):
http://127.0.0.1:3005— same port; requests to/api/...are served by the app behind the reverse proxy built into the image.
If your setup exposes the API only on an internal Docker network, use the container name and port 3001 from another container, or publish 3005 on the host and use 127.0.0.1:3005 from cron.
3. Run the pipeline three times a day (cron)
POST /api/pipeline/run starts the pipeline in the background and returns immediately ({ ok: true, data: { message: "Pipeline started" } }). That is enough for scheduling.
Example crontab entries (host time zone — adjust hours as you like):
# 08:00, 14:00, 20:00 daily — trigger JobOps pipeline
0 8,14,20 * * * /usr/local/bin/jobops-pipeline-run.sh >> /var/log/jobops-pipeline.log 2>&1
Create /usr/local/bin/jobops-pipeline-run.sh:
#!/usr/bin/env bash
set -euo pipefail
BASE_URL="${JOBOPS_URL:-http://127.0.0.1:3005}"
# If you set BASIC_AUTH_USER / BASIC_AUTH_PASSWORD in .env, uncomment:
# AUTH=(-u "${BASIC_AUTH_USER:?}:${BASIC_AUTH_PASSWORD:?}")
curl -sS -X POST "${BASE_URL}/api/pipeline/run" \
-H "Content-Type: application/json" \
-d '{}' \
"${AUTH[@]:-}" \
| tee -a /var/log/jobops-pipeline.log
echo >> /var/log/jobops-pipeline.log
sudo chmod +x /usr/local/bin/jobops-pipeline-run.sh
Optional: set JOBOPS_URL in root’s crontab or in /etc/environment if the app is on another host.
Basic Auth: When BASIC_AUTH_USER and BASIC_AUTH_PASSWORD are set in .env, all non-GET API calls need Basic auth — use curl -u user:pass as above.
4. Telegram notifications
JobOps does not send Telegram directly. Practical options:
Option A — Pipeline webhook (recommended)
- In the app: Settings → Webhooks (or env
PIPELINE_WEBHOOK_URL/WEBHOOK_SECRET) set a URL that receives JSON when a run completes or fails. - Point that URL to a small relay that translates the JSON into a Telegram
sendMessagecall.
Telegram API:
https://api.telegram.org/bot<BOT_TOKEN>/sendMessage
Body (JSON):
{
"chat_id": "<YOUR_CHAT_ID>",
"text": "Pipeline finished: ..."
}
You can host the relay on the same VM (Flask/FastAPI/Node, or n8n / Webhook.site + automation). Keep the bot token and chat id in env vars, not in the JobOps UI if possible.
Webhook payload shape (sanitized) includes fields like event, pipelineRunId, jobsDiscovered, jobsProcessed, error — see server code notify-webhook.ts.
Option B — Cron wrapper: poll status, then Telegram
Because /api/pipeline/run returns before the run finishes, a simple approach:
- Cron calls
jobops-pipeline-run.sh(as above). - A second script (or same script extended) polls
GET /api/pipeline/statusuntilisRunningis false, then readsGET /api/pipeline/runsfor the latest run and sends a short message viacurlto Telegram.
Example send (replace token and chat id):
TELEGRAM_BOT_TOKEN="123456:ABC..."
CHAT_ID="your_numeric_chat_id"
MSG="$(printf 'JobOps pipeline finished. Check dashboard.')"
curl -sS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-H "Content-Type: application/json" \
-d "{\"chat_id\":\"${CHAT_ID}\",\"text\":$(echo "$MSG" | jq -Rs .)}"
Get chat_id: message your bot, then open https://api.telegram.org/bot<TOKEN>/getUpdates and read message.chat.id.
Option C — External automation
Use n8n, Grafana OnCall, or similar: trigger on schedule → HTTP POST /api/pipeline/run → wait/poll → Telegram node.
5. Security notes
- Do not commit
.envor Telegram tokens to Git. - Prefer Basic Auth on the instance if it is reachable from the internet.
- Restrict firewall so only your IP (or VPN) can reach port 3005 if exposed.
6. Git remotes quick reference
git remote -v
git push gitea main # your Gitea
git push origin main # upstream GitHub (if you have rights)
Related project docs
- Self-hosting: docs site Self-Hosting guide (if present in your tree).
- Webhooks: Settings documentation for pipeline / job-complete webhooks.
- Optional env:
PIPELINE_WEBHOOK_URL,WEBHOOK_SECRET,BASIC_AUTH_USER,BASIC_AUTH_PASSWORDin.env.example.