Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bun incorrectly reports "port in use" inside docker container #5315

Closed
sartak opened this issue Sep 13, 2023 · 9 comments · Fixed by #6533
Closed

bun incorrectly reports "port in use" inside docker container #5315

sartak opened this issue Sep 13, 2023 · 9 comments · Fixed by #6533
Labels
bug Something isn't working docker An issue that occurs when running in Docker node.js Compatibility with Node.js APIs

Comments

@sartak
Copy link

sartak commented Sep 13, 2023

What version of Bun is running?

1.0.1+31aec4ebe325982fc0ef27498984b0ad9969162b

What platform is your computer?

inside container: Linux 5.4.0-144-generic x86_64 unknown, on host: Linux 5.4.0-144-generic x86_64 x86_64

What steps can reproduce the bug?

  1. sudo docker run -it debian:sid-slim
    • (Everything that follows is inside the container)
  2. apt update && apt install -y curl unzip
  3. curl -fsSL https://bun.sh/install | bash
  4. source /root/.bashrc
  5. bunx detect-port-alt 3000
  6. apt install -y npm
  7. npx -y detect-port-alt 3000

What is the expected behavior?

bunx detect-port-alt 3000, like npx detect-port-alt 3000, correctly reports that port 3000 is available.

What do you see instead?

root@d08a0ebb9212:/# bunx detect-port-alt 3000
port 3000 was occupied
get available port 52021
root@d08a0ebb9212:/# npx -y detect-port-alt 3000
get available port 3000

Additional information

This is blocking me from running a vanilla create-react-app project using bun.

It's important to try step 5 above (bunx detect-port-alt 3000) before step 6 (installing node) because if node is available, it will be used instead of bun (giving misleading results):

root@d08a0ebb9212:/# echo '#!/bin/echo' > `which node`
root@d08a0ebb9212:/# bunx detect-port-alt 3000
/usr/bin/node /tmp/detect-port-alt@latest--bunx/node_modules/.bin/detect 3000
@sartak sartak added the bug Something isn't working label Sep 13, 2023
@sirenkovladd
Copy link
Contributor

less repro

index.js

const net = require('net');

const server = new net.Server();

server.on('error', err => {
  console.log(err);
  server.close();
});

server.listen(3000, 'localhost', () => {
  port = server.address();
  server.close();
  console.log(port);
});
docker run -it -v ./index.js:/tmp/index.js -w /tmp debian:sid-slim
apt update && apt install -y curl unzip
curl -fsSL https://bun.sh/install | bash
~/.bun/bin/bun index.js
root@206ae2e82deb:/tmp# ~/.bun/bin/bun index.js 
error: Failed to listen at localhost
 code: "EADDRNOTAVAIL"

@Electroid Electroid added the node.js Compatibility with Node.js APIs label Sep 13, 2023
@Hanaasagi
Copy link
Collaborator

I suspect the issue might be related to the implementation of usocket, but I cannot be completely sure at this moment. It could be a general issue, but there haven't been any issues reported in the usocket repository before.

The problem seems to be related to ipv6 configuration in the container's /etc/hosts file. It happens when the address ::1 is not configured or there's no default route for ipv6. docker requires additional configuration for v6.

root@f2271f4affd3:/tmp/T# cat /etc/hosts
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback  # Remove this line will work
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2      f2271f4affd3

Below are the results of my system call tracing

2023-09-14_19-50

if (getaddrinfo(host, port_string, &hints, &result)) {
return LIBUS_SOCKET_ERROR;
}
LIBUS_SOCKET_DESCRIPTOR listenFd = LIBUS_SOCKET_ERROR;
struct addrinfo *listenAddr;
for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) {
if (a->ai_family == AF_INET6) {
listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol);
listenAddr = a;
}
}
for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) {
if (a->ai_family == AF_INET) {
listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol);
listenAddr = a;
}
}

getaddrinfo returns both 127.0.0.1 and ::1, but in the above code, it chooses the ::1 address, which causes bind error EADDRNOTAVAIL.

We can also reproduce this issue using a C program. It lists the results of getaddrinfo and performs a bind on ::1.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

