This guide covers display setup, GPU acceleration, sound (PulseAudio), and desktop environment auto-boot for Droidspaces containers. On Android, as of v6.3.0, the X server, VirGL server, and PulseAudio daemon all launch automatically when a container starts - no manual Termux commands required.
Before setting up any display, GPU, or audio feature, ensure all of the following are met:
curl -fsSL https://github.com/ravindu644/Droidspaces-OSS/raw/refs/heads/dev/scripts/setup-termux.sh | bash
As of v6.3.0, Droidspaces automatically launches the Termux:X11 X server (and VirGL server, if configured) when a container starts. It also injects the required environment variables into the container via /run/droidspaces.env, which is symlinked from /etc/profile.d/droidspaces_env.sh for automatic sourcing.
zsh, fish, or another non-login shell?
These shells may not source /etc/profile.d automatically. Run this manually inside your container after booting: source /run/droidspaces.env
This sets DISPLAY=:5 and (if VirGL is enabled) GALLIUM_DRIVER=virpipe.Software rendering via llvmpipe. The most compatible method - works on any device regardless of GPU vendor.
glxgears
startxfce4
The window will appear in the Termux:X11 app.
Hardware-accelerated rendering for non-Qualcomm devices (Mali, PowerVR) via a virglrenderer bridge. Translates OpenGL calls from the container into commands the host Android GPU can execute.
Droidspaces automatically starts both the X server and the VirGL server, and injects DISPLAY=:5 and GALLIUM_DRIVER=virpipe into the container environment.
glxgears
Look for "VirGL" in the renderer string to confirm acceleration is active.
startxfce4
--angle-vulkanNear-native hardware acceleration for Qualcomm Adreno GPUs using the open-source Turnip Mesa driver. This bypasses VirGL entirely for direct GPU access.
MESA_LOADER_DRIVER_OVERRIDE=kgsl
TU_DEBUG=noconform
sudo usermod -aG droidspaces-gpu Droidspaces bridges Android's audio stack into your container using PulseAudio. When enabled, a PulseAudio daemon runs on the host as the Termux user (so Android's audio HAL grants it access to the device speaker), and its UNIX socket is bind-mounted into the container at /tmp/.pulse-socket. The environment variable PULSE_SERVER=unix:/tmp/.pulse-socket is injected automatically, so any application inside the container that speaks PulseAudio will produce sound with no manual configuration.
/tmp/.pulse-socket to appear before proceeding.
pactl set-default-sink AAudio_sink to route audio to the device speaker.
PULSE_SERVER.
You can also enable PulseAudio via the CLI flag --pulse-audio.
PULSE_SERVER is already set in the container environment, most apps work without any additional configuration:
# Test audio output
paplay /path/to/sound.wav
# Verify the PulseAudio connection
pactl info
libskcodec.so via LD_PRELOAD before starting PulseAudio. This fixes a hidden dependency in the OpenSL ES audio module specific to Samsung firmware. No action is required from you.As of v6.3.0, when Termux:X11 is enabled in a container's configuration, Droidspaces guarantees the X server socket (/tmp/.X11-unix/X5) is live before the container's init system reaches graphical.target. This makes proper systemd-based DE auto-start possible with no race conditions.
Our official XFCE rootfs tarballs ship with a fully pre-configured auto-boot setup. Here's exactly how it works:
1. The xfce-autostart.service systemd unit is installed at /etc/systemd/system/xfce-autostart.service and enabled under graphical.target:
[Unit]
Description=XFCE Autostart
After=graphical.target
[Service]
Type=simple
User=root
ExecCondition=/bin/sh -c "grep -q 'enable_termux_x11=1' /run/droidspaces/container.config"
ExecCondition=/bin/sh -c "test -S /tmp/.X11-unix/X5"
ExecStart=/usr/local/bin/xfce-start
Restart=on-failure
[Install]
WantedBy=graphical.target
Two ExecCondition guards ensure XFCE only starts when it makes sense: the container must have Termux:X11 enabled in its config, and the X server socket must actually exist. If either condition fails, systemd skips the service silently - no errors, no crash loops.
2. The /usr/local/bin/xfce-start launcher script handles environment sourcing and user switching:
#!/bin/sh
ENV_FILE=/run/droidspaces.env
CONFIG=/run/droidspaces/container.config
if [ -f "$ENV_FILE" ]; then
. "$ENV_FILE"
WHITELIST=$(sed -n 's/^export \([A-Za-z_][A-Za-z0-9_]*\)=.*/\1/p' "$ENV_FILE" | tr '\n' ',' | sed 's/,$//')
else
export DISPLAY=:5
WHITELIST=DISPLAY
if grep -q 'enable_pulseaudio=1' "$CONFIG" 2>/dev/null; then
export PULSE_SERVER=unix:/tmp/.pulse-socket
WHITELIST="$WHITELIST,PULSE_SERVER"
fi
if grep -q 'enable_virgl=1' "$CONFIG" 2>/dev/null; then
export GALLIUM_DRIVER=virpipe
WHITELIST="$WHITELIST,GALLIUM_DRIVER"
fi
fi
if [ -n "$XFCE_USER" ]; then
exec su -l -w "$WHITELIST" "$XFCE_USER" -c 'exec /usr/bin/startxfce4'
else
exec /usr/bin/startxfce4
fi
The script first tries to source /run/droidspaces.env (written by Droidspaces at boot; contains DISPLAY=:5, GALLIUM_DRIVER=virpipe if VirGL is enabled, and PULSE_SERVER if PulseAudio is enabled). If the env file is missing for any reason, it falls back to reading the container config directly and building the environment manually. This makes the script robust against any init system startup ordering.
XFCE_USER VariableBy default, xfce-start runs XFCE as root. If you want XFCE to run as a non-root user, set the XFCE_USER environment variable in your container's Environment Variables configuration in the Droidspaces app:
XFCE_USER=youruser
When XFCE_USER is set, the script uses su -l -w "$WHITELIST" to switch to that user while explicitly passing through only the required environment variables (DISPLAY, GALLIUM_DRIVER, PULSE_SERVER, etc.). This keeps the session clean - no root env leaking into the user session.
youruser exists inside the container and has a valid home directory before setting XFCE_USER. You can create one with useradd -m youruser inside the container.You can replicate this exact pattern for any DE (Plasma, GNOME, MATE, i3, etc.) in any container.
Step 1: Create the launcher script at /usr/local/bin/de-start:
#!/bin/sh
ENV_FILE=/run/droidspaces.env
CONFIG=/run/droidspaces/container.config
if [ -f "$ENV_FILE" ]; then
. "$ENV_FILE"
WHITELIST=$(sed -n 's/^export \([A-Za-z_][A-Za-z0-9_]*\)=.*/\1/p' "$ENV_FILE" | tr '\n' ',' | sed 's/,$//')
else
export DISPLAY=:5
WHITELIST=DISPLAY
if grep -q 'enable_pulseaudio=1' "$CONFIG" 2>/dev/null; then
export PULSE_SERVER=unix:/tmp/.pulse-socket
WHITELIST="$WHITELIST,PULSE_SERVER"
fi
if grep -q 'enable_virgl=1' "$CONFIG" 2>/dev/null; then
export GALLIUM_DRIVER=virpipe
WHITELIST="$WHITELIST,GALLIUM_DRIVER"
fi
fi
DE_CMD="startplasma-x11" # replace with your DE's start command
if [ -n "$XFCE_USER" ]; then
exec su -l -w "$WHITELIST" "$XFCE_USER" -c "exec $DE_CMD"
else
exec $DE_CMD
fi
chmod +x /usr/local/bin/de-start
Step 2: Create the systemd service at /etc/systemd/system/de-autostart.service:
[Unit]
Description=Desktop Environment Autostart
After=graphical.target
[Service]
Type=simple
ExecCondition=/bin/sh -c "grep -q 'enable_termux_x11=1' /run/droidspaces/container.config"
ExecCondition=/bin/sh -c "test -S /tmp/.X11-unix/X5"
ExecStart=/usr/local/bin/de-start
Restart=on-failure
[Install]
WantedBy=graphical.target
Step 3: Enable it:
systemctl enable de-autostart.service
On next container boot with Termux:X11 enabled, your DE will appear in the Termux:X11 app automatically.
XFCE_USER variable name is a convention from our official tarballs. You can rename it to anything in your own script - what matters is the su -l -w "$WHITELIST" pattern for clean user switching with env passthrough.On Linux-based hosts, GPU acceleration works natively with zero additional configuration within Droidspaces.
--hw-access CLI flag).
xhost +local:
DISPLAY number to the container's environment (usually :0):
echo "DISPLAY=:0" >> /etc/environment
© 2026 Droidspaces · GPLv3 · by ravindu644 and contributors