Hacker News new | ask | show | jobs
Show HN: Servicer, pm2 alternative built on Rust and systemd (servicer.dev)
147 points by shardulaeer 1045 days ago
Servicer is a CLI to create and manage services on systemd. I have used pm2 in production and find it easy to use. However a lot of its functionality is specific to node.js, and I would prefer not to run my rust server as a fork of a node process. Systemd on the other hand has most of the things I need, but I found it cumbersome to use. There are a bunch of different commands and configurations- the .service file, systemctl to view status, journald to view logs which make systemd more complex to setup. I had to google for the a template and commands every time.

Servicer abstracts this setup behind an easy to use CLI, for instance you can use `ser create index.js --interpreter node --enable --start` to create a `.service` file, enable it on boot and start it. Servicer will also help if you wish to write your own custom `.service` files. Run `ser edit foo --editor vi` to create a service file in Vim. Servicer will provide a starting template so you don't need to google it. There are additional utilities like `ser which index.js` to view the path of the service and unit file.

``` Paths for index.js.ser.service: +--------------+-----------------------------------------------------------+ | name | path | +--------------+-----------------------------------------------------------+ | Service file | /etc/systemd/system/index.js.ser.service | +--------------+-----------------------------------------------------------+ | Unit file | /org/freedesktop/systemd1/unit/index_2ejs_2eser_2eservice | +--------------+-----------------------------------------------------------+ ```

Servicer is daemonless and does not run in the background. It simply sets up systemd and gets out of the way. There are no forked services, everything is natively set up on systemd. You don't need to worry about resource consumption or servicer going down which will cause your app to stop.

Do give it a spin and review the codebase. The code is open source and MIT licensed- https://github.com/servicer-labs/servicer

10 comments

You might like systemd's run command, it's a hidden little gem that basically does what your tool does--it turns some CLI params into a transient service file: https://www.freedesktop.org/software/systemd/man/systemd-run...

I dunno if this was a more recent addition to systemd, but it's pretty slick in my limited use. I use it for exactly the scenarios you mention, like quickly spinning up some local user services during development. It can do stuff like setup a listening socket, timer (cron) or file watcher service all from the CLI too.

Great tip. I would also couple it with the —-user flag so the service runs as your user. Stuff like this shouldn’t have root permissions anyway.
That's a good thought. `sudo` is needed for writing to `/usr/`. The service being set up does not have root access. The value of `User=` is $SUDO_USER (hp- my regular non privileged user who initiatiated sudo) instead of $USER (root).

https://github.com/servicer-labs/servicer/blob/762801e3c07b1...

Typically the `User=` directive is used to specify what the user the service runs in.

There is still a benefit to requiring root to create the service file though: If the service is compromised when it's running as `User=`, it can't modify the systemd service file itself, which is owned by root.

That's interesting! I wasn't aware of this command when I started working on servicer. Anyway, there are a couple of utilities like `ser status` and `ser which` which you might find useful.
TIL, Pretty cool!

But I suppose there isn't away to run these transient services as boot because they are definition gone after they have stopped?

It's actually very easy!

You can abuse the fact that systemctl has the edit subcommand. It meant to modify the existing service file, but nothing means you can't just save it out elsewhere and then modify it.

Additionally, if it's a long running service it should be located where other systemd unit files are located (`$HOME/.config/systemd/user/` for user units, `/etc/systemd/system/` for service units) and just copy them while the unit is still running.

  systemd-run --user -u test -t bash
  cp $HOME/systemd/user/test.service /new/path/for/test.service
Of course this requires it to live long enough.
Theoretically one-shot service that launches a shell script with systemd run on boot could do the trick. OTOH it's probably easier to just write a tiny service at that point.

Generally results of -run services can be captured through journalctl, so it's possible to run systemctl show on them and get the config.

As someone who knows systemd very well, my team of Mac-based devs did appreciate when I made something like this. I used a different framework, a precursor to Tome:

   https://github.com/toumorokoshi/tome
Using this, I was able to create a very focused tool with few commands and simple docs, which supported things like:

  - my-cli help
  - my-cli start
  - my-cli stop
  - my-cli restart
  - my-cli status
Sometimes the commands were thin wrappers around systemd, sometimes they were a bit more complex because we really had a collection of services, not a single one.

So while my first reaction was "why not use systemd?", my second reaction is "Oh yeah, I built something like that and it was helpful".

