by clemens (06.10.2021)

Beware of Zombie Watchers in Elixir


Running custom background watchers with e.g. watchexec will lead to zombie processes if not wrapped in custom shell script that kills the process once elixir quits.

In a project I wanted to run rsync to copy my static assets to the /priv/static folder, so that I can easily clean that up on each build (i.e. delete old digest files).

So we need the following for this to work:

First the wrapper script in assets/watcher, don’t forget to chmod 744 the file to make it executable.

!/usr/bin/env bash

# See

# Start the program in the background
exec "$@" &

# Silence warnings from here on
exec >/dev/null 2>&1

# Read from stdin in the background and
# kill running program when stdin closes
exec 0<&0 $(
  while read; do :; done
  kill -KILL $pid1
) &

# Clean up
wait $pid1
kill -KILL $pid2
exit $ret
fatal: unable to read 57c1b0bcd5c9d60f7b85ec2eb79981a66042fc37

Then add the following config to your config/dev.ex to watch all files in assets/static/ and rsync them to priv/static/ on each change:

assets_dir = Path.expand("../assets", __DIR__)

config :my_app, MyAppWeb.Endpoint,
  watchers: [
    {Path.join(assets_dir, "watcher"),
       "rsync -r --delete --exclude /assets static/ ../priv/static/",
       cd: assets_dir