Install
Prebuilt binaries, Nix and NixOS, building from source, and hardware video decoding.
Copied from README.md in veil’s repositoryView on GitHub (opens in a new tab)
veil needs a Wayland compositor with wlr-layer-shell and GStreamer 1.x with the plugin sets base, good, bad and libav at run time (waylandsink is in bad). XDG_RUNTIME_DIR must be set, as it is in any desktop session.
Prebuilt binaries#
Each release (opens in a new tab) has a tarball for x86_64 and one for aarch64 Linux, with the binary, the systemd user unit, the man pages, the shell completions, the documents and an install script, and SHA256SUMS:
version=1.0.0 # the release you want
tarball=veil-$version-$(uname -m)-unknown-linux-gnu.tar.gz
curl -LO https://github.com/ordreth/veil/releases/download/v$version/$tarball
curl -LO https://github.com/ordreth/veil/releases/download/v$version/SHA256SUMS
sha256sum --check --ignore-missing SHA256SUMS
tar -xzf "$tarball" && cd "${tarball%.tar.gz}"
sudo ./install.sh # into /usr/local
./install.sh --prefix ~/.local # or for your user only, without root./install.sh --help lists what goes where, and --uninstall (with the same --prefix) removes it again. Each tarball also has a build-provenance attestation, which says that GitHub's release workflow built it from the tagged commit: gh attestation verify "$tarball" --repo ordreth/veil. Then start the daemon, or the systemd unit, which the script installs with the path of the binary in it. For ~/.local, ~/.local/bin must be on your PATH, and zsh needs the fpath line the script prints; bash, fish, nushell, man and systemd look there by themselves. Started by hand, give the daemon MALLOC_ARENA_MAX=1 as the unit does (as in MALLOC_ARENA_MAX=1 veil daemon), or its memory grows with every video it switches to.
The binary uses the system's GStreamer, GLib and Wayland client library, and needs glibc 2.34 or newer. Install the GStreamer plugins (no development files, no Rust):
sudo apt install libwayland-client0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad gstreamer1.0-libav # Debian, Ubuntu
sudo dnf install libwayland-client gstreamer1-plugins-base gstreamer1-plugins-good \
gstreamer1-plugins-bad-free gstreamer1-plugin-libav gstreamer1-plugin-openh264 # Fedora
sudo pacman -S --needed wayland gstreamer gst-plugins-base gst-plugins-good \
gst-plugins-bad gst-libav # Arch LinuxThe x86_64 tarball was installed with these packages and run in a headless sway on Debian 12 (GStreamer 1.22.0), Debian 13 (1.26.2), Ubuntu 22.04 (1.20.3), Ubuntu 24.04 (1.24.2), Fedora 44 and Arch Linux (both 1.28.8), on 2026-10-19: a picture, an H.264 MP4 and a VP9 WebM showed and played everywhere. Animated GIFs need GStreamer 1.26 or newer: on Ubuntu 22.04 and 24.04 and Debian 12 they stand still or do not start (on Debian 12 also with a whole CPU core busy), because of GStreamer's GIF demuxer; convert them to a video. On Ubuntu 24.04, 1080p videos showed only a half to a quarter of their frames in the headless sway, for a reason not found yet (known issue 8). The release workflow builds the aarch64 tarball with the same script on an ARM machine and runs the tests there; it has not been run with a compositor.
Nix and NixOS#
nix run github:ordreth/veil -- daemon # try it without installing
nix profile add github:ordreth/veil # install it (`nix profile install` in older Nix)In a NixOS configuration, through the flake's overlay:
# flake inputs: veil.url = "github:ordreth/veil";
nixpkgs.overlays = [ veil.overlays.default ];
environment.systemPackages = [ pkgs.veil ];
# The systemd user unit, started with the graphical session:
systemd.packages = [ pkgs.veil ];
systemd.user.services.veil.wantedBy = [ "graphical-session.target" ];Because of the license, nixpkgs counts veil as unfree. The flake's own packages allow it by themselves; with the overlay, allow it in your configuration: nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "veil" ];.
The package is a wrapper around the binary that brings its own GStreamer plugin set and a GStreamer plugin cache of its own (~/.cache/veil/gstreamer-registry.bin), and sets MALLOC_ARENA_MAX=1 unless you set it. It includes the systemd user unit, the shell completions and the man pages. Hardware decoding uses the system's drivers (hardware.graphics, and hardware.nvidia for NVIDIA); with Nix on another distribution it has not been tried.
From source#
You need Rust 1.89 or newer, a C compiler and pkg-config, the development files of Wayland, libxkbcommon and GStreamer, and the GStreamer plugins. These are the packages, checked on 2026-10-19: veil was built with them in a container of each distribution, and the result showed a picture and played an H.264 video, a VP9 video and an animated GIF in a headless sway. Other versions of these distributions were not tried.
Arch Linux (GStreamer 1.28.8, Rust 1.99):
sudo pacman -S --needed base-devel git rust pkgconf wayland libxkbcommon \
gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-libav
sudo pacman -S gst-plugin-va # VA-API hardware decoding (Intel, AMD)Debian 13 (GStreamer 1.26.2). Debian's Rust (1.85) is too old, so Rust comes from rustup (opens in a new tab):
sudo apt install build-essential git pkg-config curl libwayland-dev libxkbcommon-dev \
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
. "$HOME/.cargo/env" # or open a new shellUbuntu 26.04 (GStreamer 1.28.2, Rust 1.93):
sudo apt install build-essential git pkg-config cargo libwayland-dev libxkbcommon-dev \
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libavFedora 44 (GStreamer 1.28.8, Rust 1.99):
sudo dnf install gcc git rust cargo pkgconf-pkg-config wayland-devel libxkbcommon-devel \
gstreamer1-devel gstreamer1-plugins-base-devel gstreamer1-plugins-good \
gstreamer1-plugins-bad-free gstreamer1-plugin-libav gstreamer1-plugin-openh264Fedora's own GStreamer has no software decoder for HEVC, and decodes H.264 with openh264dec (from the Cisco repository Fedora enables). For HEVC in software, the full FFmpeg of RPM Fusion is the usual addition (not tried here). On Debian, Ubuntu and Fedora the VA-API and NVIDIA decoders are in the bad plugins; Arch has VA-API in gst-plugin-va. Hardware decoding was not tried in the containers (they had no GPU).
Then build veil and install the binary, the systemd unit and the man pages (here for your user; adjust the paths for a system-wide install):
git clone https://github.com/ordreth/veil && cd veil
cargo build --release --locked
install -Dm755 target/release/veil ~/.local/bin/veil
mkdir -p ~/.config/systemd/user ~/.local/share/man/man1
sed "s|/usr/bin/veil|$HOME/.local/bin/veil|" contrib/systemd/veil.service > ~/.config/systemd/user/veil.service
~/.local/bin/veil man-pages ~/.local/share/man/man1~/.local/bin must be on your PATH. The completions are under Shell completions and man pages. Started by hand rather than through the unit, give a binary you built yourself MALLOC_ARENA_MAX=1 (as in MALLOC_ARENA_MAX=1 veil daemon), or its memory grows with every video it switches to.
Hardware video decoding#
GStreamer picks a hardware decoder when one works, and veil logs which one it got for every video (video decoder: nvh264dec (hardware)). Intel and AMD need a VA-API driver (intel-media-driver, or Mesa for AMD) and GStreamer's va plugin; NVIDIA needs the proprietary driver, used through GStreamer's nvcodec. Without either, videos are decoded in software and still play, at a higher CPU cost. docs/troubleshooting.md says how to check, and how to force software decoding.