Examples
Runnable companions to Quick start §4, in
examples/.
None of these need credentials written into them — datarobot-sync resolves
DATAROBOT_ENDPOINT/DATAROBOT_API_TOKEN itself, environment first, then
~/.config/datarobot/drconfig.yaml (the file dr auth login writes). DR_SYNC_CATALOG_ID
is not a credential — it only names which container to sync — so it does appear directly
in a couple of these, as a placeholder to replace.
Taskfile.yml | sync:push / sync:pull / sync:watch / sync:compact / sync:gc targets to copy into a project |
docker/ | a resident watch container and a periodic-push cron container, both tested live against a real container |
systemd/ | the same two shapes as native user-level systemd units (Linux) |
launchd/ | the resident-watch shape as a per-user LaunchAgent (macOS) |
Docker
Two shapes, side by side in docker-compose.yml:
watch— resident mirror. Runs the publishedghcr.io/datarobot-labs/dr-syncimage directly with a different argv; no custom image needed. Docker’srestart: unless-stoppedis the supervisor.cron-backup— periodic snapshot push. A scheduleddr-sync upload, built fromDockerfile.cron. This one does need an image of its own, because the published image is builtFROM cgr.dev/chainguard/static— no shell, no package manager, nothing but the binary — and a scheduler needs somewhere to run commands.
cd examples/docker
docker compose up -d
docker compose logs -f watchdocker run -e DATAROBOT_API_TOKEN=... never reaches it — the classic “cron works
in a container but the job can’t see its own credentials” trap. Dockerfile.cron runs
supercronic instead: a normal foreground
process that spawns each job inheriting its own environment, exactly like any other
subprocess. The image itself is cgr.dev/chainguard/busybox, not the fully shell-less
static the published datarobot-sync image uses, because supercronic execs every crontab line
through /bin/sh -c.Both examples mount at /tmp/data, not /data — see
Installation for why — so the exact same compose file
and docker run invocations work whether they run on your own infrastructure or inside a
DataRobot execution environment, with nothing to change. Credentials can go through
environment: / docker run -e, or a mounted read-only drconfig.yaml, which keeps the
token out of docker inspect entirely; both are wired up in docker-compose.yml.
To validate a crontab without waiting for the schedule:
docker build -f Dockerfile.cron -t dr-sync-cron .
docker run --rm --entrypoint supercronic dr-sync-cron -test /app/crontabsystemd (Linux)
Two independent pairs — pick the one matching what you want, not both:
dr-sync-watch.service— a residentwatch, restarted on failure. The systemd equivalent of the Dockerwatchservice above.dr-sync-backup.service+.timer— a periodic one-shotupload, systemd’s native replacement for a cron line. The service unit does nothing on its own; the timer is what triggers it, and is the one you enable.
These are user units, so they run as you and inherit ~/.config/datarobot/drconfig.yaml
exactly the way an interactive shell would — nothing to configure beyond the container id.
mkdir -p ~/.config/systemd/user
cp dr-sync-watch.service ~/.config/systemd/user/ # edit REPLACE_WITH_YOUR_CONTAINER_ID first
systemctl --user daemon-reload
systemctl --user enable --now dr-sync-watch.serviceloginctl enable-linger $USER keeps user units running after you log out — otherwise
systemd stops them along with your session, which defeats the point of a resident watch.
launchd (macOS)
com.datarobot.dr-sync.watch.plist runs a resident dr-sync watch as a per-user
LaunchAgent — the macOS equivalent of the systemd watch unit above. There is no launchd
periodic-push equivalent shipped here; StartCalendarInterval covers that if you want one,
or just use the Docker or plain-cron shape instead.
cp com.datarobot.dr-sync.watch.plist ~/Library/LaunchAgents/ # edit the two REPLACE_WITH_* placeholders first
launchctl load ~/Library/LaunchAgents/com.datarobot.dr-sync.watch.plistNo DATAROBOT_ENDPOINT or DATAROBOT_API_TOKEN anywhere in the plist: launchd runs this as
you, so drconfig.yaml resolves them exactly as it would in an interactive shell.