int main() {
    struct addrinfo hints, *result, *rp;
    int status, sockfd;

    memset(&hints, 0, sizeof(hints));
    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if ((status = getaddrinfo("localhost", "3000", &hints, &result)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
        exit(EXIT_FAILURE);
    }

    for (rp = result; rp != NULL; rp = rp->ai_next) {
        void *addr;
        char ipstr[INET6_ADDRSTRLEN];

        if (rp->ai_family == AF_INET) {
            struct sockaddr_in *ipv4 = (struct sockaddr_in *)rp->ai_addr;
            addr = &(ipv4->sin_addr);
        } else {
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)rp->ai_addr;
            addr = &(ipv6->sin6_addr);
        }

        if (inet_ntop(rp->ai_family, addr, ipstr, sizeof(ipstr)) != NULL) {
            printf("Find IP Address: %s\n", ipstr);
            if (rp->ai_family == AF_INET6) {
                printf("Try to bind %s\n", ipstr);
                sockfd = socket(rp->ai_family, SOCK_STREAM, rp->ai_protocol);
                if (sockfd == -1) {
                    perror("socket");
                    continue;
                }

                if (bind(sockfd, rp->ai_addr, rp->ai_addrlen) == -1) {
                    perror("bind");
                } else {
                    printf("Bind %s 3000 successfully.\n", ipstr);
                }

                close(sockfd);
            }

        } else {
            perror("inet_ntop");
        }
    }

    freeaddrinfo(result);
    return 0;
}

Output (gcc):

Find IP Address: 127.0.0.1
Find IP Address: ::1
Try to bind ::1
bind: Cannot assign requested address

@sartak
Copy link
Author

sartak commented Sep 14, 2023

Removing localhost from the ::1 entry in /etc/hosts (limiting localhost to just 127.0.0.1) does seem to work around the issue, thanks!

@sirenkovladd
Copy link
Contributor

But Bun should fix this without editing /etc/hosts

@Electroid
Copy link
Contributor

But Bun should fix this without editing /etc/hosts

Yes, we will fix this.

@Electroid Electroid added the docker An issue that occurs when running in Docker label Sep 16, 2023
@shelterit
Copy link

Any updates on this one? I run Bun in a lot of containers using Bun.serve, but currently I can't use them because of this (the /etc/hosts file is more or less virtual in containers). I guess back to Node.js for that part for now.

I also wanted to add that Bun doesn't report the failing ports if run with --hot, it either fails silently or reports listening, but if you try the ports you get an immediate "RangeError: Maximum call stack size exceeded."

@Hanaasagi
Copy link
Collaborator

Hanaasagi commented Oct 11, 2023

I run Bun in a lot of containers using Bun.serve, but currently I can't use them because of this (the /etc/hosts file is more or less virtual in containers).

You can directly specify a valid IP address (e.g. 0.0.0.0), which can bypass the localhost lookup.

Bun.serve({
  hostname: "0.0.0.0",
  port: 8080,
  // ...
});

If here it's expected that when IPv6 binding fails, it should automatically attempt to bind to the IPv4 address under the same hostname (/etc/hosts), I can open a pull request to fix this.

@odicho
Copy link

odicho commented Oct 15, 2023

Not sure if this is related, but I am experiencing this isssue:

const server = fastify();

server.listen({ host: "0.0.0.0", port: 3000 }, (err, address) => {
  if (err) {
    console.error(err);
    process.exit(1);
  }
  console.log(`Server listening at ${address}`);
});

Fastify version:
"fastify": "^4.23.2"

/etc/hosts file:

127.0.0.1       localhost
255.255.255.255 broadcasthost
::1             localhost

Trace:

$ bun run --hot ./src/index.ts
337 |       family: isIPv6(address) ? "IPv6" : "IPv4",
338 |       port: this.#server.port
339 |     };
340 |   }
341 | 
342 |   listen(port, host, backlog, onListen) {
                   ^
EADDRINUSE: Failed to start server. Is port 3000 in use?
 syscall: "listen"

      at listen (node:http:342:16)
      at /node_modules/fastify/lib/server.js:250:6
      at manageErr (/node_modules/fastify/fastify.js:602:10)
      at exit (/node_modules/fastify/lib/hooks.js:113:4)
      at next (/node_modules/fastify/lib/hooks.js:124:8)
      at hookRunnerApplication (/node_modules/fastify/lib/hooks.js:98:2)
      at /node_modules/fastify/fastify.js:584:10
      at _encapsulateThreeParam (/node_modules/avvio/boot.js:562:6)
      at timeoutCall (/node_modules/avvio/boot.js:458:4)

error: script "dev" exited with code 1 (SIGHUP)

I tried commenting out ::1 line from /etc/hosts file, but still didn't work. I want to use "0.0.0.0" instead of localhost when running my server locally.

@Hanaasagi
Copy link
Collaborator

@odicho I think what you're experiencing might be a separate issue. If you could open a new issue and provide some additional information, that would be greatly appreciated. Please include details such as whether you're on Linux or macOS, whether it fails every time, and if it succeeds when --hot is removed.

dylan-conway added a commit that referenced this issue Oct 19, 2023
… under the same hostname. (#6533)

* fix(serve): When IPv6 configuration is incorrect, attempt to bind to IPv4 address under the same hostname.
Close: #5315

* fix review

* fix review again

---------

Co-authored-by: Ashcon Partovi <ashcon@partovi.net>
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working docker An issue that occurs when running in Docker node.js Compatibility with Node.js APIs
Projects
None yet
6 participants