Systemd is great, but it does have a massive amount of a commands and documentation and be overkill and overwhelming.

Wow, never seen tome before but I built almost the exact same thing at my current company with the only difference being all the sub-commands run in a nix environment. Being able to share bash scripts in our monorepo by just creating a file at ./scripts/bash/foo.bash and calling it with 'my-cli foo' has been awesome. You can also set up runners so ./scripts/js and ./scripts/python works as you would think. Having everything inside of a nix env means all the tools are there and work across everyone's machines.

I don't have a solution for managing services though and that has been the one pain point after moving away from containerized dev envs. The cross platform network and service management you get with docker is pretty ideal. devenv.sh is on to something with their 'services' feature but it doesn't quiet fit the bill for us yet.

Check out devshell[1], especially paired with flake-parts and direnv. When a user cds into a flake directory, drenv's "use flake" makes the terminal show all devshell commands in a nice menu

[1]: https://flake.parts/options/devshell.html

> Systemd on the other hand has most of the things I need, but I found it cumbersome to use

Could you please expand on this? From what I understand, to use systemd to deploy a microservice is basically one file:

    # /etc/systemd/system/myservice.service
    
    [Unit]
    Description=My Microservice
    After=network.target
    
    [Service]
    ExecStart=/path/to/myservice
    Restart=always
    User=myuser
    Group=mygroup
    Environment=VARIABLE=value
    WorkingDirectory=/path/to/service/directory
    StandardOutput=syslog
    StandardError=syslog
    SyslogIdentifier=myservice
    
    [Install]
    WantedBy=multi-user.target
Then, to "use" it:

    # reload systemd
    sudo systemctl daemon-reload
    # start service
    sudo systemctl start myservice
    # enable service at boot
    sudo systemctl enable myservice
    # check status
    sudo systemctl status myservice
    # check logs
    journalctl -u myservice
What more advanced stuff were you trying to do that you ran into that made systemd seem cumbersome?
I built the tool simply to improve developer experience. Run `systemctl list-units` and the console is flooded with a wall of services, most of them are not mine. Run `ser status` and I only see the ones that I created. The activity state, CPU and memory usage is displayed so I don't need to run a separate command.

    hp@hp:~$ ser status
    +-----+-------------+----------+----------------+-------+--------+
    | pid | name        | active   | enable on boot | cpu % | memory |
    +-----+-------------+----------+----------------+-------+--------+
    | 0   | hello-world | inactive | false          | 0     | 0      |
    +-----+-------------+----------+----------------+-------+--------+
    | 0   | index.js    | inactive | false          | 0     | 0      |
    +-----+-------------+----------+----------------+-------+--------+
Another UX improvement is the template generation. I found myself googling it everytime. I f there is something more custom (etc sockets) I can use the `ser edit`, this will open nano with a pre-populated template that I can edit.

Finally why limit oneself to systemd? pm2 runs on Mac and windows, I aim to do the same. Same set of commands to create services everywhere, with details abstracted away (easier said than done, need to look into their APIs).

> The activity state, CPU and memory usage is displayed so I don't need to run a separate command.

https://github.com/crazy-canux/awesome-monitoring

Check out Netadata + Nagios

https://news.ycombinator.com/item?id=36944388

Check out this as well

    systemctl list-units | grep my-service
also see systemd-cgtop
As long as remembering a new command is required this also be used:

* systemctl status 'brand-*' That is, custom services could start with the same prefix.

I run a lot of custom services, and usually I want to check on one specifically, or if I want to look at the total service health, I also want to include the services which are "not mine".

