Hi there, I’m working through my first release with elixir/phoenix. I’ve built a release with distillery and found that it crashes when I start it since it’s trying to access port 80 and linux restricts ports under 1024. Starting and running the server as root would work but I’m not going to do that.
In the past, working with other stacks the solutions I’ve used have been:
Use apache/nginx which do have elevated permissions to start but then drop them.
Based on what I’ve seen online, the best solution may be to use setcap to give the binary permission to use the port. However I’ve tried that on erl, mix, iex and elixir and still can’t get the server to bind to port 80.
The executable that’s actually opening the port is the BEAM VM. Start iex in one window, then run ps aux | grep beam in another to see the details of the process. In my case, the executable is /usr/lib/erlang/erts-7.2/bin/beam.smp. Try setting the capabilities on that file instead.
If you only have one server, I personally am a fan of the nginx reverse proxy approach, kind of simple to setup. With it, you could also run multiple instances of your app listening on different ports and have nginx distribute incoming requests, it would then be easy in case you need rolling upgrades (which in Elixir might not be necessary). It would also be easier if you want to have multiple Elixir apps binding to different ports but want to use the same domain, for example.
But is it the best way? I don’t know Looking for some information too in case someone with more experience chimes in.
Fantastic! This works for starting the server manually. Still get [error] Failed to start Ranch listener Myapp.Endpoint.HTTP in :ranch_tcp:listen([port: 80]) for reason :eacces (permission denied) when running the script created by distillery, but this is progress!
I haven’t used Exrm or Distillery for a while, but I think it typically bundles the Erlang runtime system. That means you need to set the capabilities on the BEAM binary that’s included in your release.
Got it! I had to use setcap on a file distillery created called run_erl. On my system it was:
sudo setcap CAP_NET_BIND_SERVICE=+eip /home/ubuntu/myapp/_build/prod/rel/myapp/erts-8.1/bin/run_erl
Now I can start the web server from its run script and with just sudo start myapp (since I’d already created the /etc/init/myapp.conf script)
Wow this was a bit of a rabbit but it should be easier for the next person searching on google. Thanks @voltone
I’ll definitely keep your idea in mind too, @bobbypriambodo. Nginx is always a possibility in the future if I need it for filtering spammy requests to “admin.php” and other non-existent URLs that seem to be popular for bots to hit.
On FreeBSD I just bind ports 80 and 443 to ports 8080 and 8443, respectively, with pf. And start the app without any need for sudo. I think it’s also possible on Linux, albeit with another firewall.
On Ubuntu you can alternatively use authbind, which lets an administrator grant permissions to bind to specific ports by user/group. This means the permission is not bound to any particular application binary, and therefore persists when the binary is updated.