This seems like something that's been asked before but I can't find it anywhere.

I have a systemd service that runs on a timer, updating my packages. I want to get emailed using ssmtp every time the service throws an error.

If I run this in the terminal as root, it works.

 # /root/bin/update 2> >(printf "Subject: ERROR from update.service\n\n$(cat -)" | sendmail hostmaster@domain.tld)
Updating package lists... Done!
Updating packages... Done!
Removing old packages... Done!
Cleaning up... Done!
Updating dotfiles... FAILED!
Updating bpytop... Done!
Updating global npm packages... Done!

That part that failed sent STDERR which was caught by the pseudo file >(). The printf "...$(cat -)" appends a subject to the beginning of the stream. I get an email with the stderr as body. Pretty slick, huh?

Screenshot of the email update.service sent

This is what I want the service to do. Print the stdout to journald as default, but redirect stderr to a command of my choice. I tried this first:

ExecStart=/root/bin/update 2> >(printf "Subject: ERROR from update.service\n\n$(cat -)" | sendmail hostmaster@domain.tld)

But the errors are still sent to journald and not ssmtp. I think systemd intercepts stderr and sends it wherever is specified by StandardError=. The StandardError= option allows the stderr to be sent to a file descriptor or file... so I tried stuff like this:

StandardError=file:>(printf "Subject: ERROR from update.service\n\n$(cat -)" | sendmail hostmaster@domain.tld)


StandardError=fd:printf "Subject: ERROR from update.service\n\n$(cat -)" | sendmail hostmaster@domain.tld

Didn't work. It might be possible to send it the StandardError to a named pipe which points to sendmail. I fooled around with trying to get this to work and finally gave up and came here.

EDIT: I also thought about writing a bash function in the update script that sends email, then redirecting stderr from each command in the script to that function. I'm looking for a solution at the systemd level though, instead of hardcoding the STDERRs' destination in the script itself.


I've come up with one possible solution: writing stderr to a file and then mailing it using ExecStartPost=. I also learned that systemd service files don't play nicely with redirection unless you wrap commands in /bin/bash -c '':

ExecStartPre=/bin/rm -f /root/update.err
ExecStart=/bin/bash -c '/root/bin/update 2>/root/update.err'
ExecStartPost=/bin/bash -c 'test -f /root/update.err && printf "Subject: ERROR from update.service\n\n$(cat /root/update.err)\n" | sendmail hostmaster@domain.tld'

Source: How to pipe Systemd service standard output from ExecStart to shell, ex. bash?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.