ScreenshotNeo

BlogHow-to

How to schedule website screenshots with wkhtmltoimage on Linux

Schedule website screenshots on Linux with wkhtmltoimage using cron or a systemd timer, with setup steps, examples, and troubleshooting.

By the ScreenshotNeo team4 October 20268 min read

To schedule website screenshots with wkhtmltoimage on Linux, first confirm the binary and target page work on the host, then run the command from cron or a systemd timer. Use absolute paths, a service account that can write the output, and logs you can inspect. wkhtmltoimage renders with Qt WebKit and is designed to run headlessly, but it is a legacy renderer: test the exact site and installed binary before relying on its output.

1. Install and verify wkhtmltoimage

Check whether your distribution provides the command and where it is installed. The upstream project repository is archived, and its usage manual identifies version 0.12.6, so check the package and binary available for your Linux release rather than assuming a current package or browser engine.

command -v wkhtmltoimage
wkhtmltoimage --version

If the first command returns no path, install the package using your distribution’s package manager or follow the package guidance for that distribution. Package names and build variants differ. The upstream project describes its tools as headless, so the documented workflow generally does not need a virtual display server.

2. Run a capture manually before scheduling it

Test the full command as the same account that will own the scheduled job. Substitute a page you are allowed to capture and an output directory writable by that account.

/usr/bin/wkhtmltoimage https://example.org /var/www/captures/example.png
file /var/www/captures/example.png

Use command -v wkhtmltoimage to replace /usr/bin/wkhtmltoimage if your binary lives elsewhere. Confirm the output exists and is a valid image. A successful process exit alone does not establish that a modern, JavaScript-heavy page rendered as intended.

3. Schedule a daily capture with cron

A crontab schedule has five time and date fields followed by the command. This example runs daily at 07:00 in the cron host’s applicable local time and appends standard output and errors to a log:

0 7 * * * /usr/bin/wkhtmltoimage https://example.org /var/www/captures/example.png >> /var/log/site-capture.log 2>&1

Edit the crontab for the account that should run the job:

crontab -e

For a system crontab such as one under /etc/cron.d/, the format typically includes an additional username field between the schedule and command. Check the manual for the cron implementation installed on your distribution.

Make cron’s environment explicit

Cron jobs may have a smaller or different environment than an interactive shell. Set any required environment variables in the crontab and use absolute paths for the executable, destination, and any files the command reads. Exact defaults vary by implementation and distribution.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

0 7 * * * /usr/bin/wkhtmltoimage https://example.org /var/www/captures/example.png >> /var/log/site-capture.log 2>&1

Ensure the job owner can create or replace the image and append to the log. If the log directory is not writable, redirect to a location the account can access or use the system’s logging and alerting facilities.

Cron schedule details to account for

  • The five fields are minute, hour, day of month, month, and day of week.
  • When both day-of-month and day-of-week are restricted, cron generally runs when either matches; consult the local manual for implementation details.
  • Calendar matching follows the host’s time configuration. Around daylight-saving changes, a local time that does not occur can be skipped, and a time that occurs twice can run twice.
  • A per-user crontab runs as its owner. System crontabs add an explicit account field.

4. Schedule with a systemd timer

On a systemd host, a timer activates a service. This pair of example units requests a daily capture at 07:00. Replace the URL, binary, output path, and account for your host; verify that the chosen account exists and can write to the destination.

# /etc/systemd/system/site-capture.service
[Unit]
Description=Capture website screenshot

[Service]
Type=oneshot
User=www-data
ExecStart=/usr/bin/wkhtmltoimage https://example.org /var/www/captures/example.png
# /etc/systemd/system/site-capture.timer
[Unit]
Description=Run website screenshot capture daily

[Timer]
OnCalendar=*-*-* 07:00:00
Persistent=true

[Install]
WantedBy=timers.target

Persistent=true can catch up a missed calendar activation when the timer becomes active again after the host was off. Confirm support and behavior against the systemd version installed on your machine.

After saving the units, reload systemd, enable the timer, and inspect its next activation:

sudo systemctl daemon-reload
sudo systemctl enable --now site-capture.timer
systemctl list-timers site-capture.timer

To run the service immediately while checking your configuration, use:

sudo systemctl start site-capture.service
systemctl status site-capture.service
journalctl -u site-capture.service

Calendar expressions use the host’s time and systemd configuration. For elapsed-time schedules, systemd also offers monotonic timer expressions such as time after boot or time since a unit was activated. Use the installed systemd manual to choose the expression that matches the intended schedule.

5. Choose cron or systemd

