Converting a cron job to systemd timer

I have a cron job which refreshes keys on a monthly basis like follows

0 06 1 * * gpg --refresh-keys

I tried to move this to systemd by creating

~/.config/systemd/user/gpg_refresh_keys.service which has

[Unit]
Description=Refresh gpg keys
OnFailure=send_email_to_manfred@%n.service
# I want to get a mail in any case
OnSuccess=send_email_to_manfred@%n.service

[Service]
Type=oneshot
ExecStart=/usr/bin/gpg --refresh-keys --no-tty

~/.config/systemd/user/gpg_refresh_keys.timer

[Unit]
Description=Run gpg_refresh_keys on a montly basis

[Timer]
OnCalendar=*-*-01 06:00:00
Persistent=true

[Install]
WantedBy=timers.target

Then I ran

systemctl --user enable --now /home/manfred/.config/systemd/user/gpg_refresh_keys.timer

Just to see that the service works fine I ran systemctl --user start gpg_refresh_keys.service and get

Job for gpg_refresh_keys.service failed because the control process exited with error code.
See "systemctl --user status gpg_refresh_keys.service" and "journalctl --user -xeu gpg_refresh_keys.service" for details.

Running systemctl --user --full --lines 100 status gpg_refresh_keys.service gives

Sep 02 20:45:15 hogwart systemd[1467]: gpg_refresh_keys.service: Changed dead -> start
Sep 02 20:45:15 hogwart systemd[1467]: Starting Refresh gpg keys...
Sep 02 20:45:15 hogwart (gpg)[1563100]: Skipping PR_SET_MM, as we don't have privileges.
Sep 02 20:45:15 hogwart (gpg)[1563100]: Found cgroup2 on /sys/fs/cgroup/, full unified hierarchy
Sep 02 20:45:15 hogwart (gpg)[1563100]: Found cgroup2 on /sys/fs/cgroup/, full unified hierarchy
Sep 02 20:45:15 hogwart (gpg)[1563100]: gpg_refresh_keys.service: Executing: /usr/bin/gpg --refresh-keys --no-tty
Sep 02 20:45:15 hogwart gpg[1563100]: gpg: refreshing 23 keys from hkps://keys.openpgp.org
...
Sep 02 20:45:23 hogwart gpg[1563100]: gpg: Total number processed: 23
Sep 02 20:45:23 hogwart gpg[1563100]: gpg:              unchanged: 14
Sep 02 20:45:23 hogwart systemd[1467]: gpg_refresh_keys.service: Child 1563100 belongs to gpg_refresh_keys.service.
Sep 02 20:45:23 hogwart systemd[1467]: gpg_refresh_keys.service: Main process exited, code=exited, status=2/INVALIDARGUMENT
Sep 02 20:45:23 hogwart systemd[1467]: gpg_refresh_keys.service: Failed with result 'exit-code'.
Sep 02 20:45:23 hogwart systemd[1467]: gpg_refresh_keys.service: Service will not restart (restart setting)
Sep 02 20:45:23 hogwart systemd[1467]: gpg_refresh_keys.service: Changed start -> failed
Sep 02 20:45:23 hogwart systemd[1467]: gpg_refresh_keys.service: Job 2666 gpg_refresh_keys.service/start finished, result=failed
Sep 02 20:45:23 hogwart systemd[1467]: Failed to start Refresh gpg keys.
Sep 02 20:45:23 hogwart systemd[1467]: gpg_refresh_keys.service: Unit entered failed state.
Sep 02 20:45:23 hogwart systemd[1467]: gpg_refresh_keys.service: Triggering OnFailure= dependencies.
Sep 02 20:45:23 hogwart systemd[1467]: gpg_refresh_keys.service: Triggering OnFailure= dependencies done (1 job).
Sep 02 20:45:23 hogwart systemd[1467]: gpg_refresh_keys.service: Consumed 42ms CPU time, 1.7M memory peak.
Sep 02 20:45:23 hogwart systemd[1467]: gpg_refresh_keys.service: Releasing resources...

This is all strange as the output of the gpg command is totally fine. I have no idea what the error is.

Seems that you need

[Install]
WantedBy=gpg_refresh_keys.timer

in the service file.
Also maybe you could use simpler

OnCalendar=monthly

No, this doesn’t help as your suggestion relates to the timer/service connection.

The problem is that the service doesn’t work correctly although the gpg output is the same as when I run the gpg --refresh-keys in the terminal where gpg returns 0.

Perhaps I should add that the service works fine if I replace the ExecStart line with, for example

ExecStart=/usr/bin/ls

I have found that some commands require wrapping.

Try this and see if it makes a difference:

ExecStart=/usr/bin/bash -c '/usr/bin/gpg --refresh-keys --no-tty`
2 Likes

The [Install] section of services triggered by timers is not necessary, as long as the base name of the service matches the timer. For example, I have these for a program I run every hour:

$ systemctl cat btrfsbk.{service,timer}
# /etc/systemd/system/btrfsbk.service
[Unit]
Description=Create mirror of current state of all BTRFS snapshots

[Service]
Type=simple
ExecStart=/usr/local/sbin/btrfsbk

# /etc/systemd/system/btrfsbk.timer
[Unit]
Description=Create mirror of current state of all BTRFS snapshots

[Timer]
Unit=btrfsbk.service
OnBootSec=15min
OnUnitActiveSec=60min
AccuracySec=1us
Persistent=True

[Install]
WantedBy=timers.target

I found that adding the [Install] section would actually trigger the service on boot, which I did not want.

Thanks for this. But it doesn’t make a difference.

It it possible to cheat by creating a gpg.sh script

#! /usr/bin/bash

/usr/bin/gpg --refresh-keys --no-tty
exit 0

and call that.

But cheating isn’t a solution here.

As far as I can tell the command will try to update /etc/pacman.d/gnupg/pubring.gpg

You are running the service as a specific user. Does this user have write access to /etc/pacman.d/gnupg/pubring.gpg?

1 Like

You are wrong. gpg has per se nothing to do with /etc/pacman.d/gnupg/pubring.gpg.

Here I run gpg under my user and when I run the command gpg --refresh-keys (with or without --no-tty) manually in a shell it works fine.

When you run it from systemd, it’s likely running likely with root, and $HOME and other files are different. Is your service set up as a systemd user service? https://wiki.archlinux.org/title/Systemd/User

Is this line

~/.config/systemd/user/gpg_refresh_keys.timer

really needed in your service?

I may be mistaken but it seems that it is taken as an argument to the command line in ExecStart= and is the cause of the failure.

I guess you can just have the timer in the appropriate folder and just activate it.

From my original post you can see that it is set up as a systemd user service. So here root is not involved. The systemctl commands I run all have --user as additional parameter.

Missed that. I don’t see the User= line, and I’m not sure that’ll help.
You can specify the directives User= and Group= in the [Service] section of the unit file. Or used to be able to.

In [Unit], you may want a line like:
after=network.target

User= and Group= are not required and maybe even wrong in systemd user service.

After=network.target has nothing to do with the problem I am facing here.

I guess this was some copy error here. The line does not exist in the the service file.

1 Like

The timer itself is working fine. The problem is that the service it starts fails although the output of the gpg command is totally the same as when I run it manually.

Now I get also a rc=2 when running manually. Either something has changed or I wasn’t looking carefully enough before. Sigh.

Conclusion: the systemd service is working ok.

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