To do formatting on a message (multilines of code, equivalent to ``` in Markdown), indent everything with 4 spaces
Fixed, thanks for the heads up!
The file's pretty cumbersome, honestly, since I usually don't remember the format or the best practices for it.

Plus, sometimes there is no place to put them that isn't conflicting with the package manager, so "best practice" ends up being to make your own package containing the unit file, and ...

I'm not a big fan of "best practices" in general, but it'll turn into a whole rabbithole if you let it. At least with a piece of software like this, you just run a few commands and it remembers, and you get tons of sane defaults.

This was solved from beginning of systemd with different directories for files mananged by package managers (under /lib), and files managed by you (under /etc). This is documented in `man systemd.unit`:

https://www.freedesktop.org/software/systemd/man/systemd.uni...

The same doc also describes a "drop-in" file format, where you can override just the details you want from a system file.

FWIW, you can override unit files that are installed by a package manager: https://hsm.tunnel53.net/article/systemd/
I think the "issue" isn't the package manager overwriting your custom unit files, but rather just the litter of untracked files in places the package manager is supposed to be in charge of. Since some people want to be able to track those files, and prevent them from making a mess.

In practice I don't think it is a huge issue. I've just left unit files laying around on embedded devices without issue. Hard to find which ones are mine though, if I've lost my mental list of them.

(Also, writing one from scratch without a template is annoying, since I haven't memorized the format.)

You can even do this with 'systemctl edit <unit>' and it'll create a file in the right place for the override and show you the original contents for reference
That's 15 lines of configuration and 3 CLI commands to install a service. I have done this hundreds of times but I still need to keep a template around for unit files, I often forget to run `daemon-reload`, etc.

I like systemd but I can see a lot of value in friendlier tools like this. What I really want is a TUI tool like Lazydocker for managing systemd services.

Kudos for making it daemonless!

I think Servicer can be a good starting point for people to learn how to use Systemd services and systemd-run. Systemd is huge and can be scary to learn, so providing a set of steps to get a service running is very instructive.

But then, Servicer requires super user priviliges to modify the system, instead of running everything as the non-privileged user, via `--user` flag for systemctl or via systemd-run...

There are few commands to learn the basics when setting up a new system wide service: systemctl and journalctl. Some examples: - systemctl daemon-reload: reload all unit files - systemctl start/stop/status/restart <service>: quite intuitive, don't you think? - journalctl -u <service>: to see all logs from that service. I like to also use `-f` flag to follow new log entries. - man systemctl: the manual for systemctl - man journalctl: the manual for journalctl

Writing new service units is the "hard" part, but there are plenty of "templates" on the net, or you can browse `/usr/lib/systemd/system/` for examples.

But don't let the critics here demotivate you. You made a tool, you published it, congrats!

Thanks for the feedback! I'm certainly add a `--user` flag. One kind note though, the service being set up does not have root access. The value of `User=` is $SUDO_USER (hp- my regular non privileged user that initiatiated sudo mode) instead of $USER (root). Sudo is needed for writing `.service` files to `/usr/` and for start, stop operations with dbus.
> Kudos for making it daemonless!

It uses the systemd daemon to manage the services. It's not clear what an additional service would do.

When I picked up Deno, I couldn't use it with PM2 so I ended up learning systemd which turned out to be quite easy. I like however the added tooling and dev-ux this tool provides.

Reading your post here it seems there's more interpreters you support, however the landing page has no reference to it.

Thanks for the heads up, I shall update the landing page. The tool tries to detect the interpreter (currently just node and python), but you can provide a custom one with `--interpreter deno`.
I don't understand this. Why not just...use systemd? Is there something I'm missing here?
I think it's more about ease of use and developer experience. I agree that in the current state servicer is a proxy to systemd, anything you can do here you can do on systemd. Later down the line I will try to support process managers on Mac and Windows.

I'm happy see this question "Why not use systemd" finally being asked. Apparently the people on NPM (2 million weekly downloads for pm2) ~~disagree~~ don't care. `pm2 start` just works, not everybody is a system admin. There are a bunch of threads around asking for a pm2 for Rust or golang. This tool tries to address this segment.

You can be a linux veteran still benefit with some commands like `ser status` which will only show the services you created, not the wall of text shown by `systemctl list-units`. CPU and Ram usage is thrown in as a bonus so you don't need to run a separate command. It's a neat trick that uses no database. The services end with `.ser.service`, this way we only see the units that servicer created. Run `ser which` and you'll get the unit and service file destination.

Systemd is so damn cumbersome. It's like they never tried using servers in real life when they designed it.

No commands work as you'd expect them to, and require a lot of very specific cmd line flags to do what should be the default.

I think the architecture and design of systemd might be okay but the user experience is horrible.

It's the same with git.

Looks nice! I always found working directly with systemd to manage persistent services a bit too cumbersome, although I can see how others may not have a problem with this.
Tools like yay, an additional package manager on Arch Linux can be started without sudo and if neccessary request the passwort to sudo. This would be a nice feature for servicer, too.
I like the idea of this, will find something to run it on later.
Awesome, would really appreciate your feedback!
Have you tried pystemd?