Consideration Cron systemd timer
Host support Use when cron is installed and already administered on the host. Use when the host runs systemd and timer units are part of its operations.
Missed calendar run Do not assume a run skipped while the host is off will be replayed. Persistent=true can catch up a missed calendar activation, subject to systemd version and configuration.
Logs and diagnosis Redirect output to a writable log or configure host-specific alerting. Inspect the service with systemctl and its journal.
Identity and environment The crontab owner runs the job; set needed environment assumptions explicitly. Set the service identity with User= and configure the unit’s required environment.
Schedule semantics Five fields; local-time daylight-saving transitions can skip or repeat a matching time. Calendar timers express wall-clock schedules; verify local time and behavior for your installed systemd.

For a simple schedule on a host already using cron, cron is a direct option. A native systemd timer is useful when you want service-unit controls, journal visibility, timer inspection, or calendar catch-up behavior. Neither choice removes the need to validate permissions and the rendered image.

6. Rendering options and page behavior

The upstream manual documents options for JavaScript and a rendering delay, among others. Check the manual matching your installed binary with wkhtmltoimage --extended-help or its distribution documentation before using an option: flags and behavior can depend on the build.

wkhtmltoimage --extended-help

For a page that needs JavaScript, test the installed renderer with JavaScript enabled and an appropriate delay. For example, the documented option forms in the upstream 0.12.6 manual include:

/usr/bin/wkhtmltoimage --enable-javascript --javascript-delay 2000 https://example.org /var/www/captures/example.png

This is an example, not a guarantee that a site’s scripts will finish or that its modern browser features are supported. A fixed delay can make every run slower without ensuring readiness. Validate the actual output and tune the delay to the page’s behavior.

Other considerations include page access that requires authentication, content that varies by location or time, resources blocked by the network, and websites that reject automated requests. Do not put credentials in a world-readable crontab or unit file; use host-appropriate secret handling and restrictive file permissions if authentication is necessary.

7. Troubleshooting scheduled captures

Symptom Likely cause What to check or change
Works in a terminal, not in cron Different PATH, working directory, environment, or account. Use absolute paths, declare needed environment values, and run the command as the crontab owner.
Permission denied writing the image The job account cannot write to the destination directory or replace the file. Choose a directory writable by the intended account and check parent-directory permissions.
No image and no obvious error Output was redirected elsewhere, the job did not match, or the scheduler is unavailable. Inspect the configured log, verify cron service and crontab installation, or inspect systemctl status and journalctl -u site-capture.service.
Timer does not appear or run Units were not reloaded, enabled, or named as expected; the timer calendar may also be wrong. Run systemctl daemon-reload, enable the timer, check systemctl list-timers, then inspect the unit status and journal.
Image is blank or incomplete Navigation failed, content needed more time, or the legacy renderer lacks compatibility with the page. Run manually on the same host, inspect logs and network access, try documented JavaScript/delay options, and verify the exact output.
Page differs between runs Dynamic content, time-sensitive page state, geo-specific responses, or resource/network variation. Record the capture time and host context, then determine whether the page offers a stable capture state. Do not assume a delay alone makes dynamic output deterministic.
Capture is skipped or duplicated near a clock change Cron local time crossed a daylight-saving transition. Choose a schedule and time-zone policy deliberately; account for cron’s documented skip/repeat behavior.

8. Reliability, performance, and cost

wkhtmltoimage is a local command-line renderer, so the scheduling examples do not add a per-screenshot API charge. You still use the host’s CPU, memory, network, and storage. Limit concurrency if capturing many pages, keep enough disk space for retained images and logs, and rotate or prune output according to your retention needs.

Rendering time depends on the page, network, JavaScript, and delay settings; the dossier provides no benchmark. A long fixed delay increases job duration, while a short one may capture incomplete content. If a run can overlap the next scheduled run, consider a host-level locking approach or distinct output names so simultaneous processes do not overwrite the same file. For operational reliability, retain logs, check exit status, and monitor whether expected output files are being refreshed.

The project repository is archived and the renderer uses legacy Qt WebKit. Treat compatibility and package availability as things to verify on your distribution, especially before making captures a dependency in a production workflow.

9. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns an image or PDF; the API also supports scheduled workflows when called by your own scheduler. See the API documentation for parameters and formats.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

Cookie banners are accepted and removed before the shot, along with 60+ known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are never billed, and response headers identify the page verdict and billing status. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Every feature is on every plan.

Sign up for 1,000 free screenshots a month, with no card required.

Frequently asked questions

Does wkhtmltoimage need X11 or a virtual display?

The upstream project documents it as a headless command-line renderer. The described workflow generally does not require a display service.

Will it capture every modern website correctly?

No such compatibility guarantee is established. It uses legacy Qt WebKit, so test the exact page with the installed build and inspect the resulting file.

How can I see when a systemd timer will run next?

Use systemctl list-timers or specify the timer unit name to inspect scheduled activations.

What happens to a cron run at a daylight-saving transition?

A local scheduled time that does not occur can be skipped, and one that occurs twice can run twice. Consider the host’s time-zone behavior when choosing the schedule.