Introduction
DAYMARK already has a safe publishing wrapper in this fresh environment. The team now needs a real schedule that runs under a dedicated reporting account. Manual success in your terminal does not establish that a scheduled job can find or read its input.
You will configure a matching systemd service and timer, observe repeated real triggers, then recover from directory and input failures without losing the last report. Daymark is the business website; Workstation starts in /srv/daymark-sales. Basic terminal use is sufficient; shell wrapper concepts from the preceding guided lab are reused. The provided calculator and wrapper need no installation.
Run the Export from a Real Timer
In this step, you will establish the correct business result and let systemd run the export twice. Start in Daymark. Open Records: the initial source contains 11 items worth 3450 cents. Reports still offers the previous close, TOTAL,1,100. The job must replace that file with the actual close.
Switch to Workstation. This environment is independent: the correct wrapper is already prepared, but no reporting timer is installed. cat prints files; pwd prints your current directory:
pwd
cat publish.sh
The wrapper writes beside the destination and renames only after successful calculation. It accepts input and output arguments. id displays account identity; ls -ld displays directory ownership and access bits. A directory needs search permission to traverse it and write permission to create files.
id daymark-report
ls -ld reports
The prepared system account owns reports/. Read permission on input and search permission on parent directories permit it to read orders.csv; report-directory ownership permits publication. Do not use root or world-write permissions as a shortcut.
systemd is this instance's service manager. A .service file describes one execution; its same-named .timer describes when to request it. Type=oneshot waits for this finite job to finish. User chooses the real execution account. WorkingDirectory selects where the relative orders.csv resolves. ExecStart uses absolute executable, wrapper and output paths. A terminal's current directory and interactive shell settings do not carry into a system service. Leave RemainAfterExit unset: a completed job must become inactive so the next timer event can run it again.
sudo tee FILE writes a protected file while also printing it. <<'UNIT' is a quoted here-document; paste through the matching closing word. Only create the named D10 units:
sudo tee /etc/systemd/system/d10-sales.service <<'UNIT'
[Unit]
Description=Daymark sales export
[Service]
Type=oneshot
User=daymark-report
WorkingDirectory=/srv/daymark-sales
ExecStart=/bin/bash /srv/daymark-sales/publish.sh orders.csv /srv/daymark-sales/reports/sales.csv
UNIT
This training timer starts five seconds after activation and requests another run twenty seconds after the preceding activation. AccuracySec=1s allows a small scheduling window; the machine may add delay. This is a short rehearsal, not a daily production schedule. A daily wall-clock schedule could use OnCalendar=*-*-* 23:00:00 in place of these two relative directives, with the server timezone deliberately chosen; we keep the short schedule throughout this lab so you can observe actual runs.
sudo tee /etc/systemd/system/d10-sales.timer <<'UNIT'
[Unit]
Description=Daymark short training schedule
[Timer]
OnActiveSec=5s
OnUnitActiveSec=20s
AccuracySec=1s
[Install]
WantedBy=timers.target
UNIT
daemon-reload rereads unit definitions. enable --now connects this timer to timers.target for future boots and starts it now. It starts the timer, not a manual export:
sudo systemctl daemon-reload
sudo systemctl enable --now d10-sales.timer
sleep 50 waits without running the job. Do not start the service manually or use a shell loop in place of the timer.
sleep 50
list-timers shows actual LAST and NEXT triggers. --no-pager prints directly rather than opening a scrolling viewer:
systemctl list-timers d10-sales.timer --no-pager
The journal is systemd's actual event log. -u selects this unit and -n 20 limits output. It should contain at least two complete executions, with uid=... cwd=/srv/daymark-sales, a report hash and successful completion:
sudo journalctl -u d10-sales.service -n 20 --no-pager
Compare the numeric uid with id daymark-report. inactive (dead) after a successful oneshot is normal; an active timer requests future executions. show -p selects specific state properties:
systemctl show d10-sales.service -p User -p WorkingDirectory -p Result -p ExecMainStatus --no-pager
Expect User=daymark-report, the correct directory, Result=success and ExecMainStatus=0. If no trigger appears, read systemctl status d10-sales.timer --no-pager; correct only these unit files, rerun daemon-reload, and restart only d10-sales.timer. restart stops and starts that timer to apply timer edits. Wait again and inspect the journal. For CHDIR, check WorkingDirectory; for Permission denied, inspect input and report-directory ownership rather than broadening permissions.
Return to Daymark, reload Reports and download the new CSV. Its total is TOTAL,11,3450. Open Records again and return home; the normal business pages remain available.
Recover Through the Next Scheduled Run
In this step, you will observe real scheduled failures, preserve the previous report, then publish a new close after recovery. Start in Workstation. Work only on the D10 units and this exercise's input; never stop unrelated services.
First create a working-directory mistake. sed -i edits the named file in place; s|old|new| replaces matching text. This is an intentional fault:
sudo sed -i 's|WorkingDirectory=/srv/daymark-sales|WorkingDirectory=/tmp|' /etc/systemd/system/d10-sales.service
sudo systemctl daemon-reload
sleep 30
sudo journalctl -u d10-sales.service -n 15 --no-pager
The process still runs under the reporting account, but orders.csv resolves inside /tmp and fails with Export failed: ... No such file .... A missing working directory itself would instead cause CHDIR before the wrapper runs. daemon-reload updates definitions; it does not run the job. The timer caused this failed attempt.
cat reports/sales.csv
It must still end TOTAL,11,3450. In Daymark, reload Reports and download to confirm the same bytes are still usable. Return to Workstation and correct the directory:
sudo sed -i 's|WorkingDirectory=/tmp|WorkingDirectory=/srv/daymark-sales|' /etc/systemd/system/d10-sales.service
sudo systemctl daemon-reload
Now exercise an input failure. cp copies a prepared malformed fixture over only orders.csv; the calculator must return nonzero and the wrapper must leave the last report intact:
cp fixtures/broken.csv orders.csv
sleep 30
sudo journalctl -u d10-sales.service -n 15 --no-pager
Expect the intentional invalid-number failure. show exposes the real last exit status, independent of whether the journal-view command succeeded:
systemctl show d10-sales.service -p Result -p ExecMainStatus --no-pager
Expect Result=exit-code and ExecMainStatus=1. Confirm the previous report remains:
cat reports/sales.csv
Restore a valid new close with two additional Tea. Recovery must update the total to 13 items and 3950 cents at the next trigger:
cp fixtures/next.csv orders.csv
sleep 30
sudo journalctl -u d10-sales.service -n 15 --no-pager
cat reports/sales.csv
Expect Tea,5,1250 and TOTAL,13,3950, with a later real publication in the journal. If it remains old, read the newest error, fix the directory, input spelling or wrapper, and wait for another timer event. Do not replace the CSV by hand or manually start the service. The failed oneshot is eligible to run again on this timer.
In Daymark, reload Records and Reports; download again and compare the changed source with TOTAL,13,3950. Keep the timer active for assessment. This exercise does not test reboot recovery, timezone policy, concurrency across machines or missed-day accounting.
Summary
You practiced complete CSV publication, meaningful failure status and recovery. A scheduled report is useful only when its actual identity, trigger and business totals agree. This single-instance exercise uses synthetic data and a short training schedule; it is not a full production reporting platform.



