Mullvad VPN won't install during custom image build

I am using a custom image based on the image-template repo.
Everything has been working great, I am able to bake in some more apps that I need into the image but Mullvad VPN fails to install during the image build. Even Mullvad browser installs successfully but not Mullvad VPN.
I am using Mullvad’s instructions for adding the repo and installing the package, and I am adding these commands to the build.sh script image-template/build.sh at abd59c19e1fddbf69c8f6cccabc989e96cbe5ba5 · ublue-os/image-template · GitHub
This is the build error:

>>> [RPM] failed to open dir opt of /opt/: cpio: mkdir failed - File exists
>>> [RPM] unpacking of archive failed on file /opt/Mullvad VPN: cpio: mkdir fail
>>> Unpack error: mullvad-vpn-0:2025.4-1.x86_64
Transaction failed: Rpm transaction failed.
Error: building at STEP "RUN mkdir -p /var/lib/alternatives &&     /tmp/build.sh &&     ostree container commit": while running runtime: exit status 1
Error: Error: buildah exited with code 1

And here’s the full log:

+ dnf config-manager addrepo --from-repofile=https://repository.mullvad.net/rpm/stable/mullvad.repo
 https://repository.mullvad.net/rpm/sta 100% | 257.0   B/s | 186.0   B |  00m01s
+ dnf install -y mullvad-vpn
Updating and loading repositories:
 Mullvad VPN                            100% |   3.7 KiB/s |   4.5 KiB |  00m01s
Repositories loaded.
Package      Arch   Version  Repository          Size
Installing:
 mullvad-vpn x86_64 2025.4-1 mullvad-stable 339.1 MiB

Transaction Summary:
 Installing:         1 package

Total size of inbound packages is 86 MiB. Need to download 86 MiB.
After this operation, 339 MiB extra will be used (install 339 MiB, remove 0 B).
[1/1] mullvad-vpn-0:2025.4-1.x86_64     100% |  15.5 MiB/s |  86.2 MiB |  00m06s
--------------------------------------------------------------------------------
[1/1] Total                             100% |  15.5 MiB/s |  86.2 MiB |  00m06s
Running transaction
[1/2] https://repository.mullvad.net/rp 100% |   7.6 KiB/s |   5.1 KiB |  00m01s
--------------------------------------------------------------------------------
[2/2] Total                             100% |  15.5 MiB/s |  86.2 MiB |  00m06s
Importing OpenPGP key 0x66DE8DDF:
 UserID     : "Mullvad (code signing) <admin@mullvad.net>"
 Fingerprint: A11989A9AJ9J02FC318FJ28FJFU3D4F266DE8DDF
 From       : https://repository.mullvad.net/rpm/mullvad-keyring.asc
The key was successfully imported.
[1/3] Verify package files              100% |   3.0   B/s |   1.0   B |  00m00s
[2/3] Prepare transaction               100% |   5.0   B/s |   1.0   B |  00m00s
[3/3] Installing mullvad-vpn-0:2025.4-1 100% | 110.4 GiB/s | 339.1 MiB |  00m00s
>>> [RPM] failed to open dir opt of /opt/: cpio: mkdir failed - File exists
>>> [RPM] unpacking of archive failed on file /opt/Mullvad VPN: cpio: mkdir fail
>>> Unpack error: mullvad-vpn-0:2025.4-1.x86_64
Transaction failed: Rpm transaction failed.
Error: building at STEP "RUN mkdir -p /var/lib/alternatives &&     /tmp/build.sh &&     ostree container commit": while running runtime: exit status 1
Error: Error: buildah exited with code 1

Add this

# Create symlink for /opt to /var/opt since it is not created in the image yet
mkdir -p "/var/opt" && ln -s "/var/opt"  "/opt"

@xlion Thank you, that solved it.
But now I am facing another issue.
The image build successfully finished, I ran ujust update and saw that the image staged for reboot will now include Mullvad VPN, but when I rebooted, the app wasn’t there.
I checked the Mullvad daemon and it actually was active and running but the GUI part of the app nowhere to be found…
The image build logs in Github showed some soft failures:

>>> System has not been booted with systemd as init system (PID 1). Can't operat
>>> Failed to connect to system scope bus via local transport: Host is down
>>> Failed to start mullvad-daemon.service

Understandable, since the daemon can’t run in the image…
But something happened to the GUI part of the app…

And this is the full log:

+ dnf config-manager addrepo --from-repofile=https://repository.mullvad.net/rpm/stable/mullvad.repo
 https://repository.mullvad.net/rpm/sta 100% | 343.0   B/s | 186.0   B |  00m01s
