Skip to content

8. Scheduled jobs

Four jobs keep a v1 site current. They are independent of each other, and each can be run by hand at any time.

Time Job Unit / runner Log
01:30 Database backup eregister-db-backup / /usr/local/bin/eregister-db-backup.sh /var/log/eregister-db-backup.log
02:30 Repo auto-pull eregister-autopull / /usr/local/bin/eregister-autopull.sh /var/log/eregister-autopull.log
03:30 Clinical form import eregister-form-import / /usr/local/bin/eregister-form-import.sh /var/log/eregister-form-import.log
04:30 Concept dictionary import eregister-concept-import / /usr/local/bin/eregister-concept-import.sh /var/log/eregister-concept-import.log

The order is deliberate:

  • The backup runs first, so the night's dump predates whatever the other three do.
  • The auto-pull runs before the two imports, so each import sees the repos that were pulled the same night.

8.1 systemd or cron

Where systemd is present (Ubuntu's default), each job is a .service + .timer pair in /etc/systemd/system/. Where it is not, the job is an /etc/cron.d/ entry.

Both run the same standalone runner script, so behaviour does not change with the scheduler.

A host with neither gets the exact cron line to add by hand:

[⚠] Neither systemd nor /etc/cron.d is available. The runner was installed at
[⚠] /usr/local/bin/eregister-form-import.sh but NOT scheduled — add your own entry:
[⚠]   30 3 * * * root /usr/local/bin/eregister-form-import.sh

8.2 The auto-pull job

Keeps the asset and config repos in sync with their remotes — fetch --depth 1 plus a fast-forward reset --hard onto the tracked branch, which mirrors what the installer does and keeps the checkout shallow.

Covered repos:

standard-config-ls
implementer-interface-release
openmrs-v1-modules
clinical-obs-forms
dhisconnector_mappings_v1
eregister_concepts_release_v1
openmrs_reporting_release

Deliberately excluded:

  • bahmni-docker-ls — the stack itself, pinned to the deployed release. It must not drift underneath a running instance.
  • bahmni_config (0.92) — historical, restore-only.

A dirty working tree is left untouched. Never clobber uncommitted local work.

Info

Pulling a repo changes files on disk. It does not deploy anything. The forms still need an import, the dictionary still needs a load, and the stack still needs docker compose up -d. That is what the nightly imports and catch-up.sh are for.

8.3 What catch-up checks

For each job, one row:

  ✔ OK    cron  eregister-db-backup      systemd timer active (next: Mon 2026-09-22 01:30:00)
  ⟳ FIXED cron  eregister-form-import    was missing — installed (systemd timer)
  — SKIP  cron  eregister-concept-import disabled (--no-concepts)
  ✘ GAP   cron  eregister-autopull       not scheduled (declined)

Note the shape of the form-importer rows in particular: neither --no-forms nor a failed importer install may skip the credentials row. A site that asked to leave its forms alone still needs the script and env file in place for a later manual run.

8.4 Inspecting a job

systemd

# is the timer active, and when does it next fire?
systemctl status eregister-form-import.timer
systemctl list-timers 'eregister-*'

# what happened on the last run?
journalctl -u eregister-form-import.service -n 50

# run it now, out of schedule
sudo systemctl start eregister-form-import.service

cron

cat /etc/cron.d/eregister-form-import
sudo tail -n 50 /var/log/eregister-form-import.log

Either way

Every runner is safe to invoke directly:

sudo /usr/local/bin/eregister-db-backup.sh
sudo /usr/local/bin/eregister-autopull.sh
sudo /usr/local/bin/eregister-form-import.sh
sudo /usr/local/bin/eregister-concept-import.sh

8.5 Changing a schedule

Set the environment variable and re-run catch-up.sh, which rewrites the unit:

# form import at 05:15 instead of 03:30
sudo EREGISTER_FORM_IMPORT_ONCALENDAR='*-*-* 05:15:00' \
     EREGISTER_FORM_IMPORT_CRON='15 5 * * *' \
     ./catch-up.sh

Set both variables. The systemd one is used where systemd is present, the cron one where it is not, and a host can change scheduler between runs.

8.6 Disabling a job

Job Disable with
Database backup --no-db-backup / EREGISTER_DB_BACKUP=0
Auto-pull EREGISTER_AUTO_PULL=0
Form import --no-forms / EREGISTER_IMPORT_FORMS=0
Concept import --no-concepts / EREGISTER_CONCEPT_IMPORT=0

Disabling silences the corresponding report rows too, so the exit code does not fail for a state you chose.

8.7 The form-import runner in detail

The wrapper eregister-form-import.sh is generated, and re-generated by every catch-up.sh run. One run:

  1. Sources /etc/eregister/form-import.env for the credentials
  2. If EREGISTER_FORM_IMPORT_SELF_PULL=1 (the default), refreshes the clinical-obs-forms clone itself — so the daily import still picks up new forms on a host where auto-pull was declined
  3. cds into /var/lib/v1/form-import so importErrors.txt lands somewhere writable rather than wherever cron happened to start
  4. Runs the importer over the whole folder with -k -r

A detached HEAD, or a modification to a tracked file, skips the refresh and imports what is on disk:

SKIP  refresh (/var/lib/v1/clinical-obs-forms has uncommitted changes to tracked files)

Untracked files do not block it. git reset --hard cannot delete an untracked file, so one is not local work a refresh could destroy — and treating it as "dirty" is what froze sites whose EMR drops <form-uuid>.json into the clone: permanently dirty tree, refresh skipped every night, new forms never arriving.