Run a Headless X Display with Xvfb and Check It Safely
You will start a disposable X display with no physical monitor, point a test or batch job at it, verify the connection, and stop it cleanly afterwards. This is useful for software that needs X11 but is running on a server, in a build job, or over an ordinary SSH session.
The route
Jump straight to the step you need, or tick off Done means at the end.
The examples use Xvfb from Ubuntu package xvfb version 2:21.1.12-1ubuntu1.6. Its installed manual describes the X server as xorg-server 21.1.11, so use the local command and manual as the final authority if your package version differs.
Allow about fifteen minutes. You need a shell, the xvfb package, and a client such as xdpyinfo for the verification step. The server normally runs as your user, so none of these examples need sudo.
1. Check the local installation
Confirm which executable is being used and record the package version. These are read-only checks:
$ command -v Xvfb
/usr/bin/Xvfb
$ dpkg-query -W -f='${Package} ${Version}\n' xvfb
xvfb 2:21.1.12-1ubuntu1.6
Do not use Xvfb -version as a version check. This build treats it as an unknown server option and prints the normal X server usage text. The package query and the manual's header are more useful.
2. Choose an unused display number
An X display name such as :99 is a local server number, not a network address. Xvfb will listen on that display and clients will find it through the DISPLAY environment variable. Pick a number that is not already in use by another X server.
Check the candidate before starting anything:
$ DISPLAY_NUM=:99
$ if DISPLAY="$DISPLAY_NUM" xdpyinfo >/dev/null 2>&1; then
> echo "display already answers: $DISPLAY_NUM"
> exit 1
> fi
$ echo "using $DISPLAY_NUM"
using :99
Checkpoint: if the command reports that the display already answers, choose another number. Do not kill an unknown X server just to make this example fit. It may belong to another user or service.
3. Start Xvfb with an explicit screen
Start screen 0 at 1280 by 1024 pixels and 24-bit depth. Xvfb's default is already 1280x1024x24, but writing it explicitly makes test runs easier to review:
$ LOG_FILE="/tmp/xvfb${DISPLAY_NUM#:}.log"
$ Xvfb "$DISPLAY_NUM" -screen 0 1280x1024x24 >"$LOG_FILE" 2>&1 &
$ XVFB_PID=$!
$ sleep 1
$ if ! kill -0 "$XVFB_PID" 2>/dev/null; then
> echo "Xvfb stopped; log follows:"
> sed -n '1,80p' "$LOG_FILE"
> exit 1
> fi
$ echo "Xvfb PID: $XVFB_PID"
Xvfb PID: 12345
The PID is an example and will differ. If startup fails, read the log before retrying. A common cause is a display-number collision. Another is a malformed -screen value; the format is WxHxD, with width, height and depth as integers.
Keep the PID in the same shell session as the work that uses the display. If you close the shell without arranging cleanup, the server can remain running until it is stopped separately.
4. Point a client at the display
Export DISPLAY for the test or command that needs X11, then ask xdpyinfo to make a harmless connection:
$ export DISPLAY="$DISPLAY_NUM"
$ xdpyinfo | sed -n '1,12p'
name of display: :99
version number: 11.0
vendor string: The X.Org Foundation
vendor release number: 12101011
X.Org version: 21.1.11
maximum request size: 16777212 bytes
motion buffer size: 256
Your release or formatting may differ. The useful checks are that the command exits successfully, names the display you selected, and reports an X11 server. If xdpyinfo says it cannot open the display, check DISPLAY, the PID, and the startup log.
Now run the real client in the same environment. For a script, prefer a narrowly scoped variable when possible:
$ DISPLAY="$DISPLAY_NUM" /path/to/your-test --headless-x11
Replace the placeholder with a real program and its documented arguments. Xvfb provides an X server and virtual framebuffer; it does not make a graphical program automatically safe, fast, or compatible with every hardware-accelerated feature.
5. Stop the temporary server
When the client has finished, stop the process you started and confirm that it has gone away:
$ kill "$XVFB_PID"
$ for attempt in 1 2 3 4 5; do
> if ! kill -0 "$XVFB_PID" 2>/dev/null; then break; fi
> sleep 1
> done
$ if kill -0 "$XVFB_PID" 2>/dev/null; then
> echo "Xvfb did not stop; inspect before using force"
> exit 1
> fi
$ echo "stopped $XVFB_PID"
stopped 12345
This is the undo for the example. If the process ignores the normal termination signal, inspect it with ps -p "$XVFB_PID" -o pid,stat,cmd before considering a forceful action. Do not use kill -9 against a PID you have not checked. The log in /tmp is ordinary temporary data and can be removed once you no longer need it.
Useful framebuffer options
-screen can create another screen, for example -screen 0 1600x1200x24. If you specify screen 1 as well, screen 0 still exists with its default configuration unless you configure it explicitly. Most clients expect screen 0, so configure that screen first.
Use -pixdepths only when a client or test genuinely needs additional pixmap depths. Its argument is a space-separated list of integers from 1 to 32, such as -pixdepths 3 27. Do not confuse these extra pixmap depths with the screen depth in -screen.
-fbdir DIRECTORY changes the storage model. Xvfb creates one memory-mapped XWD framebuffer file per screen, named like Xvfb_screen0. That can support a file copy for a full-screen snapshot. Choose a directory you control, and check its free space and permissions before starting. The files are state produced by the running server, not a substitute for a normal screenshot tool.
-shmem instead puts the framebuffer in System V shared memory and prints a shared-memory ID for each screen. Treat that output as diagnostic data for the consuming tool, not as a secret or a stable identifier. Neither -fbdir nor -shmem is needed for the ordinary client-testing workflow.
Done means
- the installed package and executable were identified;
- the display number was checked rather than assumed to be free;
- Xvfb started with a deliberate screen size and depth;
xdpyinfoconnected through the intendedDISPLAY;- the test or batch client ran in that environment; and
- the recorded Xvfb PID was stopped and checked afterwards.