undocker, run a Docker image without Docker

I self-host on *.benpro.fr a lot of things with LXD, right now I have 20 LXC containers managed by LXD on a single machine.

When I want to self-host something, there are three possibilities:

But thanks to a new discovery, this has changed!

I discovered undocker and skopeo. With these two tools you can easily extract a Docker image (a succession of “layers”), and run the app out of Docker.

I tried with Photoview. Here is the basic guideline:

Here are the steps for Photoview:

$ lxc launch images:debian/11 gallery
$ lxc exec gallery bash
# apt install mariadb-server skopeo wget
# mysql -e "CREATE DATABASE photoview; GRANT ALL PRIVILEGES ON photoview.* TO 'photoview'@localhost IDENTIFIED BY '<PASSOWRD>'"
# wget https://git.sr.ht/~motiejus/undocker/refs/download/v1.0.1/undocker-linux-amd64-v1.0.1 -O /tmp/undocker
# install -m 755 /tmp/undocker /usr/local/bin/undocker
# adduser --disabled-password photoview
# sudo -iu photoview
$ skopeo copy docker://docker.io/viktorstrate/photoview:latest docker-archive:photoview.tar
$ mkdir photoview cache
$ cd photoview
$ undocker ~/photoview.tar - | tar -xv
$ cat << EOT > ~/.env
PHOTOVIEW_LISTEN_IP=127.0.1.1
PHOTOVIEW_LISTEN_PORT=1234
PHOTOVIEW_SERVE_UI=1
PHOTOVIEW_UI_PATH=/ui
PHOTOVIEW_DATABASE_DRIVER=mysql
PHOTOVIEW_MYSQL_URL='photoview:<PASSWORD>@tcp(127.0.0.1)/photoview'
PHOTOVIEW_MEDIA_CACHE=/home/photoview/cache
MAPBOX_TOKEN=<TOKEN>
EOT
# cat << EOT > /etc/systemd/system/photoview.service
[Unit]
Description=Photoview
After=network.target
Requires=mariadb.service

[Service]
Type=simple
User=photoview
EnvironmentFile=/home/photoview/.env
BindReadOnlyPaths=/proc
WorkingDirectory=/app
RootDirectory=/home/photoview/photoview
ExecStart=/app/photoview
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOT

# systemctl daemon-reload
# systemctl enable --now photoview
# systemctl status photoview
● photoview.service - Photoview
     Loaded: loaded (/etc/systemd/system/photoview.service; enabled; vendor preset: enabled)
    Drop-In: /run/systemd/system/service.d
             └─zzz-lxc-service.conf
     Active: active (running) since Sun 2021-09-05 12:49:47 UTC; 8s ago
   Main PID: 4643 (photoview)
      Tasks: 9 (limit: 7084)
     Memory: 54.1M
     CGroup: /system.slice/photoview.service
             ├─4643 /app/photoview
             └─4653 /usr/bin/perl -w /usr/bin/exiftool -stay_open True -@ - -common_args -n

Sep 05 12:49:47 test photoview[4643]: 2021/09/05 12:49:47 Scan interval runner: Waiting for signal
Sep 05 12:49:47 test photoview[4643]: 2021/09/05 12:49:47 Scan interval runner: New ticker detected
Sep 05 12:49:47 test photoview[4643]: 2021/09/05 12:49:47 Scan interval runner: Waiting for signal
Sep 05 12:49:47 test photoview[4643]: 2021/09/05 12:49:47 Found executable worker: darktable (this is darktable-cli>
Sep 05 12:49:47 test photoview[4643]: 2021/09/05 12:49:47 Found executable worker: ffmpeg (ffmpeg version 4.3.2-0+d>
Sep 05 12:49:47 test photoview[4643]: 2021/09/05 12:49:47 Found exiftool
Sep 05 12:49:47 test photoview[4643]: 2021/09/05 12:49:47 Initializing face detector
Sep 05 12:49:48 test photoview[4643]: 2021/09/05 12:49:48 Photoview API endpoint listening at http://127.0.1.1:1234>
Sep 05 12:49:48 test photoview[4643]: 2021/09/05 12:49:48 Photoview API public endpoint ready at /api
Sep 05 12:49:48 test photoview[4643]: 2021/09/05 12:49:48 Photoview UI public endpoint ready at /

And voilà! 🎉

Now, you still have the the same issue as using Docker, the Dockerfile may have used old software and libraries when compiling the application... But, somehow I find it better, and it integrate well with my LXD usage.

Note that it is also possible to not use RootDirectory in the systemd unit file and install all the required dependencies (ffmpeg, darktable, ...) and it may also work, having more control on these dependencies, instead of relying of what is shipped in the Docker image.