Figment.so
Mac hostingWebsite hostingGuidesHow to usePricing

Schedule Time-Windows for Remote Mac Access

Short answer: install two launchd system daemons with StartCalendarInterval arrays — one that loads Apple's Screen Sharing daemon at 09:00 Mon–Fri, one that unloads it at 18:00 — and test both with launchctl kickstart -k the same day. The complete plists and the test procedure are below. On Figment the task fits a shared physical Mac where a scheduled off-hours lockdown is the point: the i3 at $49/month and M-series from $95/month are configured on request, so ask for a user that can write to /Library/LaunchDaemons/ when you submit the configuration. The $29 virtual Intel macOS VM can also host these jobs inside its browser-console desktop terminal; confirm the exact access path in your quote before you rely on this schedule as a security boundary.

If you rent a Mac for a team of contractors, leaving VNC open around the clock creates two problems: unplanned evening or weekend work that bypasses your approval process, and a larger attack surface for credential-stuffing against VNC passwords. Scheduling Screen Sharing to business hours takes about fifteen minutes to set up and enforces the boundary automatically.

This guide produces two launchd daemon plist files — one to enable Screen Sharing at 09:00 and one to disable it at 18:00, Monday through Friday — plus a test procedure you can run on the same day without waiting for the clock to advance.

Why Time-Restricted Access Reduces Risk and Cost

Screen Sharing being disabled means no new VNC connections can be established. Any contractor who attempts to connect outside business hours receives an immediate "unable to connect" response rather than a login prompt. Combined with a strong VNC password, this reduces the credential-brute-force window from 168 hours per week to roughly 45. It also makes your activity log easier to review: any connection landing inside the Screen Sharing subsystem outside the scheduled window is anomalous by definition.

For rental cost management, contractors who cannot connect off-hours cannot accidentally leave long-running CPU jobs running overnight — a practical guard on a shared compute budget.

macOS Screen Sharing: Enable and Disable from the Terminal

Screen Sharing on macOS is a system LaunchDaemon (com.apple.screensharing) with a plist at /System/Library/LaunchDaemons/com.apple.screensharing.plist. Apple documents turning it on and off in Turn screen sharing on or off on Mac. From the Terminal:

# Enable Screen Sharing
sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.screensharing.plist

# Disable Screen Sharing
sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.screensharing.plist

On macOS 13 Ventura and later Apple prefers the domain-aware form:

sudo launchctl bootstrap system /System/Library/LaunchDaemons/com.apple.screensharing.plist
sudo launchctl bootout system/com.apple.screensharing

Both forms still work on current macOS; use whichever matches the version installed on your rental. Starting with macOS 12.1 Apple also tightened TCC gating around Screen Sharing — on some configurations launchctl only cleanly starts the daemon after the service has been enabled once via System Settings → General → Sharing → Screen Sharing. If your scheduled jobs refuse to start it, flip the toggle on once by hand to grant the one-time permission, then flip it off and let the daemon take over.

Verify the current state:

sudo launchctl list | grep screensharing
# A line with a PID means the daemon is running.
# A line with "-" means it is loaded but not currently running.
# No line at all means it is not loaded.

launchd CalendarInterval: Scheduling Enable and Disable Jobs

StartCalendarInterval is a dict (or array of dicts) with keys Minute, Hour, Day, Weekday, and Month; omitted keys act as wildcards, documented in Apple's Creating launchd Jobs and the launchd.plist(5) man page. Weekday is 0–7, where 0 and 7 both mean Sunday, so Monday–Friday is 1 through 5. A range of weekdays needs one dict per day.

The two plists below belong in /Library/LaunchDaemons/ and run as root, which is required for launchctl calls that start or stop a system service.

/Library/LaunchDaemons/com.company.screensharing-enable.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.company.screensharing-enable</string>
  <key>ProgramArguments</key>
  <array>
    <string>/bin/launchctl</string>
    <string>load</string>
    <string>-w</string>
    <string>/System/Library/LaunchDaemons/com.apple.screensharing.plist</string>
  </array>
  <key>StartCalendarInterval</key>
  <array>
    <dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer><key>Weekday</key><integer>1</integer></dict>
    <dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer><key>Weekday</key><integer>2</integer></dict>
    <dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer><key>Weekday</key><integer>3</integer></dict>
    <dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer><key>Weekday</key><integer>4</integer></dict>
    <dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer><key>Weekday</key><integer>5</integer></dict>
  </array>
  <key>StandardOutPath</key>
  <string>/var/log/screensharing-schedule.log</string>
  <key>StandardErrorPath</key>
  <string>/var/log/screensharing-schedule.log</string>
</dict>
</plist>