+ mkdir -p /var/opt
+ ln -s /var/opt /opt
+ dnf install -y mullvad-vpn
Updating and loading repositories:
 Mullvad VPN                            100% |   4.6 KiB/s |   4.5 KiB |  00m01s
 Fedora 41 - x86_64 - Updates Archive   100% |  36.1 MiB/s |  35.1 MiB |  00m01s
 Fedora 41 - x86_64                     100% |   4.2 MiB/s |  35.4 MiB |  00m08s
 Fedora 41 - x86_64 - Updates           100% |  15.6 MiB/s |  10.4 MiB |  00m01s
Repositories loaded.
Package      Arch   Version  Repository          Size
Installing:
 mullvad-vpn x86_64 2025.4-1 mullvad-stable 339.1 MiB

Transaction Summary:
 Installing:         1 package

Total size of inbound packages is 86 MiB. Need to download 86 MiB.
After this operation, 339 MiB extra will be used (install 339 MiB, remove 0 B).
[1/1] mullvad-vpn-0:2025.4-1.x86_64     100% |  25.1 MiB/s |  86.2 MiB |  00m03s
--------------------------------------------------------------------------------
[1/1] Total                             100% |  25.1 MiB/s |  86.2 MiB |  00m03s
Running transaction
[1/2] https://repository.mullvad.net/rp 100% |  13.4 KiB/s |   5.1 KiB |  00m00s
--------------------------------------------------------------------------------
[2/2] Total                             100% |  25.1 MiB/s |  86.2 MiB |  00m03s
Importing OpenPGP key 0x66DE8DDF:
 UserID     : "Mullvad (code signing) <admin@mullvad.net>"
 Fingerprint: A1198702N3DSN4R5T8NEDCTGR90N2R4EDE8DDF
 From       : https://repository.mullvad.net/rpm/mullvad-keyring.asc
The key was successfully imported.
[1/3] Verify package files              100% |   3.0   B/s |   1.0   B |  00m00s
[2/3] Prepare transaction               100% |   5.0   B/s |   1.0   B |  00m00s
[3/3] Installing mullvad-vpn-0:2025.4-1 100% |  58.4 MiB/s | 339.1 MiB |  00m06s
>>> Running post-install scriptlet: mullvad-vpn-0:2025.4-1.x86_64
>>> Finished post-install scriptlet: mullvad-vpn-0:2025.4-1.x86_64
>>> Scriptlet output:
>>> Created symlink '/etc/systemd/system/multi-user.target.wants/mullvad-daemon.
>>> System has not been booted with systemd as init system (PID 1). Can't operat
>>> Failed to connect to system scope bus via local transport: Host is down
>>> Failed to start mullvad-daemon.service
>>> Created symlink '/etc/systemd/system/mullvad-daemon.service.wants/mullvad-ea
>>> 
>>> Running post-transaction scriptlet: mullvad-vpn-0:2025.4-1.x86_64
>>> Finished post-transaction scriptlet: mullvad-vpn-0:2025.4-1.x86_64
>>> Scriptlet output:
>>> enabled
>>> 
Complete!

On Fedora Atomic
Not 100% of softwares that installed to /opt can work properly, if this is not works, unfortunately, there is no solutions currently.

Crap…
thanks anyways…
I guess I’ll have to layer that bish…

Layering will not works either.

I actually layered it before and it worked really solid…

Fedora Atomic handling the softwares that installing to /opt weirdly.

I think what you’ll have to do is specify the target opt directory to link. At least that’s how BlueBuild does it (Github), which works for me for RPM Zen and Windscribe.

FWIW it might take a few reboots and/or builds for the fix to work as intended. I recall Windscribe not appearing correctly the first few updates, and suddenly it just appears. You might also want to monitor if the .desktop files exist in the system and can be launched.

Thanks for the suggestion, I’ve tried the BlueBuild script with directory looping - same result, the Mullvad daemon installs and is runnung, but the GUI part of the app is missing.
I guess we’ll just have to accept it…

That’s unfortunate. Maybe using the Bluebuild template as a whole to build your image will help?

Interesting…
So how would I go about adding these official Mullvad commands to a custom Bluebuild?

sudo dnf config-manager addrepo --from-repofile=https://repository.mullvad.net/rpm/stable/mullvad.repo
sudo dnf install mullvad-vpn

Or is it as simple as specifying the Mullvad repo under the modules/repos block in this script? template/recipes/recipe.yml at d51f78b998af590abc0c1901af64632528d318fa · blue-build/template · GitHub

And adding mullvad-vpn under the install block?

Kind of new to this whole business…
Thanks

So just tried BlueBuild.
Added this to the recipe.yaml

    repos:
      - https://repository.mullvad.net/rpm/stable/mullvad.repo
    install:
      - mullvad-vpn

But that didn’t work, the build succeeded but it wasn’t able to install Mullvad VPN:

========================== Start 'rpm-ostree' Module ==========================
Adding repositories
Downloading repo file https://repository.mullvad.net/rpm/stable/mullvad.repo
Downloaded repo file https://repository.mullvad.net/rpm/stable/mullvad.repo
Installing RPMs
Installing: mullvad-vpn
Enabled rpm-md repositories: mullvad-stable updates fedora updates-archive
Updating metadata for 'mullvad-stable'...done
Updating metadata for 'updates'...done
Updating metadata for 'fedora'...done
Updating metadata for 'updates-archive'...done
Importing rpm-md...done
rpm-md repo 'mullvad-stable'; generated: 2025-02-13T14:09:54Z solvables: 2
rpm-md repo 'updates'; generated: 2025-02-20T02:18:59Z solvables: 20949
rpm-md repo 'fedora'; generated: 2024-10-24T13:55:59Z solvables: 76624
rpm-md repo 'updates-archive'; generated: 2025-02-20T03:36:50Z solvables: 32332
Resolving dependencies...done
Will download: 1 package (90.4?MB)
Downloading from 'mullvad-stable'...done
Installing 1 packages:
  mullvad-vpn-2025.4-1.x86_64 (mullvad-stable)
/var/tmp/rpm-tmp.sJDzHA: line 6: /opt/Mullvad VPN/resources/mullvad-setup: No such file or directory
rpm-ostree-systemctl: Ignored non-preset command: stop mullvad-daemon.service
rpm-ostree-systemctl: Ignored non-preset command: disable mullvad-daemon.service
rpm-ostree-systemctl: Ignored non-preset command: disable mullvad-early-boot-blocking.service
cp: cannot stat '/var/log/mullvad-vpn/daemon.log': No such file or directory
Failed to copy old daemon log
Installing: mullvad-vpn-2025.4-1.x86_64 (mullvad-stable)
rpm-ostree-systemctl: Ignored non-preset command: enable /usr/lib/systemd/system/mullvad-daemon.service
rpm-ostree-systemctl: Ignored non-preset command: start mullvad-daemon.service
rpm-ostree-systemctl: Ignored non-preset command: enable /usr/lib/systemd/system/mullvad-early-boot-blocking.service
rpm-ostree-systemctl: Ignored non-preset command: is-enabled mullvad-daemon
=========================== End 'rpm-ostree' Module ===========================
DONE 51.6s

You will have to specify optfix as documented here: rpm-ostree | BlueBuild

I assume the target opt directory would be Mullvad VPN from this line

/var/tmp/rpm-tmp.sJDzHA: line 6: /opt/Mullvad VPN/resources/mullvad-setup: No such file or directory

So this should get this to work:

    repos:
      - https://repository.mullvad.net/rpm/stable/mullvad.repo
    optfix:
      - Mullvad VPN
    install:
      - mullvad-vpn

If it still doesn’t work, you might have to dig around to find the correct opt target.

For comparison, this is what mine looks like

  - type: rpm-ostree
    repos:
      # zen-browser
      - https://copr.fedorainfracloud.org/coprs/sneexy/zen-browser/repo/fedora-%OS_VERSION%/sneexy-zen-browser-fedora-%OS_VERSION%.repo
    optfix:
      - zen # note that the optfix directory is not the same as the package name of zen-browser
    install:
      - zen-browser

and

  - type: rpm-ostree
    optfix:
      - windscribe

  - type: script
    snippets:
      # auto-fetch & install latest version of windscribe
      - 'rpm-ostree install https://github.com/Windscribe/Desktop-App/releases/latest/download/windscribe_$(curl https://api.github.com/repos/Windscribe/Desktop-App/releases/latest | grep tag_name | cut -d : -f2 | tr -d "v\", ")_x86_64_fedora.rpm'

Both Zen and Windscribe are working on my end. Again, though, Windscribe only started appearing on my app drawer & working properly after a few reboots and/or updates. Zen worked immediately. YMMV, but I hope this helped!

Aaaaand that did it!
Pheeew… talk about jumping through hoops… :joy:
BTW like you said, the app didn’t show up on the first boot, and the only mention of Mullvad in the boot logs was Created symlink for Mullvad VPN
I then rebooted again and that’s where I exhaled with relief, it was finally there. Though I had to kick off the Mullvad daemon manually the first time, but I think that makes sense?

Last question - is there anything else I need to keep in mind when adding additional packages via BlueBuild besides the optfix? I want this stay robust and not explode during a random image build… I’ll make sure to read the manual too, but maybe there’s more gotchas to this…

Thanks for the help!

Happy to hear that!

I haven’t came across many packages that require optfix, my image only has Windscribe and Zen requiring it LOL. My images have been building smoothly - in the past it would fail every so often when using non-standard kernels & additional akmods, but now that I’ve settled on Blu & CachyOS kernels with no additional akmods builds don’t really fail anymore. Occasionally builds may fail though due to upstream package states and whatnot, so when/if it happens (e.g. builds fine yesterday but fails today) you don’t really have to worry.

Bluebuild is also being continuously developed and improved, so you’d probably want to check out the changelogs whenever you get an automated version upgrade PR and/or their information channels!

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.