New Phoenix Developer: Installation Directory, UFW Firewall Rules, Run Nonstop

Greetings,

I am just learning Elixir and Phoenix. I finished the elixir-lang.org tutorial and a bit of Phoenix. While I’m still not very confident in the language yet, I am extremely impressed so far.

Basically, I have a social media site (we’ll say example.com). It is developed using PHP/MySQL and I built a Node.js chat app a few years back just to learn about sockets and build some simple chat features, like one page with a big 1990s style chat room where everyone on the site can chat on one page, live private messaging for users to chat among people only on their friends list, and live web cam chat. I was successful in getting all of that set up, and haven’t touched it in years. From the beginning, I was never comfortable with Node.js and how one crash anywhere in the app causes the whole thing to crash completely, and how it simply didn’t have as much capability in case our site one day became popular with tons of users. I always hated “callback hell” as well… After reading about chat apps, I always knew that someday I would eventually learn Phoenix/Elixir to replace Node.js.

Right now, I have Ubuntu 20.04. When I login to my shell using the “ubuntu” user (like a root user), I installed Phoenix and set up a project in the ubuntu username directory “/home/ubuntu/chatter/”, the app name is “chatter”. I’ve been making some good progress getting the simple chat example to work, Presence to work and that awesome LiveDashboard working. Really impressed on how all those things just “worked” right out of the box without any arcane configurations.

I am running this stuff on my generic IP address, like [xxx.xxx.xxx.xxx]:4000 and through the shell. So now I have some questions on starting up a real project on the website itself.

First question: If my website directory is in: “/home/example/example.com”, what is the common practice for where where you would install the new project for use in production? Do I install it in “/home/example/chatter”, or “/home/example/example.com/chatter”? I’m seeing that in a standard install, some files are server stuff, and other are “assets”, which is stuff that I think goes in the website directory for browsers to load the JavaScript files. Or do I install it under the user “example” or directory: “/home/example/chatter” and copy and paste the “asset” files into the “/home/example/example.com/assets”?

Next question, for UFW firewall rules for port “4000”, should I set the “Allow” flag for 0.0.0.0 for everyone on the internet to access, or is there a common security practice for this for setting it up on a public-facing website? Phoenix will run on the same server as my website.

Lastly, how do I run Phoenix and my “chatter” app non-stop and have it automatically restart in case the server crashes? I’m cheap right now, so my server does sometimes crash from running out of memory every once in awhile. so I’m going to need my app to fire back up automatically again after reboots, crashes and server restarts, especially I’m sleeping and can’t get to the shell until I wake up.

Thanks

So it sounds like you have a VPS. I would suggest using mix release to package everything.

This will allow you to:

  • start in foreground with logs to stdout
  • package your assets
  • runtime configuration through environmental variables
  • bundling of erts,etc.
  • connect a remote shell

So just pick a directory and a user, just make sure it isn’t root. The release package will take care of the rest.

Docs:
https://hexdocs.pm/phoenix/releases.html
https://hexdocs.pm/mix/1.9.0/Mix.Tasks.Release.html

Generally speaking, most people use a reverse proxy to forward the traffic to phoenix on port 4000. The only ports available to the public should be 80 and 443. Search for nginx reverse proxy or haproxy reverse proxy guides. An alternative is to expose phoenix directly on port 443 itself and use a library like SiteEncrypt — site_encrypt v0.4.2 to manage the ssl certificate from LetsEncrypt.

You can just run the release via a supervisor in the foreground. I’m pretty old-school and have the logs piped out to stdout via runit / daemontools, but you can use systemd as well.

Hope that helps.

1 Like

Welcome to the forum! :wave:

For this portion I would recommend GitHub - hauleth/erlang-systemd: systemd utilities for Erlang applications

Thanks for the response. Basically, I’m set up on Dreamhost’s Cloud Compute service. It’s not actually a VPS, but I have my server configured that way to separate each of my websites/users, as I have about 20 websites hosted on one server, so that if one gets hacked, I can contain the damage to just one user.

I did some research on reverse proxy (been a few years since I worked with Node), but I remembered that I have a setup like below on Apache. This was thrown together and eventually worked for my Node.js chat app:

<VirtualHost *:443>
  ServerName www.example.com
  ServerAlias example.com
  DocumentRoot /home/example/example.com

  #// socket.io 1.0+ starts all connections with an HTTP polling request
  RewriteCond %{QUERY_STRING} transport=polling       [NC]
  RewriteRule /(.*)           http://127.0.0.1:3000/$1 [P]

  #// When socket.io wants to initiate a WebSocket connection, it sends an
  #// "upgrade: websocket" request that should be transferred to ws://
  RewriteCond %{HTTP:Upgrade} websocket               [NC]
  RewriteRule /(.*)           ws://127.0.0.1:3000/$1  [P]

  ProxyRequests Off  # can't remember why this is set to "Off"
  ProxyPass /chatnode http://127.0.0.1:3000
  ProxyPassReverse /chatnode http://127.0.0.1:3000

</VirtualHost>

I remember this part being a real pain working with Node and I didn’t understand it too well, so I’m hoping it will be easier with Phoenix.

How would you set up this block of code to run Node and Phoenix at the same time? I’d like to have our Node-based chat continue to run for our users while I experiment and develop the new Phoenix app at the same time.

UPDATE:

I’ve been working for a few hours now, I added the following to my apache VirtualHost tag:

  ProxyRequests Off
  ProxyPass /ex http://127.0.0.1:4000
  ProxyPassReverse /ex http://127.0.0.1:4000

However, I’m having problems getting the default “Welcome to Phoenix!” page on example.com/ex/

I edited /config/config.exs with:

config :chatter, ChatterWeb.Endpoint,
  url: [host: "localhost", path: "/ex"],

I edited: /lib/endpoint.ex with:

plug Plug.Static,
    at: "/ex",
    from: :chatter,

The welcome page shows up on example.com/ex, but none of the css, images or any other files load up. If I click a link in the source code, for example: <link rel="stylesheet" href="/ex/css/app.css"/>, I’ll get an error like:

Phoenix.Router.NoRouteError at GET /css/app.css
no route found for GET /css/app.css (ChatterWeb.Router)
Available routes
          page_path  GET  /                       ChatterWeb.PageController :index
live_dashboard_path  GET  /dashboard              Phoenix.LiveView.Plug :home
live_dashboard_path  GET  /dashboard/:page        Phoenix.LiveView.Plug :page
live_dashboard_path  GET  /dashboard/:node/:page  Phoenix.LiveView.Plug :page

I tried changing /lib/chatter_web/router.ex with various combinations, like:

 scope "/ex", ChatterWeb do
    pipe_through :browser

    get "/", PageController, :index
  end

or:

  scope "/", ChatterWeb do
    pipe_through :browser

    get "/ex", PageController, :index
  end

but I’m kinda stuck now.

I’m still having problems here trying to get my website URL https://www.example.com/ex/ to display the default “Welcome To Phoenix” page. I made some slight changes and made some progress. First, I created a new project in my web user directory where I plan to use it indefinitely. In apache, I’ve edited the virtual host to:

<VirtualHost *:443>
  ServerName www.example.com
  ServerAlias example.com
  DocumentRoot /home/example/example.com

  <Location /ex/>
    ProxyPass http://127.0.0.1:4000/ex/
    ProxyPassReverse http://127.0.0.1:4000/ex/
  </Location>
</VirtualHost>

and left the router.ex the same:

 scope "/ex", ChatterWeb do
    pipe_through :browser

    get "/", PageController, :index
  end

So when I go to that URL (without the port 4000 part), the css, js and image files loaded up. However, in the logs, I still get this error:

[info] GET /ex/phoenix/live_reload/frame
[debug] ** (Phoenix.Router.NoRouteError) no route found for GET /ex/phoenix/live_reload/frame (ExchatWeb.Router)
    (exchat 0.1.0) lib/phoenix/router.ex:402: ExchatWeb.Router.call/2
    (exchat 0.1.0) lib/exchat_web/endpoint.ex:1: ExchatWeb.Endpoint.plug_builder_call/2
    (exchat 0.1.0) lib/plug/debugger.ex:136: ExchatWeb.Endpoint."call (overridable 3)"/2
    (exchat 0.1.0) lib/exchat_web/endpoint.ex:1: ExchatWeb.Endpoint.call/2
    (phoenix 1.5.10) lib/phoenix/endpoint/cowboy2_handler.ex:65: Phoenix.Endpoint.Cowboy2Handler.init/4
    (cowboy 2.9.0) /home/example/exchat/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
    (cowboy 2.9.0) /home/example/exchat/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
    (cowboy 2.9.0) /home/example/exchat/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
    (stdlib 3.15.1) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

When I click on the “Live Dashboard” link (https://example.com/ex/dashboard), I get the same type of error:

##### Phoenix.Router.NoRouteError <small>at GET</small> <small>/ex/dashboard</small>

# no route found for GET /ex/dashboard (ExchatWeb.Router)

Just trying to get the default phoenix page working on a production website URL so I can begin practicing… Right now, I’m just randomly adding/subtracting “/ex” to various snippets in the endpoints/router files, probably breaking stuff in the process, and there is no good guide on the internet on just doing this basic thing of getting example.com:4000 to fully work on example.com/[some-directory]/