Skip to content

Instantly share code, notes, and snippets.

@waynebloss
Last active November 29, 2022 22:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save waynebloss/63b4a8a326c4ef4e5db24a209eab5882 to your computer and use it in GitHub Desktop.
Save waynebloss/63b4a8a326c4ef4e5db24a209eab5882 to your computer and use it in GitHub Desktop.
Systemd Socket Activation for PWA

Systemd Socket Activation for PWA

How to create a PWA with a local server that starts on demand using systemd on Linux.

The steps below are very rough and would need to be automated during app installation. These notes are geared for Arch/Manjaro Linux. Your path to systemd-socket-proxyd might be different.

(Something similar can be done with macOS' launchd service. Windows has nothing for this except for some really complicated .NET-centric services that aren't enabled by default, called WPAS or Windows Process Activation Service.)

Create server, Install service units

To run this experiment:

  • Build a simple node http server that listens on port 48101.
    • It should serve a basic HTML file from the root url on localhost.
    • It does not have to serve a full PWA manifest for any of this to work, but that's just what I'm working towards.
  • Copy the *.service and .socket files from this directory into your local ~/.config/systemd/user/ directory (create that directory if necessary).
    • Edit them and fix any paths in them, such as the path to node and your node app.
    • What these files do is explained in the next section after the commands.

Register and start the socket unit

Now run the following commands. None of these should need sudo:

systemctl --user daemon-reload
systemctl --user enable myapp-proxy.socket
systemctl --user start myapp-proxy.socket

The myapp-proxy.socket unit is now listening on port 48100 or any port that is different from what your node server listens on...

  • First visit http://localhost:48101 to confirm no site is running.
  • Now visit http://localhost:48100.
    • This causes the myapp-proxy.socket unit to activate the myapp-proxy.service unit (by virtue of the shared file name).
    • The myapp-proxy.socket unit accepts the request and passes the active socket file descriptor to myapp-proxy.service.
    • The myapp-proxy.service causes the configured myapp.service dependency to be started first and then uses systemd-socket-proxyd to proxy all packets from it's given socket over to myapp.service!

Install the PWA

You can install the site at http://localhost:48100 as a PWA if you wish, from Chrome More tools -> Create shortcut... and check "Open as window".

Now when you start the PWA, the backend service is also started on the first request.

Stop the service

systemctl --user stop myapp.service
systemctl --user stop myapp-proxy.service
$ Warning: Stopping myapp-proxy.service, but it can still be activated by:
  myapp-proxy.socket

Stop the socket

systemctl --user stop myapp-proxy.socket

Remove the service

systemctl --user stop myapp-proxy.socket
systemctl --user disable myapp-proxy.socket
trash ~/.config/systemd/user/myapp*
systemctl --user daemon-reload
[Unit]
Description=MyApp Proxy
Requires=myapp.service
After=myapp.service
[Service]
ExecStart=/usr/lib/systemd/systemd-socket-proxyd 127.0.0.1:48101
[Unit]
Description=MyApp Socket
[Socket]
ListenStream=127.0.0.1:48100
[Install]
WantedBy=sockets.target
[Unit]
Description=MyApp Server
[Service]
ExecStart=/home/YOU/n/bin/node /home/YOU/lab/myapp/packages/myapp-server/dist/index.js
ExecStartPost=/bin/sleep 1
SyslogIdentifier=myapp
@waynebloss
Copy link
Author

I don't think macOS launchd has a built-in proxy like systemd-socket-proxyd, so you'd have to bring your own. However, since there's nothing at all for Windows I would probably build something in Golang that would work in both places.

Alternatively, on Windows at least, I might not need socket activation at all since I can probably capture the PWA shortcut to get it's CLI parameters after install, then replace that shortcut with my own utility that starts a hidden, shared node app server before launching the PWA frontend...

@waynebloss
Copy link
Author

Shutting down the node app server when the PWA closes should not be difficult. It could be as simple as monitoring for heartbeat requests from the PWA and quitting after they stop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment