Schedule an off-host server copy with a cron job
Last updated: 2026-09-01
Copies you have to remember to make don't happen. Cron does remember, and the Relayium CLI is built for that: a single non-interactive command that copies (or mirrors) a directory to another machine and verifies each file it transfers.
Be clear about what a scheduled run gives you. push and sync both put the files as they are right now onto another machine; neither keeps an earlier version. A scheduled sync in particular carries a deletion or an in-place corruption at the source over to the copy on its next run, and --delete makes the deletion half explicit. If you need last week's version of a file, either schedule push into a dated destination as below, or keep snapshots on the destination.
This guide covers scheduling relayium push and the incremental relayium sync from cron, why a repeated push needs a fresh destination, the two transports you can point either one at, and the crontab lines to copy.
push vs sync: a dated copy or one mirror kept current
Both push and sync move a directory to another machine, but only one of them is built to run twice into the same place.
push sends a full SSH or daemon-direct copy, and with relayium on the remote it refuses a destination that already exists rather than overwriting or resuming. That makes it the wrong shape for a nightly job pointed at one fixed directory — the first run succeeds and every run after it is refused — and exactly the right shape for a job that writes into a fresh, dated directory each time, which is also the only arrangement here that leaves you an earlier copy to go back to. push is also the one that works against a bare server with no relayium, via a tar fallback that verifies nothing per file.
sync instead keeps one destination directory as an incremental one-way mirror of the source: files whose size and modification time are unchanged are skipped, only what changed is sent, and a partial file left by an interrupted run is continued on the next run. sync always needs relayium's native protocol on both ends — it has no tar fallback. Being a mirror, it is current rather than historical: delete or corrupt a file at the source and the next run propagates that.
- Use push into a dated destination when you want each run to stand on its own and older copies to survive.
- Use sync for a large or frequently-changing directory kept as one current copy, where re-sending everything every night would be wasteful.
- Both verify what they transfer with a per-file SHA-256 on the native protocol. Neither push nor pull resumes; sync is the only one of the three that continues a partial file, and the tar fallback verifies and resumes nothing.
Before you start
Everything below is the relayium CLI, so install it first if you haven't. On macOS or Linux, one command drops a prebuilt binary on your PATH:
curl -fsSL https://relayium.com/install.sh | sh
- Prefer to pick the file yourself, or on Windows? Grab a binary from the releases page — relayium.com/cli lists every install option (or go build ./cmd/relayium if you have Go).
- relayium --version confirms it's installed. Skip this and the commands below just print 'command not found'.
Two transports: SSH or daemon-direct
Point either command at an SSH destination (scp-style, using your ~/.ssh/config) or, if the other machine is running relayium serve, straight at it over the daemon-direct protocol — no SSH needed.
# SSH destination — uses your existing SSH keys and config
relayium push ./data user@backup-server:/srv/backups/
# daemon-direct — the destination runs "relayium serve", no SSH required
relayium push ./data relayium://backup-server:9031
- Daemon-direct connections are pinned TLS 1.3 with trust-on-first-use, then pinned to that fingerprint on every run after.
- sync accepts the same two destination forms as push.
Schedule it with cron
What you need before step 1
- The CLI on this machine, and on the destination too if you plan to use sync. sync has no tar fallback.
- An SSH key with no passphrase, or a destination running relayium serve. cron has no agent and no terminal, so it cannot answer a passphrase prompt.
- A source directory that exists at the moment cron fires — not one on a network mount that is only there while you are logged in.
- Somewhere to write a log. A cron job whose output goes nowhere is a backup you will find out about when you need it.
Both push and sync are single, non-interactive commands, so they drop straight into a crontab. Point them at an SSH key with no passphrase (or an agent), and log the output so failures are visible. Note the destination in the push line: it carries the date, because push refuses a destination that already exists. The % has to be escaped as \% in a crontab, where a bare % means end-of-command:
# a dated full copy every night at 2am — a fresh destination each run, so the
# collision check never refuses it — add to your crontab (crontab -e)
0 2 * * * relayium push -i ~/.ssh/backup_key ~/documents user@backup-server:/srv/backups/$(date +\%F)/ >> ~/relayium-backup.log 2>&1
# incremental mirror every 15 minutes instead
*/15 * * * * relayium sync -i ~/.ssh/backup_key ~/documents user@backup-server:/srv/backups/ >> ~/relayium-sync.log 2>&1
Find out where relayium actually is. cron does not use your shell's PATH, and install.sh falls back to ~/.local/bin when /usr/local/bin is not writable — which is exactly the case cron cannot see.
command -v relayiumConfirm the key works with nobody at the keyboard. BatchMode=yes fails instead of prompting, which is what cron would do.
ssh -i ~/.ssh/backup_key -o BatchMode=yes user@backup-server trueRun the whole command by hand once, written exactly as cron will run it, absolute path included.
/usr/local/bin/relayium push -i ~/.ssh/backup_key ~/documents user@backup-server:/srv/backups/Only then add the schedule. Keep the absolute path and the redirect.
crontab -eAfter the first scheduled tick, read the log instead of assuming. This is the step people skip, and it is the one that would have told them.
tail -n 20 ~/relayium-backup.log
What a working setup looks like
relayium resolves to an absolute path you can paste into the crontab, and the BatchMode ssh check exits 0 without printing anything or asking for anything. A backup that only works from your interactive shell is not scheduled yet.
$ command -v relayium
/usr/local/bin/relayium
$ ssh -i ~/.ssh/backup_key -o BatchMode=yes user@backup-server true
$ echo $?
0- The command exits non-zero if any file fails its integrity check, so cron's mail-on-failure catches problems.
- An interrupted sync catches up on the next scheduled run: what already matches is skipped and a partial file is continued. An interrupted push does not resume — but because each night writes into its own dated directory, the next night is a clean full copy rather than a refusal.
- Dated directories accumulate. Prune them on the destination on whatever schedule you can afford, or the disk answers the question for you.
Mirroring deletions and real-time sync
By default sync only ever adds or updates files at the destination. Add --delete to make it a true mirror that also removes files the source no longer has — and who has to agree to that depends on which destination you used. Over relayium:// the receiver is a separate process someone started, so it must have been started as serve --allow-delete; without that the deletions are skipped and reported back to you as denied. Over SSH there is no separate listener to consent: sync starts the receiver itself, through your own SSH session, as you — there is no --allow-delete to set, and passing --delete does delete. The SSH destinations in this guide are that second case.
Two things bound the damage either way. Deletion is confined to the top-level directories the run actually sends, so a sibling directory on the destination is never touched; and sync refuses --delete outright if the source resolves to no files, so a typo in the source path or an unmounted source cannot empty the destination.
If you'd rather not wait for cron's next tick, --watch keeps relayium sync running and re-syncs automatically a moment after any file under the source changes — a lightweight alternative to polling on a schedule.
- relayium sync ./data user@backup-server:/srv/backups/ --delete mirrors deletions, and over SSH it needs nobody's permission but yours. Leave --delete off if a mistaken deletion at the source is a worse outcome than a stale file on the destination.
- relayium sync ./data relayium://backup-server:9031 --delete deletes only if that listener was started with serve --allow-delete; otherwise it is reported back as denied.
- relayium sync ./data user@backup-server:/srv/backups/ --watch stays running and re-syncs on change instead of running once from cron.
When it doesn't work
Every one of these is invisible until you look at the log, which is why the log redirect is in the crontab line rather than being optional. The fifth is worse than invisible: it looks like success.
Symptom, check, fix
- The log says relayium: command not found, but the same command works in your shell.
tail -n 5 ~/relayium-backup.log # /bin/sh: relayium: command not foundcron runs with a minimal PATH, usually just /usr/bin:/bin. If install.sh could not write to /usr/local/bin it put the binary in ~/.local/bin, which cron will never find. Use the absolute path from command -v in the crontab line, or set PATH= on a line at the top of the crontab.
- The log shows the ssh connection being refused, or nothing after the first run.
ssh -i ~/.ssh/backup_key -o BatchMode=yes user@backup-server true # Permission denied (publickey).cron has no ssh-agent and no terminal, so a key with a passphrase can only hang or fail. Point -i at a passphrase-less key reserved for backups, and confirm with BatchMode=yes, which refuses to prompt rather than waiting for someone who is not there.
- sync runs cleanly but files deleted at the source are still on the destination.
grep -i deni ~/relayium-sync.logOver a relayium:// destination, deletion is a receiver-side opt-in: without serve --allow-delete on the other end the deletions are skipped and reported back as denied, which is why the log has the answer and the exit code does not. Restart that listener with --allow-delete. Over an SSH destination there is no listener to ask, so a denial is not the explanation — check that --delete is actually on the crontab line, since without it sync only ever adds and updates.
- sync refuses --delete outright.
relayium sync ~/documents user@backup-server:/srv/backups/ --delete # refusing --delete with an empty source: this would delete everything on the destination. Check the path(s).The source resolved to no files, so the mirror would have emptied the destination. That refusal is deliberate. Check the path for a typo, and check that anything mounted there is actually mounted at the time cron fires rather than only when you are logged in.
- The backup runs, exits 0, and is not what you think it is.
ssh user@backup-server command -v relayiumpush falls back to a plain tar stream over SSH when the remote has no relayium. Your files arrive, so nothing complains — but that path has no per-file SHA-256 verification and no up-front collision check, which are the two reasons to schedule this rather than scp, and tar -x -k can leave a batch partly applied. Install the CLI on the destination to get them back. sync does not have this failure mode because it has no fallback at all: it fails loudly instead.
Frequently asked questions
Does the backup server need relayium installed?
It depends on the command. push works either way: with relayium installed it uses the native protocol (an up-front collision check, plus a SHA-256 on every file it transfers); without it, push falls back to a plain tar stream over SSH, so a bare server still works but nothing is verified per file. sync always needs relayium's native protocol on the remote — there's no tar fallback for sync, so install it there first.
Is the copy encrypted and verified?
In transit, yes, and per file with a caveat worth knowing. Pushing over SSH or daemon-direct means the bytes are already protected by that connection's encryption, with nothing extra to configure. On the native protocol every file the run transfers is checked with a SHA-256 hash end to end — but the tar fallback hashes nothing, and sync decides what to send from size and modification time, so a file it skips is never read and therefore never hashed. Matching directory sizes are a sanity check, not proof that a skipped file's contents still match.
What happens if the cron job is interrupted halfway through?
It depends which command you scheduled. sync continues: the next run skips what already matches and carries on a partial file, and --no-resume turns that off. push does not resume in either protocol — it refuses a destination that already exists, which is why the push line above writes into a dated directory, so the next night is a clean full copy rather than a refusal. --no-resume is accepted by push and does nothing.
Can --delete accidentally wipe my destination?
sync refuses to run with --delete if the source directory contains no files, and the receiver has to be started with serve --allow-delete for deletions to take effect at all — otherwise they're skipped and reported back to you.
Do I need an account or does this cost anything?
No. The CLI is free and needs no account for push, pull, or sync — the transfer runs over your own SSH connection or a direct daemon connection, not through Relayium's servers.
Is this a backup?
It is the off-host copy half of one. A scheduled sync keeps one directory current, which means it also propagates a deletion or an in-place corruption at the source on its next run; a scheduled push into a dated directory does keep older copies, but only for as long as you keep the directories, and nothing here prunes or verifies them for you. Treat it as a copy you own on hardware you control, and pair it with snapshots or a versioning tool if you need to recover a file as it was last week.
Put an off-host copy on a schedule you don't have to remember — encrypted in transit, verified per file, and free.
Get the CLI