Autorun asynchronously program on background

Hello! Sometimes I meet tasks when I need a certain program to always run in the background, turn on at system startup and restart on errors.

For example, I need to run a docker image like this (not necessarily docker, preferably a universal solution).

I’m not very versed in Linux, so it only occurred to me to make a systemd task:

# /etc/systemd/system/mytask.service
[Unit]
Description=Some Container
After=docker.service
Requires=docker.service

[Service]
TimeoutStartSec=0
Restart=on-failure
RestartSec=5s
ExecStartPre=-/usr/bin/docker stop image
ExecStartPre=-/usr/bin/docker rm image
ExecStartPre=/usr/bin/docker pull image
ExecStart=/usr/bin/docker run --rm --name taskName image

[Install]
WantedBy=multi-user.target

This solution does work, but it increases the system boot time. And, it seems to me, the system, because of this, shutting down the system began to take a lot of time.

What are the ways to run similar tasks, but somehow more… right? I probably need to run this asynchronously as the system doesn’t need this process right now, it just needs to always run in the background.

Hi @BlackBaroness,

I have no idea if this will work, but look at 1nohup1:

AFAIK it’s a standard part of a Linux installation and you just have to put nohup at the start f your command. So in your case, I think it’d be:

# /etc/systemd/system/mytask.service
[Unit]
Description=Some Container
After=docker.service
Requires=docker.service

[Service]
TimeoutStartSec=0
Restart=on-failure
RestartSec=5s
ExecStartPre=nohup /usr/bin/docker stop image
ExecStartPre=nohup /usr/bin/docker rm image
ExecStartPre=nohup /usr/bin/docker pull image
ExecStart=nohup /usr/bin/docker run --rm --name taskName image

[Install]
WantedBy=multi-user.target

Buut I might be totally wrong.

:man_shrugging:

1 Like

Well, systemd starts the services in paralell, but in steps as I know. So there are targets and each target has dependencies. When all dependencies has been started (this goes parallel), it goes to the next target.

systemctl list-dependencies multi-user.target  

Well I guess there is no real solution here. More services means always a little longer boot time.

2 Likes

If I understand correctly, this will deprive me of the opportunity to check the operation of the program through systemctl status so this doesn’t look like a good solution.

Hmm… But systemd-analyze blame tells me that my docker services take a long time to start. Not docker itself, but 2 images. And the duration of the system startup felt increased after they were added. It looks like they are not running in parallel, but in series.

I decided to take another look at the analysis and noticed that systemd continues to initialize after the system boots.

> systemd-analyze blame                                                                                                                                                                                                                                                          
21.343s NetworkManager-wait-online.service
20.502s DOCKER_SERVICE_1.service
19.601s pkgfile-update.service
13.164s DOCKER_SERVICE_2.service
 9.488s packagekit.service
 9.397s rtkit-daemon.service
  696ms docker.service
  374ms dev-nvme0n1p2.device
> systemd-analyze critical-chain
graphical.target @43.549s
└─multi-user.target @43.548s
  └─DOCKER_SERVICE_1.service @23.045s +20.502s
    └─docker.service @22.305s +696ms
      └─network-online.target @22.303s
        └─NetworkManager-wait-online.service @959ms +21.343s
          └─NetworkManager.service @849ms +44ms
            └─dbus.service @814ms +32ms

It seems to me that I understand something wrong. Are these services bottleneck or is it something else? Maybe I don’t need to do anything with them and they don’t affect the loading speed.

Why remove and re-create images ??? pull is always long - normally you don’t regenerate your image, you only load the container…

No it’s always parallel !

systemd-analyze plot > myinit.svg && firefox myinit.svg &

systemd runs everything async - as in creating new threads - so in theory you can create services which interferes with eachother causing unwanted delays

remove this line - only pull the docker image if it has actually been changed.

you may create a service and a timer to do a periodically pull and reload

Am I deleting the image? I’m only deleting the container in case one is already running. Maybe I misunderstood something in docker, then my mistake

I use pull to upgrade the image if there is an update

Another option is a timer unit that only runs after your computer has started?

They usually go in pairs.

  1. The .service defines what needs to be done.
  2. The .timer executes the service at the specified intervals.
1 Like

See here for more info, if required:

https://wiki.archlinux.org/title/systemd/Timers

Specifically:

Monotonic timer

A timer which will start 15 minutes after boot and again every week while the system is running.

/etc/systemd/system/foo.timer

[Unit]
Description=Run foo weekly and on boot

[Timer]
OnBootSec=15min
OnUnitActiveSec=1w

[Install]
WantedBy=timers.target

Something like that ought to work.

This stops the container

This removes the container - delete if you like

This pulls a new image from docker hub

This one starte the image and instructs the service to remove the image on shutdown.

You will need to rethink that strategy

You can view “image” as an .iso file and container as linux installed :wink:
A Docker Container is a (running) instance of a Docker Image


At all your boot :

  • “docker stop image” : we can stop only container ! and at boot … container exists ???
  • “docker rm image” : ? remove iso file, ok, but now i can’t create instance (container)
  • “docker pull image” : download datas and compile them for create .iso file
  • “docker run” : install iso file on disk and boot on this new instance

What is the update frequency ? 8 days … 3 months ? Use another service and a timer

I did this for systemctl restart. If you restart the service, the container will already exist and this will cause an error

Start existing containers with docker start.
docker run creates a new container and it will error when you try to create over an existing container name.

I understand but that just dont work

[Service]
TimeoutStartSec=0
Restart=on-failure
RestartSec=5s
ExecStart=/usr/bin/docker run --name NAME IMAGE
ExecStop=/usr/bin/docker stop NAME
Loaded: loaded (/etc/systemd/system/honeygain.service; enabled; preset: disabled)
Active: activating (auto-restart) (Result: exit-code) since Tue 2022-08-16 01:45:17 +03; 3s ago
Process: 9579 ExecStart=/usr/bin/docker run --name NAME IMAGE (code=exited, status=125)
Main PID: 9579 (code=exited, status=125)
CPU: 56ms

Thats wrong, --rm will make container removed on exit. I don`t touch image at all


Looks like problem is solved now. Final result:

[Unit]
Description=SERVICE_NAME
After=docker.service
Requires=docker.service

[Service]
TimeoutStartSec=0
Restart=always
RestartSec=5s
# Stop and remove exiting container to avoid failure
ExecStartPre=-/usr/bin/docker exec %n stop
ExecStartPre=-/usr/bin/docker rm %n
# Launch container
ExecStart=/usr/bin/docker run --rm --name %n IMAGE
# Stop container
ExecStop=-/usr/bin/docker stop %n

[Install]
WantedBy=multi-user.target

The launch of these services accelerated from 10-15s to 150-200ms

It became faster because you are not pulling the image anymore, but I meant that there is likely no need to recreate the container on every start.
Create it manually once with run, then you can use it so:

[Unit]
Description=SERVICE_NAME
After=docker.service
Requires=docker.service

[Service]
TimeoutStartSec=0
Restart=always
RestartSec=5s
ExecStart=/usr/bin/docker start %n
ExecStop=/usr/bin/docker stop %n

[Install]
WantedBy=multi-user.target