What's the main difference between hub_connect_url vs hub_bind_url?

hub_bind_url is the URL passed to APIs such as socket.listen. This is what interface the socket is “bound to” or “listening on”. Most commonly, the ip has one of two values:

  1. 127.0.0.1 for a single-host deployment. This will only work if the proxy and single-user servers are on the same host (or in the same container if you are using docker). This is the default value.
  2. 0.0.0.0 for a multi-host (multi-container is multi-host) deployment, which means “all IPv4 interfaces”.

You can also specify a single LAN IP if you want to. This is typically useful in cluster scenarios where you want to avoid listening unnecessarily on publicly-accessible ips and only on the internal LAN.

In a container scenario, this will almost always want to be "0.0.0.0" because other containers are “external” from a networking perspective due to docker’s virtual networking/namespaces.

hub_connect_url is the URL passed to other entities, such as the proxy or single-user servers when they connect to the Hub. This should use an ip or hostname that is connectable from the proxy and single-user servers. In Docker, this is often the name of the Hub container.

In docker, a common configuration would be:

c.JupyterHub.hub_bind_url = "http://0.0.0.0:8081"
c.JupyterHub.hub_connect_url = "http://hub-container-name:8081"

because docker-networking makes a container’s name its hostname, connectable from other containers on a shared docker network. Importantly, docker networking does not mean that binding on 0.0.0.0 is publicly accessible on the host. Only within the docker network used by the Hub container.

The ports can (but typically do not) mismatch if you have routing layers in between, such as docker-swarm endpoints or kubernetes services, or other reverse-proxy layers. In those cases, you would use the listening port of the intermediate service in hub_connect_url rather than the port the Hub binds on.