Using phoenix without nginx

I am having problems trying to access the phoenix app from outside my digitalocean vps.

I already checked the following topic:

The URL mentioned in the above topic uses iptables with a --dport, which fails saying “unknown option --dport”
I then tried the instructions mentioned in this URL:

I can run

mix phx.server

curl localhost:4000 # works properly from within the server.

However I get connection refused with the following commands from outside the server:

curl # or curl :4000

I have disabled ufw on my server:

sudo ufw status
Status: inactive

The following is my iptables configuration:

sudo iptables -L INPUT
Chain INPUT (policy ACCEPT)
target prot opt source destination
ufw-before-logging-input all – anywhere anywhere
ufw-before-input all – anywhere anywhere
ufw-after-input all – anywhere anywhere
ufw-after-logging-input all – anywhere anywhere
ufw-reject-input all – anywhere anywhere
ufw-track-input all – anywhere anywhere
ACCEPT tcp – anywhere anywhere tcp dpt:4000
ACCEPT tcp – anywhere anywhere tcp dpt:4000 state NEW limit: up to 5/min burst 10 mode srcip htable-expire 300000`

sudo iptables -L -n -t nat
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
REDIRECT tcp – 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 redir ports 4000
REDIRECT tcp – 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 redir ports 4000

EDIT:
The reason the commands were failing in the first URL was because I forgot to run them with sudo. The commands work now, but I still can’t access the URL outside the server.
My configuration is now like this:

nft list ruleset
table ip filter {
chain INPUT {
type filter hook input priority filter; policy accept;
counter packets 6110 bytes 385214 jump ufw-before-logging-input
counter packets 6110 bytes 385214 jump ufw-before-input
counter packets 6110 bytes 385214 jump ufw-after-input
counter packets 6110 bytes 385214 jump ufw-after-logging-input
counter packets 6110 bytes 385214 jump ufw-reject-input
counter packets 6110 bytes 385214 jump ufw-track-input
meta l4proto tcp tcp dport 4000 counter packets 11 bytes 560 accept
meta l4proto tcp tcp dport 4000 ct state new meter HTTP { ip saddr timeout 300s limit rate 5/minute burst 10 packets} counter packets 0 bytes 0 accept
meta l4proto tcp tcp dport 4000 ct state new meter HTTP { ip saddr timeout 300s limit rate 5/minute burst 10 packets} counter packets 0 bytes 0 accept
}

    chain FORWARD {
            type filter hook forward priority filter; policy accept;
            counter packets 0 bytes 0 jump ufw-before-logging-forward
            counter packets 0 bytes 0 jump ufw-before-forward
            counter packets 0 bytes 0 jump ufw-after-forward
            counter packets 0 bytes 0 jump ufw-after-logging-forward
            counter packets 0 bytes 0 jump ufw-reject-forward
            counter packets 0 bytes 0 jump ufw-track-forward
    }

    chain OUTPUT {
            type filter hook output priority filter; policy accept;
            counter packets 6014 bytes 402628 jump ufw-before-logging-output
            counter packets 6014 bytes 402628 jump ufw-before-output
            counter packets 6014 bytes 402628 jump ufw-after-output
            counter packets 6014 bytes 402628 jump ufw-after-logging-output
            counter packets 6014 bytes 402628 jump ufw-reject-output
            counter packets 6014 bytes 402628 jump ufw-track-output
    }

    chain ufw-after-forward {
    }

    chain ufw-after-input {
    }

    chain ufw-after-logging-forward {
    }

    chain ufw-after-logging-input {
    }

    chain ufw-after-logging-output {
    }

    chain ufw-after-output {
    }

    chain ufw-before-forward {
    }

    chain ufw-before-input {
    }

    chain ufw-before-logging-forward {
    }

    chain ufw-before-logging-input {
    }

    chain ufw-before-logging-output {
    }

    chain ufw-before-output {
    }

    chain ufw-reject-forward {
    }

    chain ufw-reject-input {
    }

    chain ufw-reject-output {
    }

    chain ufw-track-forward {
    }

    chain ufw-track-input {
    }

    chain ufw-track-output {
    }

}
table ip nat {
chain PREROUTING {
type nat hook prerouting priority dstnat; policy accept;
meta l4proto tcp tcp dport 80 counter packets 11 bytes 560 redirect to :4000
}

    chain INPUT {
            type nat hook input priority 100; policy accept;
    }

    chain OUTPUT {
            type nat hook output priority -100; policy accept;
    }

    chain POSTROUTING {
            type nat hook postrouting priority srcnat; policy accept;
    }

}
table ip6 filter {
chain INPUT {
type filter hook input priority filter; policy accept;
counter packets 231 bytes 89525 jump ufw6-before-logging-input
counter packets 231 bytes 89525 jump ufw6-before-input
counter packets 231 bytes 89525 jump ufw6-after-input
counter packets 231 bytes 89525 jump ufw6-after-logging-input
counter packets 231 bytes 89525 jump ufw6-reject-input
counter packets 231 bytes 89525 jump ufw6-track-input
}

    chain FORWARD {
            type filter hook forward priority filter; policy accept;
            counter packets 0 bytes 0 jump ufw6-before-logging-forward
            counter packets 0 bytes 0 jump ufw6-before-forward
            counter packets 0 bytes 0 jump ufw6-after-forward
            counter packets 0 bytes 0 jump ufw6-after-logging-forward
            counter packets 0 bytes 0 jump ufw6-reject-forward
            counter packets 0 bytes 0 jump ufw6-track-forward
    }

    chain OUTPUT {
            type filter hook output priority filter; policy accept;
            counter packets 243 bytes 90293 jump ufw6-before-logging-output
            counter packets 243 bytes 90293 jump ufw6-before-output
            counter packets 243 bytes 90293 jump ufw6-after-output
            counter packets 243 bytes 90293 jump ufw6-after-logging-output
            counter packets 243 bytes 90293 jump ufw6-reject-output
            counter packets 243 bytes 90293 jump ufw6-track-output
    }

    chain ufw6-after-forward {
    }

    chain ufw6-after-input {
    }

    chain ufw6-after-logging-forward {
    }

    chain ufw6-after-logging-input {
    }

    chain ufw6-after-logging-output {
    }

    chain ufw6-after-output {
    }

    chain ufw6-before-forward {
    }

    chain ufw6-before-input {
    }

    chain ufw6-before-logging-forward {
    }

    chain ufw6-before-logging-input {
    }

    chain ufw6-before-logging-output {
    }

    chain ufw6-before-output {
    }

    chain ufw6-reject-forward {
    }

    chain ufw6-reject-input {
    }

    chain ufw6-reject-output {
    }

    chain ufw6-track-forward {
    }

    chain ufw6-track-input {
    }

    chain ufw6-track-output {
    }

}

What about using Traefik? It works really well for me.

It’s not that I want to avoid nginx (or alternatives) completely, it’s just that I want to learn what I’m doing wrong here since this should be a simple setup that I thought should work.

Your Phoenix app might only be listening on the localhost interface. I’m do not think the firewall rules rewrite requests from your public IP to 127.0.0.1?

You can check which interface Phoenix is listening on with netstat -tulpn | grep beam.

If it is only listening on 127.0.0.1, you might want to check the configuration for your app’s endpoint: Phoenix.Endpoint — Phoenix v1.6.15

If you set it to something like config MyAppWeb.Endpoint, http: [ip: {0, 0, 0, 0}]then it should listen on all interfaces. But please beware that this might not be best practice, and you probably want SSL and all that stuff :slight_smile:

2 Likes

Few days ago I tried to use Phoenix without reverse proxy, and you need to make sure ports are open (like you tried to do so) you can use nmap scan or any online tool to verify opened ports.
I’ve used ufw allow 443 or 4000 if you want to have website at that port, but in order to run on port 443 or 80 you need to run that process as sudo, another option is to use setcap (which I also used for SSL certs) . I don’t know about iptables :man_shrugging:t2:

Thank you! This worked! I searched for “Endpoint” in my codebase, and found the following line in config/dev.exs:

For development, we disable any cache and enable

debugging and code reloading.

The watchers configuration can be used to run external

watchers to your application. For example, we use it

with esbuild to bundle .js and .css sources.

config :leftovers, LeftoversWeb.Endpoint,

Binding to loopback ipv4 address prevents access from other machines.

Change to ip: {0, 0, 0, 0} to allow access from other machines.

http: [ip: {127, 0, 0, 1}, port: 4000],

1 Like