/Library/LaunchDaemons/com.company.screensharing-disable.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.company.screensharing-disable</string>
  <key>ProgramArguments</key>
  <array>
    <string>/bin/launchctl</string>
    <string>unload</string>
    <string>-w</string>
    <string>/System/Library/LaunchDaemons/com.apple.screensharing.plist</string>
  </array>
  <key>StartCalendarInterval</key>
  <array>
    <dict><key>Hour</key><integer>18</integer><key>Minute</key><integer>0</integer><key>Weekday</key><integer>1</integer></dict>
    <dict><key>Hour</key><integer>18</integer><key>Minute</key><integer>0</integer><key>Weekday</key><integer>2</integer></dict>
    <dict><key>Hour</key><integer>18</integer><key>Minute</key><integer>0</integer><key>Weekday</key><integer>3</integer></dict>
    <dict><key>Hour</key><integer>18</integer><key>Minute</key><integer>0</integer><key>Weekday</key><integer>4</integer></dict>
    <dict><key>Hour</key><integer>18</integer><key>Minute</key><integer>0</integer><key>Weekday</key><integer>5</integer></dict>
  </array>
  <key>StandardOutPath</key>
  <string>/var/log/screensharing-schedule.log</string>
  <key>StandardErrorPath</key>
  <string>/var/log/screensharing-schedule.log</string>
</dict>
</plist>

Set ownership and permissions before loading (LaunchDaemons must be root-owned and not group/world-writable or launchd will refuse them):

sudo chown root:wheel /Library/LaunchDaemons/com.company.screensharing-*.plist
sudo chmod 644 /Library/LaunchDaemons/com.company.screensharing-*.plist

sudo launchctl bootstrap system /Library/LaunchDaemons/com.company.screensharing-enable.plist
sudo launchctl bootstrap system /Library/LaunchDaemons/com.company.screensharing-disable.plist

Set the Timezone Before You Schedule

launchd fires calendar jobs in the system timezone. If your Mac is in a US data centre but the people using it are in Europe, 09:00 and 18:00 mean the Mac's local clock, not theirs. Set the timezone first:

# Example: UK business hours
sudo systemsetup -settimezone "Europe/London"
sudo systemsetup -gettimezone

sudo systemsetup -listtimezones prints the valid identifiers. After changing the timezone, bootout and bootstrap both plists again so launchd recalculates the next fire time.

Test the Schedule Without Waiting for Tomorrow

You do not need to advance the system clock. launchctl kickstart runs a loaded job immediately; -k kills a running instance first, which is fine here because each job is a short-lived command.

# Run the disable job now
sudo launchctl kickstart -k system/com.company.screensharing-disable
sleep 2
sudo launchctl list | grep screensharing
# Expect: "-" (loaded, not running) or no line at all

# Run the enable job now
sudo launchctl kickstart -k system/com.company.screensharing-enable
sleep 2
sudo launchctl list | grep screensharing
# Expect: a line with a PID

Then test from an actual VNC client at each state. A refused connection while the daemon is stopped is the behaviour you want.

Manual override for emergencies. If someone needs access outside hours, enable it by hand and log the event so the record stays honest:

sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.screensharing.plist
printf '%s MANUAL_OVERRIDE_ENABLE by=%s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$USER" \
  | sudo tee -a /var/log/screensharing-schedule.log >/dev/null

# When done
sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.screensharing.plist
printf '%s MANUAL_OVERRIDE_DISABLE by=%s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$USER" \
  | sudo tee -a /var/log/screensharing-schedule.log >/dev/null

The scheduled jobs re-assert the correct state at the next fire time, so an override cannot silently persist into the next morning.

Log and Review Off-Hours Attempts

The plists write their own output to /var/log/screensharing-schedule.log. To capture connection events, stream the Screen Sharing subsystem to a review log:

sudo log stream --predicate 'subsystem == "com.apple.screensharing"' \
  --style compact >> /var/log/screensharing-attempts.log &

Attempts that arrive while the daemon is stopped never reach the Screen Sharing subsystem; they land in the network layer. For a simple confirm-the-schedule-ran check, add a third launchd job that runs netstat -an | grep '\.5900 .*LISTEN' every 15 minutes and writes a line with the result. If port 5900 is listening at 02:00, the disable job did not fire and the log makes that obvious.

Running This on Figment

Pick the plan by the job the schedule protects.

  • $29 virtual Intel macOS (2 vCPU, 8 GiB RAM, 80 GiB disk) — enough to host a scheduled Screen Sharing policy and run compatible Intel Mac toolchains, including Xcode 26 or earlier for cross-compiled arm64 iOS/macOS builds. Access is a browser console desktop; use its terminal to run the launchctl commands above. No GPU or audio, and ARM-only execution needs an M-series.
  • i3 physical Mac at $49/month — a dedicated small physical host for shared contractor access when you want hardware isolation; configured on request.
  • M-series physical Mac from $95/month — for native Apple silicon execution, Xcode 27 targets, or GPU work; the specific chip, RAM and disk are confirmed in the quote.
  • Managed Build Mac at $249/month — one M-series Mac with CI setup, backups, monitoring and 30 minutes of monthly support. The right tier if you want Figment to install and own this kind of schedule end to end.
  • Managed Mac Team at $999/month — two M-series Macs, two CI runners, team setup, backups, monitoring and one hour of monthly support.

When you request the configuration, tell us you need a user that can write /Library/LaunchDaemons/ and run sudo launchctl bootstrap system ... so these daemons load on first boot.

Start here: /mac-hosting. Configure a plan: /mac-hosting/start.

Related reading


Subscribe for Figma design and website publishing guides.


Figment.so

Contact

Twitter

Website pricing

Compute index

About

Privacy

Terms