Running Vapor on Google Compute Engine fails to start?

Am trying deploy a Vapor 4 app on a standard Google Compute Engine instance and am getting an error at launch. I imagine its a permissions issue but there are few clues as to which they are.

$swift run Run
swift runServer failed to start: The operation could not be completed. (NIO.IOError error 1.)
[ NOTICE ] Server starting on [http://127.0.0.1:8080]

I've also attempted

$ sudo .build/release/Run
$ vapor run serve --env production

With the same result.

The reason why I'm on GCE instead of Heroku or some such is that its both an HTTP and socket server app thus need to expose multiple ports, which GCI allows with its configurable firewall rules.

Even tho Vapor is showing server is running on 8080 I'm not getting responses, perhaps due to the SwiftNIO error 1

Am on ubuntu 18.04 if that matters.

Thoughts? TIA

Some more info:

The NIO error appears to be occurring at binding of the socket:

channel = try bootstrap.bind(host: , port: ).wait()

as I'm binding to the GCE public IP here instead of the private IP. When I bind to the private IP, the error goes away and the server successfully binds to the port. However, when I try to connect to the public IP on that port no connection is possible.

Have you configured the firewall?

The server failed to start error is weird but for connecting - however, are you attempting to connect from the same machine or a different one? You need to set the hostname to 0.0.0.0 if you want to be able to connect from outside the machine

Thanks guys. The firewall was configured correctly, but it wasn't attaching to the instance correctly.

When I use the private address in the bind call it works there and the error goes away, tho that is cumbersome to manage. It seems I can reach the machine remotely on that port with the private IP hard coded.

I can use 0.0.0.0 and it will just use the network interface the VM has to bind to? I am seeing that work on the VM, but when I try that locally on my Mac, its not reachable (tho the Mac has multiple network interfaces).

Thanks again.

0.0.0.0 will bind to all interfaces.

In this case, it's entirely possible that your GCE instance does not know what its public IP address is, and so binding to it will fail. Is your public IP address visible on any interface if you run ifconfig?

Thanks Cory. the public IP is not listed on any interface on the machine. But, public clients connecting to the public IP with the vapor server bound to the private IP (or 0.0.0.0) do connect and get data.

But another question, why are the connections flapping for the client 2 connections that I'm running? e.g.:

Server started and listening on [IPv4]0.0.0.0/0.0.0.0:49899
[ NOTICE ] Server starting on http://0.0.0.0:80
  channel registered: [IPv4]162.236.246.167/162.236.246.167:60498
  channel registered: [IPv4]90.104.37.118/90.104.37.118:59417
channel unregistered: [IPv4]162.236.246.167/162.236.246.167:60498
  channel registered: [IPv4]162.236.246.167/162.236.246.167:60499
channel unregistered: [IPv4]90.104.37.118/90.104.37.118:59417
  channel registered: [IPv4]90.104.37.118/90.104.37.118:59419
channel unregistered: [IPv4]162.236.246.167/162.236.246.167:60499
  channel registered: [IPv4]162.236.246.167/162.236.246.167:60500
channel unregistered: [IPv4]90.104.37.118/90.104.37.118:59419
  channel registered: [IPv4]90.104.37.118/90.104.37.118:59520
channel unregistered: [IPv4]162.236.246.167/162.236.246.167:60500
  channel registered: [IPv4]162.236.246.167/162.236.246.167:60501
channel unregistered: [IPv4]90.104.37.118/90.104.37.118:59520
  channel registered: [IPv4]90.104.37.118/90.104.37.118:59526
channel unregistered: [IPv4]162.236.246.167/162.236.246.167:60501
  channel registered: [IPv4]162.236.246.167/162.236.246.167:60502
channel unregistered: [IPv4]90.104.37.118/90.104.37.118:59526
  channel registered: [IPv4]90.104.37.118/90.104.37.118:59530

The handler code is pretty simple from the SwiftNIO example:

// Invoked on client connection
public func channelRegistered(context: ChannelHandlerContext) {
  print("  channel registered:", context.remoteAddress ?? "unknown")
}

// Invoked on client disconnect
public func channelUnregistered(context: ChannelHandlerContext) {
  print("channel unregistered:", context.remoteAddress ?? "unknown")
}

Does this flapping ever hit a point where it will run out of available ports or any other resources that would cause a problem?

This is a signal that the public IP is not owned by your machine, but by GCE itself. This is why the bind to the public IP was failing: your machine doesn't think it owns that IP. Binding to 0.0.0.0 is a good solution here.

Seems like one or the other of the clients is closing the connection. Nothing exciting here, you still only ever have two connections outstanding. You're at no risk of running out of FDs here.

Thanks again Cory.

The interesting bit on the flapping is those clients are no longer connected. One is a telnet session I started but then stopped. Even if I reboot the instance and start the vapor app the flapping just starts with no new connections attempted.

While I wouldn't entirely be worried about this given that it seems to 'just work' but there is logic in the app that upon each new client a set of initial data has to be sent, which this flapping would flood the client with startup info when it shouldn't. Am wondering if this is something between the public and private IP address specific to GCE and can be remedied.

It may be worth logging whether there is any data being transferred, as well as trying to isolate what these two IP addresses have to do with your infrastructure. These could well be a TCP liveness probe from the GCE infrastructure, attempting to determine whether your app is still running by means of making inbound TCP connections.