Jobs
Run one-off and long-running jobs on a Rootsy computer, from its terminal or over MCP, and keep them alive through sleep.
This page covers jobs: work that runs without a visitor waiting on it, such as a data import, a long build, a scrape or a report. A job needs the computer to stay awake while it runs, and it needs to survive a restart. For jobs that run on a timetable, see Scheduled tasks.
Why a job needs a hold
A computer sleeps when nothing holds it awake. A process running in the background doesn’t count as a hold. Claude Code holds the computer while it’s working, and holds it for background tasks it started only while they use CPU. A job that waits on the network, runs inside Docker, or keeps going after Claude Code is done can be put to sleep partway through. Sleep and wake has the full rules.
Two things hold the computer awake for a job. rootsy keep-awake holds it for as long as its command runs. A command started through MCP run_command holds it until the command finishes or reaches its time limit.
Run a job from the terminal
Wrap the command in rootsy keep-awake:
rootsy keep-awake -- ./import.sh
The computer stays awake for exactly as long as the command runs, busy or idle. rootsy keep-awake exits with the command’s exit code, so you can chain on success:
rootsy keep-awake -- npm run build && ./deploy.sh
The command runs with no terminal input, so it can’t prompt for anything. While the hold is on, rootsy status shows it:
wake-hold: active — keep-awake: ./import.sh
Every second the computer is held awake spends Runtime Credits. The hold doesn’t check whether the command is doing anything, so a command that hangs keeps the computer awake until you stop it. See Billing.
Don’t keep the computer awake by requesting your own address on a timer. A sleeping computer can’t run timers, so the requests stop exactly when they’d be needed.
Run a job from an outside agent
An agent connected over MCP runs commands with the run_command tool. Connect with MCP covers setup, every field and the full limits.
The command runs as the dev user in non-interactive bash, with no stdin or terminal. It starts in /home/dev unless you pass cwd. It wakes the computer if it’s asleep and holds it awake until the command exits or hits its time limit. The limit is 300 seconds by default and 900 at most. Rootsy shortens it if the account’s Runtime Credits cover less. The environment is minimal. PATH is /usr/local/bin:/usr/bin:/bin and no shell profile loads, so a tool installed through a profile (nvm, for example) needs its full path.
run_command returns as soon as the command starts. To get the result of a quick command in the same call, pass completion_mode: "check_once". Rootsy then checks once before answering. If the command is still running, the call returns running and the command carries on.
The command keeps running if the client disconnects. Read its state and output with read_command, and stop it with cancel_command. If a response is lost or the state is unknown, call read_command, or call run_command again with the same request_id and input. Neither starts the command a second time. Up to 4 commands run at once on a computer. See Connect with MCP.
At the time limit, the command and everything it started stop, including processes it put in the background. The output that comes back is a tail of at most 64 KiB, and it can be missing lines. Write anything you need to a file.
Run work longer than 15 minutes from outside
To start a job that outlives a run_command time limit, have the command start it as its own systemd unit and return:
sudo systemd-run --unit=import-job --collect --uid=dev --gid=dev --slice=customer.slice \
--working-directory=/home/dev/import \
/usr/local/bin/rootsy keep-awake -- bash -c './import.sh >> /home/dev/import/import.log 2>&1'
The unit runs until import.sh exits, separate from the command that started it. rootsy keep-awake holds the computer awake the whole time. customer.slice is the slice MCP commands run in. It throttles work that uses more than 80% of the computer’s memory, so the platform’s services keep room. Check on the job with later commands:
systemctl status import-job --no-pager
tail -n 50 /home/dev/import/import.log
Stop it early with sudo systemctl stop import-job. Once the job exits, systemctl no longer lists the unit and the log file holds the result. A unit started this way doesn’t start again after the computer restarts.
Write output and state to disk
Treat the disk as the only memory a job has. Write logs to a file instead of relying on terminal scrollback or the run_command output tail. Record progress as you go, for example one line per finished item or a row per batch in SQLite under ~. Make each step safe to repeat, so a rerun skips what’s done. Anything held only in a variable is gone after a restart.
Files under /home survive sleep, restarts and Reset. See Backups and Reset.
If the computer sleeps mid-job
When nothing holds the computer awake, it can sleep even if a job is running. Its processes are frozen or stopped. On wake, a frozen job can carry on where it was, or the computer can start fresh from disk and the job is gone. You can’t choose which. Open network connections are cut either way, so a job that was mid-download or mid-query sees a connection error.
After a restart, rootsy status can show a last stop: line saying how the computer last stopped. See The rootsy command. To finish an interrupted job, rerun it. A job that records progress on disk continues from its last checkpoint.
Recurring jobs
Cron and systemd timers inside the computer don’t run while it sleeps, and they can’t wake it. A job that should run every hour or every night needs a schedule. A schedule sends a signed request to an endpoint on the computer, and the endpoint starts the job. See Scheduled tasks.