Update #1: Added info about adding the gpu configuration device to the container, for hardware acceleration to work (required for some users).
Update #2: Added info about setting the permissions for the gpu device.
Wine lets you run Windows programs on your GNU/Linux distribution.
When you install Wine, it adds all sort of packages, including 32-bit packages. It looks quite messy, could there be a way to place all those Wine files in a container and keep them there?
This is what we are going to see today. Specifically,
- We are going to create an LXD container, called wine-games
- We are going to set it up so that it runs graphics-accelerated programs. glxinfo will show the host GPU details.
- We are going to install the latest Wine package.
- We are going to install and play one of those Windows games.
Creating the LXD container
Let’s create the new container for LXD. If this is the first time you use LXD, have a look at Trying out LXD containers on our Ubuntu.
$ lxc launch ubuntu:x wine-games Creating wine-games Starting wine-games $ lxc list +---------------+---------+--------------------+--------+------------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +---------------+---------+--------------------+--------+------------+-----------+ | wine-games | RUNNING | 10.0.185.63 (eth0) | | PERSISTENT | 0 | +---------------+---------+--------------------+--------+------------+-----------+ $
We created and started an Ubuntu 16.04 (ubuntu:x) container, called wine-games.
Let’s also install our initial testing applications. The first one is xclock, the simplest X11 GUI app. And glxinfo, that shows details about graphics acceleration. We will know that our set up in Wine works, if both xclock and glxinfo work in the container!
$ lxc exec wine-games -- sudo --login --user ubuntu ubuntu@wine-games:~$ sudo apt update ubuntu@wine-games:~$ sudo apt install x11-apps ubuntu@wine-games:~$ sudo apt install mesa-utils ubuntu@wine-games:~$ exit $
We execute a login shell in the wine-games container as user ubuntu, the default non-root username in Ubuntu LXD images.
Then, we run apt update in order to update the package list and be able to install the subsequent two packages that provide xclock and glxinfo respectively. Finally, we exit the container.
Setting up for graphics acceleration
For graphics acceleration, we are going to use the host graphics card and graphics acceleration. By default, the applications that run in a container do not have access to the host system and cannot start GUI apps.
We need two things; let the container to access the GPU devices of the host, and make sure that there are no restrictions because of different user-ids.
First, we run (only once) the following command (source),
$ echo "root:$UID:1" | sudo tee -a /etc/subuid /etc/subgid [sudo] password for myusername: root:1000:1 $
The command adds a new entry in both the /etc/subuid and /etc/subgid subordinate UID/GID files. It allows the LXD service (runs as root) to remap our user’s ID ($UID, from the host) as requested.
Then, we specify that we want this feature in our wine-games LXD container, and restart the container for the change to take effect.
$ lxc config set wine-games raw.idmap "both $UID 1000" $ lxc restart wine-games $
This “both $UID 1000” syntax is a shortcut that means to map the $UID/$GID of our username in the host, to the default non-root username in the container (which should be 1000 for Ubuntu images, at least).
Let’s attempt to run xclock in the container.
$ lxc exec wine-games -- sudo --login --user ubuntu ubuntu@wine-games:~$ xclock Error: Can't open display: ubuntu@wine-games:~$ export DISPLAY=:0 ubuntu@wine-games:~$ xclock Error: Can't open display: :0 ubuntu@wine-games:~$ exit $
We run xclock in the container, and as expected it does not run because we did not indicate where to send the display. We set the DISPLAY environment variable to the default :0 (send to either a Unix socket or port 6000), which do not work either because we did not fully set them up yet. Let’s do that.
$ lxc config device add wine-games X0 disk path=/tmp/.X11-unix/X0 source=/tmp/.X11-unix/X0 $ lxc config device add wine-games Xauthority disk path=/home/ubuntu/.Xauthority source=/home/MYUSERNAME/.Xauthority
We give access to the Unix socket of the X server (/tmp/.X11-unix/X0) to the container, and make it available at the same exactly path inside the container. In this way, DISPLAY=:0 would allow the apps in the containers to access our host’s X server through the Unix socket.
Then, we repeat this task with the ~/.Xauthority file that resides in our home directory. This file is for access control, and simply makes our host X server to allow the access from applications inside that container.
How do we get hardware acceleration for the GPU to the container apps? There is a special device for that, and it’s gpu. The hardware acceleration for the graphics card is collectively enabled by running the following,
$ lxc config device add wine-games mygpu gpu $ lxc config device set wine-games mygpu uid 1000 $ lxc config device set wine-games mygpu gid 1000
We add the gpu device, and we happen to name it mygpu (any name would suffice). [UPDATED] In addition, we set the uid/gui of the gpu device to 1000 (the default uid/gid of the first non-root account on Ubuntu; adapt accordingly on other distributions). The gpu device has been introduced in LXD 2.7, therefore if it is not found, you may have to upgrade your LXD according to https://launchpad.net/~ubuntu-lxc/+archive/ubuntu/lxd-stable Please leave a comment below if this was your case (mention what LXD version you have been running).
Let’s see what we got now.
$ lxc exec wine-games -- sudo --login --user ubuntu ubuntu@wine-games:~$ export DISPLAY=:0 ubuntu@wine-games:~$ xclock ubuntu@wine-games:~$ glxinfo name of display: :0 display: :0 screen: 0 direct rendering: Yes server glx vendor string: SGI server glx version string: 1.4 ... ubuntu@wine-games:~$ echo "export DISPLAY=:0" >> ~/.profile ubuntu@wine-games:~$ exit $
Looks good, we are good to go! Note that we edited the ~/.profile file in order to set the $DISPLAY variable automatically whenever we connect to the container.
We install Wine in the container according to the instructions at https://wiki.winehq.org/Ubuntu.
$ lxc exec wine-games -- sudo --login --user ubuntu ubuntu@wine-games:~$ sudo dpkg --add-architecture i386 ubuntu@wine-games:~$ wget https://dl.winehq.org/wine-builds/Release.key --2017-05-01 21:30:14-- https://dl.winehq.org/wine-builds/Release.key Resolving dl.winehq.org (dl.winehq.org)... 18.104.22.168 Connecting to dl.winehq.org (dl.winehq.org)|22.214.171.124|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 3122 (3.0K) [application/pgp-keys] Saving to: ‘Release.key’ Release.key 100%[=====================================>] 3.05K --.-KB/s in 0s 2017-05-01 21:30:15 (24.9 MB/s) - ‘Release.key’ saved [3122/3122] ubuntu@wine-games:~$ sudo apt-key add Release.key OK ubuntu@wine-games:~$ sudo apt-add-repository https://dl.winehq.org/wine-builds/ubuntu/ ubuntu@wine-games:~$ sudo apt-get update ... Reading package lists... Done ubuntu@wine-games:~$ sudo apt-get install --install-recommends winehq-devel ... Need to get 115 MB of archives. After this operation, 715 MB of additional disk space will be used. Do you want to continue? [Y/n] Y ...
715MB?!? Sure, bring it on. Whatever is installed in the container, stays in the container! 🙂
Let’s run a game in the container
Here is a game that looks good for our test, Season Match 4. Let’s play it.
ubuntu@wine-games:~$ wget http://cdn.gametop.com/free-games-download/Season-Match4.exe ubuntu@wine-games:~$ wine Season-Match4.exe ... ubuntu@wine-games:~$ cd .wine/drive_c/Program\ Files\ \(x86\)/GameTop.com/Season\ Match\ 4/ ubuntu@wine-games:~/.wine/drive_c/Program Files (x86)/GameTop.com/Season Match 4$ wine SeasonMatch4.exe
Here is the game, and it works.It runs full screen and it is a bit weird to navigate between windows. The animations though are smooth.
We did not set up sound either in this post, nor did we make nice shortcuts so that we can run these apps with a single click. That’s material for a future tutorial!