4

I have a local postgresql on each machine. I want to run maintenance scripts on shutdown, so users can click "Turn off" and go away while the machine does the maintenance and then actually shutdown when done.

The script must run before postgresql.service stops.

I also want to display a shutdown message informing the user that where will be a maintenance.

I have tried to create a service:

[Unit]
Description=PostgreSQL Maintenance
Requires=postgresql.service
Before=shutdown.target

[Service]
User=postgres
WorkingDirectory=/opt/postgres
ExecStart=/bin/true
ExecStop=/opt/postgres/run-maintenance.sh

[Install]
WantedBy=multi-user.target

and:

[Unit]
Description=PostgreSQL Maintenance
Requires=postgresql.service

[Service]
Type=oneshot
User=postgres
WorkingDirectory=/opt/postgres
ExecStart=/opt/postgres/run-maintenance.sh

[Install]
WantedBy=halt.target shutdown.target

They do not work.

The maintenance script is:

#!/usr/bin/env bash

plymouth display-message --text="Maintenance Message"
psql -d db -f /opt/postgres/maintenance.sql

maintenance.sql

reindex (verbose) database db;
vacuum (full, analyze, verbose);

I have found many similar questions, but did not find a definitive solution.

  • "The script must run before postgresql.service stops." No it doesn't. "It has to run while postgress is running" is what you should say as you could (as part of your unit) restart and stop postgress yourself. – Rinzwind Jul 31 '19 at 14:54
  • The 1st one you have in there looks good to me besides you might want to do: Before=shutdown.target reboot.target halt.target in [Unit] It might be halt that is used to shutdown Oh and maybe add RemainAfterExit=true in [Service] – Rinzwind Jul 31 '19 at 14:55
  • first one is a type=simple. I expect it to run in multi-user.target as all dependencies are there. shutdown.target is reached once this service is active. With a failed state systemd shoudl remove it completely after your script finished in startup. – user228505 Jul 31 '19 at 15:54
  • The second script looks much closer to what i would expect. What happens with After=postgresql.service? I expect systemd to then reverse the startup order on shutdown. As your unit is started in the shutdown target you want it to run after Postgres, so while this is still active. Then systemd can think about stopping units and will wait with termination of postgres until your maintenance unit is finished. Double-check timeout settings as well as otherwise systemd is terminating your service in case it runs too long. – user228505 Jul 31 '19 at 15:57
  • 1
    For first service: To keep it active you can add remainAfterExit=true. And adding After=postgresql.service for ordering dependency. Then it could also be in the correct shutdown order. – user228505 Jul 31 '19 at 16:02

3 Answers3

1

Here's an example of a simple service that performs a task before shutdown. Please note that the default dependencies are disabled by the DefaultDependencies=no option (I guess omission of this configuration is the main reason why your oneshot unit file is not working). As pointed out by other comments, it's probably a good idea to have the RemainAfterExit=yes option too. I also agree with the concerns about timeout made in the comments .

[Unit]
Description=SleepBeforeShutdown Service
DefaultDependencies=no
Before=halt.target shutdown.target reboot.target

[Service]
Type=oneshot
ExecStart=/bin/sleep 30
RemainAfterExit=yes

[Install]
WantedBy=halt.target shutdown.target reboot.target

After editing your service unit file run systemctl enable yourservice.service and reboot. After that, any time you run shutdown, halt or reboot, the type-oneshot service will do its thing first before the system proceeds with actual shutdown/reboot.

Edit
I've now found a previous post where the same solution was offered. I do not have the credits to post comments with a link to the question above, so I'll leave my answer as is (even though, strictly speaking, it's a duplicate) (Moderators, please remove this post if it violates the rules)

Edit 2
And yet another old post with the same answer.

yesno
  • 201
1

This worked for me:

[Unit]
Description=PostgreSql Maintenance.
After=postgresql.service

[Service]
Type=oneshot
User=postgres
WorkingDirectory=/opt/postgres
ExecStart=/opt/postgres/run-maintenance.sh
TimeoutSec=3600

[Install]
WantedBy=shutdown.target halt.target
0

Your first attempt is close to correct. My recommendation would be to use a unit with an ExecStop= for commands to run while the system is shutting down, as that's the most reliable way to do so and it's easy to get the ordering right.

One important point is to use Type=oneshot and RemainAfterExit=yes, that ensures your unit will be "started" and stay in that state until the system goes down. Once it starts to go down, it will consider your unit as one of the units to stop and will execute the ExecStop= command on it.

Regarding dependencies, you set them regarding starting up the unit, so you state everything that needs to be up when your unit gets started. When the system goes down, dependencies are stopped in the inverse order, so if you want PostgreSQL to be up while your unit stops, you need an After= dependency on it.

You don't need any dependencies on shutdown, etc. since this is just like a normal unit (starts on boot, or starts manually while the system is running, stops on shutdown as all other units are stopped as well.)

Putting it all together:

[Unit]
Description=PostgreSQL Maintenance
After=postgresql.service

[Service]
Type=oneshot
RemainAfterExit=yes
User=postgres
WorkingDirectory=/opt/postgres
ExecStart=/bin/true
ExecStop=/opt/postgres/run-maintenance.sh
TimeoutSec=3600

[Install]
WantedBy=multi-user.target
filbranden
  • 2,631