You will run an X client or GUI-aware script inside a temporary virtual X server, without ever opening a window on a physical display. The wrapper starts Xvfb, hands the command a display and an authentication cookie, then removes its own temporary state the moment the command exits.
This guide uses the xvfb-run from the Ubuntu xvfb package, version 2:21.1.12-1ubuntu1.6 on the machine checked for this guide. You also need xauth, which the wrapper requires outright.
Check both dependencies before debugging the command you actually care about:
command -v xvfb-run
command -v xauth
dpkg-query -W -f='${Package} ${Version}\n' xvfb xauth
Expected output includes paths for both commands and their package versions. If xauth is missing, xvfb-run exits with status 3; install the distribution package through your normal system administration process before going any further.
Start with a command that reports the display rather than a long-running GUI. This proves the virtual server was actually created and the child process got its environment:
xvfb-run --auto-servernum sh -c 'printf "DISPLAY=%s\n" "$DISPLAY"'
A successful run prints something like DISPLAY=:99. The exact number varies. With --auto-servernum, the wrapper starts at whatever number you gave --server-num, or 99 by default, and hunts for a free one from there.
The wrapper normally sends xauth and Xvfb output straight to /dev/null. That keeps successful runs quiet, but it also hides the one thing you need when a start fails. Capture that diagnostic output when investigating:
xvfb-run --auto-servernum --error-file=/tmp/xvfb-run-error.log \
sh -c 'printf "DISPLAY=%s\n" "$DISPLAY"'
status=$?
printf 'xvfb-run status=%s\n' "$status"
sed -n '1,120p' /tmp/xvfb-run-error.log
exit "$status"
That log path is temporary and not managed by xvfb-run at all. Remove it once you are done with it:
rm -f /tmp/xvfb-run-error.log
Put the command after the wrapper's own options. Anything after the command belongs to that command, not to xvfb-run. This example asks the X11 diagnostic client to inspect the virtual display:
xvfb-run --auto-servernum xdpyinfo | sed -n '1,20p'
Look for a display name, dimensions and screen information in the output. If your application is a script, pass the script and its arguments in the same position:
xvfb-run --auto-servernum /path/to/your-test-script --headless-check
Use a real executable path in place of /path/to/your-test-script. Do not set DISPLAY yourself unless you have a separate reason to; the wrapper already does that for the child process.
By default, the virtual screen is screen 0 at 1280 by 1024 pixels with 24-bit colour. Supply a different screen with one quoted --server-args value:
xvfb-run --auto-servernum \
--server-args='-screen 0 1024x768x24' \
/path/to/your-test-script
Quoting matters here. The wrapper treats whitespace in the server-arguments value as separate arguments to Xvfb; leave the value unquoted and those words can look like wrapper options or a second command.
For a one-off command, --auto-servernum avoids collisions with another X server on its own. A fixed number is worth the extra care when a test harness expects a predictable display:
xvfb-run --server-num=101 xdpyinfo | sed -n '1,12p'
This fails outright if display 101 is already occupied. Prefer automatic allocation for parallel jobs, or give each job its own known, separate number. --auto-servernum --server-num=101 means: start searching at 101, then try higher numbers from there.
Do not put a display such as :101 inside --server-args to pick the number. The manpage warns that X server argument parsing can simply ignore it. Use --server-num instead.
xvfb-run disables TCP listening by default, which is the safer setting for a local test since it never exposes the virtual X server on a network socket. --listen-tcp turns that back on, and it deserves to be treated as a deliberate security boundary change, not a routine troubleshooting switch.
If a legacy client genuinely needs TCP, check its network and authentication requirements first, restrict exposure outside this wrapper wherever you can, and record the change in your test configuration. The X authority cookie protects access, but that is no reason to expose a service you did not need to.
Without --auth-file, the wrapper creates a temporary directory below TMPDIR, or below /tmp when TMPDIR is unset or empty. It stores the X authority cookie there, starts Xvfb, runs your command, then kills that server and removes the temporary cookie and directory behind it.
Use --auth-file only when another process must share a specific authority file:
xvfb-run --auth-file=/path/to/existing/Xauthority \
/path/to/your-test-script
That specified file is written to, but it is never created or deleted by xvfb-run itself; xauth may create it if it is missing. Choose a private path with suitable permissions. Once the other process has finished, remove or securely retire that file according to your local policy. Never share an authority file casually: it carries the cookie that authenticates X clients.
For ordinary runs, do not force a persistent authority file into existence. The automatic temporary cleanup is less state to remember and less stale authentication data left lying around.
Once the child command exits, xvfb-run normally returns that command's own status. Preserve it in scripts:
xvfb-run --auto-servernum /path/to/your-test-script
status=$?
printf 'test status=%s\n' "$status"
exit "$status"
Wrapper failures use their own statuses: 1 means Xvfb did not start, 2 means no command was supplied, 3 means xauth is unavailable, 4 means the temporary directory already exists, 5 means cleanup failed, and 6 means option parsing failed. Status 0 is also used for --help; otherwise it is usually the child's own success status.
Status 4 deserves extra attention. The wrapper expects a unique temporary directory, so an existing one can point to a collision or a temporary-file race. Do not delete an unfamiliar directory just to force the next run through. Check its owner, contents and timestamps first. If it is clearly stale and belongs to your own failed run, remove only that specific directory, then retry with diagnostic output turned on.
--auto-servernum, or pick an unused fixed number for a controlled test.xvfb-run, and that nothing overwrote DISPLAY or launched it through a separate environment.--error-file and read the resulting file before touching any server options.xvfb-run and xauth are both installed.DISPLAY and exited cleanly.--server-args is quoted and verified.