Running a phoenix appplication on a unix domain socket

I want to run a bunch of phoenix applications on unix domain sockets, and put a nginx reverse proxy in front of them. Plug.Cowboy can listen on a unix domain socket by passing a {:local PATH} to the ip parameter of the endpoint, and it works. Except there is a permission problem. The socket file is created with 0755 permission so my reverse proxy cannot connect to it.

Is there anyway to create the socket file with a more relaxed permission? I don’t want to change my umask. I can make it work by changing the permission manually after the application is up, but that kind of sucks.

I know that it sucks, but this is what manpage says:

When creating a new socket, the owner and group of the socket file are set according to the usual rules. The socket file has all permissions enabled, other than those that are turned off by the process umask(2).

The owner, group, and permissions of a pathname socket can be changed (using chown(2) and chmod(2)).

I understand, but I don’t have a good place to chmod the file. Before the endpoint start, there is no file. How do I know the enpoint has started, and to chmod accordingly? starting a Task to poll the file until it exists? that sucks.

I found another problem when using a Unix socket. It does not delete the file on start. I can do that in my startup script no problem, but what if the Endpoint crashed, but the whole BEAM process does not crash, the file will be there and preventing the Endpoint to startup.

About 1st problem - you can start task after your endpoint that will chmod the socket, like:

[
  # …
  Endpoint,
  {Task, fn -> File.chmod!("/tmp/socket", 0o777 end},
  # …
]

About 2nd problem - there is no direct solution for that. It would highly depend on how you manage startup of your application. If you are using systemd then you could use cleanup action that runs after process shuts down or you could use socket activation instead. It highly depends on your configuration there.

I will try, thank you.

For the second problem, my fear is the endpoint can crash then is restarted by the supervisor. Because the socket file would still be there, it will crash again into an endless loop. Systemd won’t know because the whole beam process is still there.

I think the best solution is to patch cowboy, so it will rm the socket file on startup, and make the permission to the socket file wide open. Http over unix domain socket is unspeced and only used behind reverse proxy. IMHO, it make the most sense this way.

Would it work to write a “GenServer sandwich” around the Endpoint? The first GS would do nothing on boot, but would register to receive callback on shutdown and delete the file. The second would set permissions on startup and do nothing on shutdown.

You could define the 3 processes in a supervisor so that if any of them crash, they all shutdown and reboot. There’s no patching involved in this approach.

If you do that via systemd.socket then systemd will be responsible for creating and deleting socket, so it will not end in endless loop.

You can do it on your own in Application.start/2 callback instead of patching Cowboy. Or as I said, you can do it via ExecStartPost= or ExecStopPost= directives.

1 Like

Thanks @hauleth and @sb8244 . Let me try to contact the cowboy author and send a PR first. I think patching cowboy will provide least surprise for future users and I cannot think of any downsides.