Skip to content

Install rootless

Rootless runs Quasar under an ordinary account, quasar, reducing its host privileges. Both engines are experimental rootless: see Container engines. For a rootful install, use the quick start instead.

  • Linux with systemd, and root once, for the steps below.
  • Podman, or Docker with its rootless extras (dockerd-rootless-setuptool.sh).
  • setfacl (the acl package) for the device rules.
  • On SELinux (Fedora and its relatives): semanage and restorecon, from policycoreutils-python-utils. On an image-based system: sudo rpm-ostree install policycoreutils-python-utils, then reboot.
  • On NVIDIA: complete NVIDIA setup for rootless engines.

The examples keep homes in /var/lib/quasar/homes and templates beside them. Use a directory of its own, never /home or another system tree.

Run each step from an account with sudo. Every step is safe to repeat.

  1. Create the quasar account and turn on lingering, so its engine runs with nobody logged in and starts at boot:

    Terminal window
    sudo useradd --create-home --user-group --comment Quasar quasar
    sudo loginctl enable-linger quasar
  2. Check its subordinate ID ranges. The containers’ own users map into them.

    Terminal window
    grep '^quasar:' /etc/subuid /etc/subgid

    Most distributions add both with useradd. If either line is missing, add a 65536-wide range that no other line in that file uses, for example:

    Terminal window
    sudo usermod --add-subuids 524288-589823 --add-subgids 524288-589823 quasar
  3. Install the device rules from Device rules. They give the quasar group /dev/uinput, Quasar’s own virtual input devices and the GPU render nodes, and nothing else.

  4. Create the agent’s runtime directory, made again at every boot:

    Terminal window
    echo 'd /run/quasar-agent 0750 quasar quasar -' | sudo tee /etc/tmpfiles.d/quasar.conf
    sudo systemd-tmpfiles --create /etc/tmpfiles.d/quasar.conf
  5. Create the homes and templates roots, owned by quasar:

    Terminal window
    sudo install -d -m 0750 -o quasar -g quasar /var/lib/quasar/homes /var/lib/quasar/templates
  6. SELinux only (Podman, or Docker with SELinux on): label those directories for containers. This changes labels only; SELinux stays enforcing.

    Terminal window
    for d in /run/quasar-agent /var/lib/quasar/homes /var/lib/quasar/templates; do
    sudo semanage fcontext -a -t container_file_t "$d(/.*)?"
    sudo restorecon -R "$d"
    done
  7. Start the engine for quasar.

    Podman, its API socket and the restart of Quasar’s containers at boot:

    Terminal window
    sudo machinectl shell quasar@ /bin/sh -c \
    'systemctl --user enable --now podman.socket podman-restart.service'

    Docker, installed and started for quasar:

    Terminal window
    sudo machinectl shell quasar@ /bin/sh -c \
    'dockerd-rootless-setuptool.sh install && systemctl --user enable --now docker'
  8. NVIDIA only: complete rootless NVIDIA setup, including CDI and any required SELinux policy.

  9. Only for a port below 1024 (the defaults are 8080 and 8443): let unprivileged services bind from that port, here 443:

    Terminal window
    echo 'net.ipv4.ip_unprivileged_port_start=443' | sudo tee /etc/sysctl.d/99-quasar-ports.conf
    sudo sysctl -w net.ipv4.ip_unprivileged_port_start=443

For smoother streams, set the UDP send buffer.

Check Expect
grep '^quasar:' /etc/subuid /etc/subgid A line in each.
ls /var/lib/systemd/linger/ quasar listed.
ls -ld /run/quasar-agent /var/lib/quasar/homes /var/lib/quasar/templates All three owned by quasar.
ls /etc/udev/rules.d/70-quasar.rules The file exists.
ls -Zd /var/lib/quasar/homes (SELinux) container_file_t in the label.
sudo machinectl shell quasar@ /bin/sh -c 'systemctl --user is-enabled podman.socket podman-restart.service' (Podman) enabled twice.

The add-host command checks the same things, and names anything still missing.

deploy/prepare-host.sh does the steps above in one go (step 9 only with --unprivileged-port-start). It also sets the UDP send buffer and loads uinput at boot. Read it before you run it. --dry-run prints what it would change and changes nothing:

Terminal window
sudo sh prepare-host.sh --mode rootless --engine podman \
--homes /var/lib/quasar/homes --templates /var/lib/quasar/templates --dry-run

A machine that shows a desktop on its own screen needs more access, because a rootless engine passes no device rules: the quasar account must be able to open every device the console desktop is given.

  1. Install the console rules from Device rules: the display cards, sound devices, i2c buses, this machine’s input devices and tty8, each by ACL. Or run prepare-host.sh with --console. On SELinux it also loads the quasar-console-devices module, which lets the console session open those input, sound and hidraw nodes and read the udev database; without it the desktop starts with no keyboard, mouse or sound. SELinux stays enforcing.

  2. Keep login prompts off tty8:

    Terminal window
    sudo systemctl mask getty@tty8.service autovt@tty8.service
  3. Free the display: no desktop or login screen may run on the output the console uses.

It worked when every console check on the host’s page passes once console mode is on (see Console mode). A device plugged in later works through the rules alone.

Open a full session as the account (sudo -u quasar is not enough):

Terminal window
sudo machinectl shell quasar@
  1. Start the seed. Fill in QUASAR_PUBLIC_HOST, the name or LAN address you type into the browser.

    Podman: write ~/.config/containers/systemd/quasar-seed.container:

    [Unit]
    Description=Quasar seed
    Wants=network-online.target
    Requires=podman.socket
    After=network-online.target podman.socket
    [Container]
    ContainerName=quasar-seed
    Image=ghcr.io/accreleus/quasar/quasar-recovery:latest
    Pull=always
    Exec=seed
    SecurityLabelDisable=true
    Volume=%t/podman/podman.sock:/var/run/docker.sock
    Volume=quasar-machine:/var/lib/quasar-machine:ro
    Environment=QUASAR_ROLE="combined"
    Environment=QUASAR_PUBLIC_HOST="192.168.1.50"
    Environment=QUASAR_HOME_ROOT="/var/lib/quasar/homes"
    Environment=QUASAR_TEMPLATE_ROOT="/var/lib/quasar/templates"
    Environment=QUASAR_CONTROL_PLANE_IMAGE="ghcr.io/accreleus/quasar/quasar-control-plane:latest"
    Environment=QUASAR_AGENT_IMAGE="ghcr.io/accreleus/quasar/quasar-node-agent:latest"
    [Service]
    Restart=always
    [Install]
    WantedBy=default.target
    Terminal window
    systemctl --user daemon-reload
    systemctl --user start quasar-seed

    Docker:

    Terminal window
    docker run -d --name quasar-seed --restart unless-stopped --pull always \
    --security-opt label=disable \
    -v "${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/docker.sock:/var/run/docker.sock" \
    -v quasar-machine:/var/lib/quasar-machine:ro \
    -e QUASAR_ROLE=combined \
    -e QUASAR_PUBLIC_HOST=192.168.1.50 \
    -e QUASAR_HOME_ROOT=/var/lib/quasar/homes \
    -e QUASAR_TEMPLATE_ROOT=/var/lib/quasar/templates \
    -e QUASAR_CONTROL_PLANE_IMAGE=ghcr.io/accreleus/quasar/quasar-control-plane:latest \
    -e QUASAR_AGENT_IMAGE=ghcr.io/accreleus/quasar/quasar-node-agent:latest \
    ghcr.io/accreleus/quasar/quasar-recovery:latest seed

    Other inputs (control-only, your own Postgres, ports) are the same as on Docker.

  2. Check it came up, then claim it with the one-time setup token (docker for Docker):

    Terminal window
    podman exec quasar-recovery quasar-recovery status
    curl http://localhost:8080/health
    podman exec quasar-control-plane cat /run/quasar/setup-token

    /health answers {"status":"ok","db":"ok"}. Open https://<QUASAR_PUBLIC_HOST>:8443 and continue with First run and checks.

Prepare it as above, then run the Add host command from a sudo machinectl shell quasar@ session. It never uses sudo.