The OCI image HOME should comply with Dockerfile's ENV

Hello,

I was trying to run the qbittorrent linunxserver.io Docker/OCI image, but it would not work because environment.HOME is set to /root when I create the container, however it’s supposed to be /config according to their Dockerfile. Changing the variable solves the issue.

What’s weird is that XDG_CONFIG_HOME and XDG_DATA_HOME are defined correctly.

Steps to reproduce:

incus remote add linuxserver https://lscr.io --protocol oci --public
incus init linuxserver:linuxserver/qbittorrent:latest qbittorrent
incus config show qbittorrent
# Observe environment.HOME is /root, and XDG_* is /config

Am I missing something, or is it a bug? If so, I can create a Github issue.

I must say I’m in awe of IncusOS, I deeply thanks the maintainers <3

stgraber@castiana:~$ incus create linuxserver:linuxserver/qbittorrent:latest qbittorrent
Creating qbittorrent
stgraber@castiana:~$ sudo mount -t zfs castiana/incus/containers/qbittorrent /mnt/
stgraber@castiana:~$ sudo cat /mnt/config.json | jq .process.env
[
  "PATH=/lsiopy/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
  "TERM=xterm",
  "PS1=$(whoami)@$(hostname):$(pwd)\\$ ",
  "HOME=/root",
  "S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0",
  "S6_VERBOSITY=1",
  "S6_STAGE2_HOOK=/docker-mods",
  "VIRTUAL_ENV=/lsiopy",
  "LSIO_FIRST_PARTY=true",
  "XDG_CONFIG_HOME=/config",
  "XDG_DATA_HOME=/config"
]

So we’re correctly respecting what comes in the OCI config.json for this image.

Thanks a lot, I’ll ask on the linuxserver side.

Is there a way to retrieve the config.json in IncusOS? With incus file mount I can mount the root system, but it doesn’t work for the image properties, right?

You should be able to get the raw image with incus image export linuxserver:linuxserver/qbittorrent:latest that will get you the tarball version of the image that Incus runs, including the OCI config.json.

1 Like

I think I’m missing something. Downloading the image with with the incus cli :

incus image export linuxserver:linuxserver/qbittorrent:latest qbittorrent-oci-via-incus
tar xzf qbittorrent-oci-via-incus
jq '.process.env' config.json

I get get indeed HOME=/root:

[
  "PATH=/lsiopy/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
  "TERM=xterm",
  "PS1=$(whoami)@$(hostname):$(pwd)\\$ ",
  "HOME=/root",
  "S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0",
  "S6_VERBOSITY=1",
  "S6_STAGE2_HOOK=/docker-mods",
  "VIRTUAL_ENV=/lsiopy",
  "LSIO_FIRST_PARTY=true",
  "XDG_CONFIG_HOME=/config",
  "XDG_DATA_HOME=/config"
]

I don’t know much about OCI images format, so I’ve been looking for another way to download the image.

The opencontainers/image-tools repo says to use skopeo to download an OCI image, e.g. skopeo copy docker://XXX:latest oci:XXX:latest. In the documentation of skopeo, they describe docker:// is for “registry implementing the Docker Registry HTTP API V2”.

I’m not 100% sure that’s what I wanted, but here is the interesting part :

skopeo copy docker://lscr.io/linuxserver/qbittorrent:latest oci:qbittorrent-oci-via-skopeo:latest
skopeo inspect oci:qbittorrent-oci-via-skopeo | jq '.Env'

Note : lscr.io is basically redirecting to Github’s registry.
This time I get HOME=/config:

[
  "PATH=/lsiopy/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
  "PS1=$(whoami)@$(hostname):$(pwd)\\$ ",
  "HOME=/config",
  "TERM=xterm",
  "S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0",
  "S6_VERBOSITY=1",
  "S6_STAGE2_HOOK=/docker-mods",
  "VIRTUAL_ENV=/lsiopy",
  "LSIO_FIRST_PARTY=true",
  "XDG_CONFIG_HOME=/config",
  "XDG_DATA_HOME=/config"
]

So I’m confused, what could explain the difference of HOME?

I think that it’s probably a OCI vs Docker registry thing, but not sure if it’s something specific to their build process, the registry, Incus or skopeo.

stgraber@castiana:~/a$ skopeo copy docker://lscr.io/linuxserver/qbittorrent:latest oci:qbittorrent-oci-via-skopeo:latest --insecure-policy
Getting image source signatures
Copying blob b2b23ff83a78 done   | 
Copying blob f6a4c3e338ed done   | 
Copying blob eaaff5b57d4b done   | 
Copying blob f8bb884a82df done   | 
Copying blob dd68a5cb23a9 done   | 
Copying blob a9d3edac96c8 done   | 
Copying blob 3f69ab2581bb done   | 
Copying blob afc64ced3f9c done   | 
Copying blob 0973b7111dc3 done   | 
Copying blob 6704caebe0b5 done   | 
Copying config 7e6801cfb9 done   | 
Writing manifest to image destination
stgraber@castiana:~/a$ sudo umoci unpack --image qbittorrent-oci-via-skopeo/:latest out
stgraber@castiana:~/a$ sudo cat out/config.json | jq .process.env
[
  "PATH=/lsiopy/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
  "TERM=xterm",
  "PS1=$(whoami)@$(hostname):$(pwd)\\$ ",
  "HOME=/root",
  "S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0",
  "S6_VERBOSITY=1",
  "S6_STAGE2_HOOK=/docker-mods",
  "VIRTUAL_ENV=/lsiopy",
  "LSIO_FIRST_PARTY=true",
  "XDG_CONFIG_HOME=/config",
  "XDG_DATA_HOME=/config"
]
stgraber@castiana:~/a$ 

Looks like an umoci bug which has actually been fixed upstream:umoci/CHANGELOG.md at main · opencontainers/umoci · GitHub

It’s just not in a release yet :frowning:

@cyphar nudge? :slight_smile:
Though given we’re using the Go package now, we could pick the unreleased commit until we get a new tag.

Looks like the updated umoci does the trick:

stgraber@castiana:~$ incus create linuxserver:linuxserver/qbittorrent:latest qbittorrent
Creating qbittorrent
stgraber@castiana:~$ incus config show qbittorrent | grep HOME
  environment.HOME: /config
  environment.XDG_CONFIG_HOME: /config
  environment.XDG_DATA_HOME: /config
stgraber@castiana:~$ 

Thanks for getting to the bottom of it!

Bonne journée :grinning_face_with_smiling_eyes: