Systemd does not see external drive partition when running ExecStart command

I am trying to run calibre-server at startup. Here is an example of the service content

[Unit]
Description=calibre Content server
After=network.target

[Service]
Type=simple
User=root
Group=root
ExecStart=/opt/calibre/calibre-server "/path-to-mounted-external-hdd-partition/Calibre Library"

[Install]
WantedBy=multi-user.target

When I check the status of the service: sudo systemctl status calibre-server.service

I receive following message: There is no calibre library at: /path-to-mounted-external-hdd-partition/Calibre Library

However when I type: sudo systemctl start calibre-server.service it starts flawlessly.

My assumption was that partition and folder structure is not mounted/available at the time the service initially is ran. So I tried to replace ExecStart with ExecStart=/test.sh and content of test.sh

#!/bin/bash

ls -la /path-to-mounted-external-hdd-partition/ 2> /out.txt >> /out.txt

the result stored in out.txt shows (simplified for brevity):

.
..

it seems the content of external hdd is empty at the time service is ran.

Why is that? How can I solve it?

P.S. I do not know if that is important but mounting is done via ubuntu/disks app and not by /etc/fstab

P.P.S.
systemctl list-units | grep '.mount'

-.mount                   loaded active mounted   Root Mount
...
media-XXX-Storage.mount   loaded active mounted   /media/XXX/Storage

where /media/XXX/Storage is exactly what I described as /path-to-mounted-external-hdd-partition

Asked By: kkris1983

||

Your calibre-server is starting before your mount is ready. Let’s add the proper ordering dependency (EDIT please either skip to my second solution, or use @noisefloor’s answer as a cleaner version of this first solution of mine):

[Unit]
Description=calibre Content server
After=network.target
# Added: wait for mount.
After=media-XXX-Storage.mount
# Added: Pull in the mount job if for some reason it's not
# started directly or indirectly by multi-user.target . Thanks @muru
Requires=media-XXX-Storage.mount

[Service]
Type=simple
User=root
Group=root
ExecStart=/opt/calibre/calibre-server "/path-to-mounted-external-hdd-partition/Calibre Library"

[Install]
WantedBy=multi-user.target

If that doesn’t work, you can add a loop on start to test for a file inside:

[Unit]
Description=calibre Content server
After=network.target

[Service]
Type=simple
User=root
Group=root
ExecStart=/opt/calibre/calibre-server "/path-to-mounted-external-hdd-partition/Calibre Library"
# Added: wait for mount, workaround
ExecStartPre=/bin/bash -c "while ! grep -q '/media/XXX/Storage' /proc/mounts; do sleep 1; done"
# Don't let the above command time out
TimeoutStartSec=infinity

[Install]
WantedBy=multi-user.target
Answered By: Daniel T

If you certain path should be mounted prior to running your service unit, you can also add the RequiresMountsFor= directive in the [Unit] section. This will check whether the path is available. So in the given case the complete directive would be
RequiresMountsFor="/path-to-mounted-external-hdd-partition.

For more information on this directive check the systemd unit documentation.

Answered By: noisefloor

This answer depends on an assumption I made based on your edits and comments.

The mount you are using is an external mount, and you’re depending on the GNOME’s auto-mounting functionality. You don’t have it configured in fstab nor as a systemd mount. This assumption is mainly because you mention ls on the directory shows an empty one, and that should not be the case. And the fact that the directory is under /media/XXX.

Auto-mounted drives are not available during boot. You can confirm that by running the command from your edit systemctl list-units | grep '.mount' in your test instead of the ls command. The result will not show the mount point. So adding them as dependencies will not have any effect, as those mount points are auto-generated after you log into your user session.

You can try experimenting with a user service instead of a system service. In this case, you may find more success with Daniel T’s loop approach.

Create the service under ~/.config/systemd/user/your-service-name.service and enable it while passing --user to the systemctl command.

systemctl --user enable your-service-name

Otherwise, the safer approach I recommend is adding an fstab entry (or a systemd .mount entry) to mount your drive. Then, as the other answers suggest, you can use it for Requires or RequiresMountsFor.

Ps. This is not directly related to your question, but if ls -la /path-to-mounted-external-hdd-partition/ is showing you an empty directory during boot and you expect it to show you the content of your disk, it’s because the directory exists. When the drive is mounted, the path is replaced from a local path to the mounted drive.

You can test this by completely unplugging the drive and trying the ls command and see if it results in a No such file or directory error.

Answered By: Dan

After setting static mount in /etc/fstab it works with settings as you advised.

[Unit]
Description=calibre Content server
After=network.target
RequiresMountsFor=/media/XXX/Storage

[Service]
Type=simple
User=user
Group=user
ExecStart=/opt/calibre/calibre-server "/home/XXX/Storage/Calibre Library"

[Install]
WantedBy=multi-user.target

Thank you all for help.

Answered By: kkris1983
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.