1. 15
  1.  

  2. 7

    What’s the advantage of this vs. just expressing dependencies directly? That is, if you have some frontend service Foobar that needs Postgres, why not just have foobar.service do After=postgresql.service etc. directly, instead of going through the hassle of putting that directive in infra.target and then having frontend.target depend on that? It just seems like a lot of abstraction for very little gain. In fact, you’ll probably take a boot performance hit because systemd can no longer fully parallelize boot. Specifically, if some frontend service Baz does not require Postgres, under this setup systemd is not allowed to start Baz at the same time as Postgres, even though it could.

    What I have found targets to be very helpful for is grouping different services that make up one “app” together. For example: foobar-web@.service and foobar-worker@.service, where there might be an arbitrary number of web frontend processes and background job worker processes, respectively. It’s very helpful to bind these together into e.g. foobar.target so you can query and change the state of the entire Foobar app with a single systemctl command.

    1. 2

      Additionally what I would personally do is to make most of these services start on demand instead. This would allow me to make services start when really needed at cost of some initial connections.

      1. 1

        What’s the advantage of this vs. just expressing dependencies directly?

        Now it’s quite simple to add another dependency to all your apps, say redis. As I run more and more apps knowing I get the same services to boot before them lets me think of my server more of a platform. E.g. starting nginx after all container names are registered makes it super easy to have route traffic to the correct app without having to hand out IP manually.

        In fact, you’ll probably take a boot performance hit because systemd can no longer fully parallelize boot.

        Yep, that’s true there’s a little overhead event with my simple use case I’m seeing 39s instead of 37s which in the end is something.

        What I have found targets to be very helpful for is grouping different services that make up one “app” together.

        That’s also very nice to have I was using namespaces for this but it’s much simpler.

        1. 2

          Now it’s quite simple to add another dependency to all your apps, say redis.

          Does all that apps require Redis? I highly doubt it. And finer grade control would allow to start such services before Redis, which could result in better startup performance.

          E.g. starting nginx after all container names are registered makes it super easy to have route traffic to the correct app without having to hand out IP manually.

          Or you can dynamically register services and just reload Nginx configuration after each new service is registered. This would allow to handle services faster with proper fallback to error message. You could use for example Consul to do so, with consul-template. No manual handling of IPs, distributed if needed, and more robust.

          1. 3

            I think we are getting further and further from each others use case, I run one VPS for my wonky web apps not software in production. That said it’s just very nice to add one new app and be sure it will get the same services as all the others, I can always register it as WantedBy=multi-user.target or specify its Wants if needed.

            Or you can dynamically register services and just reload Nginx configuration after each new service is registered.

            that’s all very nice but I will always take my configuration as text files thank you, I looked into using the nginx HTTP API and it’s not worth it for my usage. I have some M4 templates around and a shell script for adding and removing them and so far so good.

      2. 2

        I guess often the issue is that even if, for example, Postgres is up, that doesn’t mean it’s available for other services to use. So even if the startup order is enforced, that solves only part of the problem.

        If you know how to solve that with systemd, I’m interested!

        1. 6

          Well, PostgreSQL supports Type=notify if compiled with appropriate flags. You can use that to launch it and wait until it is ready to accept connections.

          1. 1

            Oh I didn’t know that! Thank you very much !!

          2. 2

            As you say there’s many things that might prevent a client from connecting, one that I found was the network name resolution. As I run postgres inside a container managed by systemd-nspawn I have to register the name postgres to be resolved by nss. I’m just lucky that nss_mymachines does this for me and is bound to networkd so I can just put another target there and wait for both my machined target along networkd. This way I know that everything has been started and the network names are registered.

            Obviously this does not mean you can just gloss over client side retries and backoffs, that’s just asking for troubles.

            If you are not that lucky and you are writing your own daemon I guess the best option is systemd.notify where you can signal that your daemon is now ready for doing real work and the unit startup sequence is only completed when you send the READY=1.