Framework: [8.x] Local dev server spawns a new server on `port number + 1` when environment reloaded

Created on 3 Sep 2020  路  16Comments  路  Source: laravel/framework

  • Laravel Version: 8.x-dev
  • Jetstream Version: 0.6.0
  • PHP Version: 7.4.9
  • Database Driver & Version: SQLite 3.22

Description:

Development server (run with ./artisan serve) cannot listen on same port after .env file reloaded, and starts another server on one port higher. The existing dev server is not shut down when the .env file is edited and saved, so the port's already in use.

Steps To Reproduce:

  1. Create fresh Laravel project using the new installer: laravel new foo --jet --dev.
  2. Configure the database details in .env, then run migrations with ./artisan migrate:fresh.
  3. Serve the project with ./artisan serve - a local dev server is successfully started on port 8000
  4. Modify the .env file and save - a new local dev server is started on port 8001
  5. http://localhost:8000 and http://localhost:8001 both serve my project.
paul@ubuntu:~/projects/foo$ ./artisan serve
Starting Laravel development server: http://127.0.0.1:8000
[Thu Sep  3 16:56:31 2020] PHP 7.4.9 Development Server (http://127.0.0.1:8000) started
[Thu Sep  3 16:56:45 2020] 127.0.0.1:51424 Accepted
[Thu Sep  3 16:56:45 2020] 127.0.0.1:51424 Closing

# .env file is modified.

Environment modified. Restarting server...
[Thu Sep  3 16:57:42 2020] Failed to listen on 127.0.0.1:8000 (reason: Address already in use)
Starting Laravel development server: http://127.0.0.1:8001
[Thu Sep  3 16:57:42 2020] PHP 7.4.9 Development Server (http://127.0.0.1:8001) started

# .env file is modified again.

Environment modified. Restarting server...
[Thu Sep  3 16:58:25 2020] Failed to listen on 127.0.0.1:8001 (reason: Address already in use)
Starting Laravel development server: http://127.0.0.1:8002
[Thu Sep  3 16:58:25 2020] PHP 7.4.9 Development Server (http://127.0.0.1:8002) started

needs more info

All 16 comments

I can't reproduce this. Works fine for me:

master $ a serve                                                                                                                                                                                                                        ~/Sites/test-laravel8
Starting Laravel development server: http://127.0.0.1:8000
[Thu Sep  3 18:25:13 2020] PHP 7.4.9 Development Server (http://127.0.0.1:8000) started
[Thu Sep  3 18:25:15 2020] 127.0.0.1:52735 Accepted
[Thu Sep  3 18:25:15 2020] 127.0.0.1:52735 Closing
[Thu Sep  3 18:25:15 2020] 127.0.0.1:52739 Accepted
[Thu Sep  3 18:25:15 2020] 127.0.0.1:52739 Closing
[Thu Sep  3 18:25:15 2020] 127.0.0.1:52740 Accepted
[Thu Sep  3 18:25:15 2020] 127.0.0.1:52740 Closing
Environment modified. Restarting server...
[Thu Sep  3 18:25:38 2020] PHP 7.4.9 Development Server (http://127.0.0.1:8000) started
[Thu Sep  3 18:25:42 2020] 127.0.0.1:52763 Accepted
[Thu Sep  3 18:25:43 2020] 127.0.0.1:52763 Closing

I've the same problem

Starting Laravel development server: http://127.0.0.1:8000
[Thu Sep  3 23:13:51 2020] Failed to listen on 127.0.0.1:8000 (reason: Address already in use)
Starting Laravel development server: http://127.0.0.1:8001
[Thu Sep  3 23:13:54 2020] 127.0.0.1:48284 [200]: /favicon.ico
[Thu Sep  3 23:13:58 2020] 127.0.0.1:48320 [200]: /favicon.ico
Environment modified. Restarting server...
[Thu Sep  3 23:19:47 2020] Failed to listen on 127.0.0.1:8001 (reason: Address already in use)
Starting Laravel development server: http://127.0.0.1:8002
Environment modified. Restarting server...
[Thu Sep  3 23:47:49 2020] Failed to listen on 127.0.0.1:8002 (reason: Address already in use)
Starting Laravel development server: http://127.0.0.1:8003

Hi @driesvints. Is there anything else I can do, or any information I can provide that would assist?

I've just tried this again, with an even simpler setup (no --jet), and no db setup or migrations, not even changed the .env file from the default (and all I did to force reload of the environment is to add an extra 'l' to APP_NAME in the .env file:

$ laravel new --dev foo
...
$ cd foo
$ php artisan serve
Starting Laravel development server: http://127.0.0.1:8000
[Thu Sep  3 19:24:40 2020] PHP 7.4.9 Development Server (http://127.0.0.1:8000) started
Environment modified. Restarting server...
[Thu Sep  3 19:24:57 2020] Failed to listen on 127.0.0.1:8000 (reason: Address already in use)
Starting Laravel development server: http://127.0.0.1:8001
[Thu Sep  3 19:24:58 2020] PHP 7.4.9 Development Server (http://127.0.0.1:8001) started

Unsure if it matters, but I'm running Ubuntu 18.04.5, Composer 1.10.10, and of course version 4.0.0 of the Laravel installer. Let me know if I can provide any further information.

The old processes are never terminated, leading to a bunch of zombie processes and used up ports.

1
2

Is everyone who's experiencing this using Ubuntu? I can't reproduce this on macOS.

Xubuntu in my case.

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.5 LTS
Release:    18.04
Codename:   bionic

The processes don't 'zombie' for me: if I run php artisan serve then edit .env to force the environment reload, then the site on port 8000 (the 'old' one) as well as the site on port 8001 (the 'new' one) both work, serving up the local dev site as expected.

Also, if I serve the local dev site and force the config to reload 10 times, it spawns sites on ports 8000 to 8010, then cannot create any more (as I think the upper port limit is 8010) then the serve command quits, leaving the 11 websites still being served (and they still work as expected), and I have to kill them via top:

Screenshot from 2020-09-04 09-56-14

@vaughany That's what I meant by "zombie" - they're just left hanging around, even though they should have terminated. They do work, they're just not supposed to exist anymore.

@AegirLeet Apologies, took you too literally. :)

@driesvints Some troubleshooting with different Ubuntus:

I used Vagrant to spin up Ubuntu 16.04.5 LTS with PHP 7.4.9, and got the same issue.

I used Vagrant to spin up Ubuntu 20.04 LTS with PHP 7.4, and got the same issue.

I used Vagrant to spin up Debian 10.5 with PHP 7.3.19, and got the same issue.

Pull requests welcome. I don't use Ubuntu.

Troubleshooting steps: running ps -ef | grep php when artisan serve is running shows, amongst other cruft:

sh -c '/usr/bin/php7.4' -S 127.0.0.1:8000 '/foo/server.php'
/usr/bin/php7.4 -S 127.0.0.1:8000 /foo/server.php

So the first line is the shell command, and the second is the command the shell command runs. Makes sense so far.

If I reload the .env file, and run ps -ef | grep php again, I now see this:

sh -c '/usr/bin/php7.4' -S 127.0.0.1:8001 '/foo/server.php'
/usr/bin/php7.4 -S 127.0.0.1:8001 /foo/server.php
/usr/bin/php7.4 -S 127.0.0.1:8000 /foo/server.php <-- this should have been killed.

So it seems that the original command sh -c has been successfully terminated, however the command _run_ by sh was not. I'll look into this further as soon as I can.

Edit: stripped the info out, but the command starting /usr/bin/php7.4 is definitely a child process of the command starting sh -c. The PID that $process->getPid() has is that of the parent, but as I said, it seems that the child process is not killed when the parent process is.

https://github.com/symfony/symfony/issues/5030

Prefixing the command at https://github.com/laravel/framework/blob/3624a82ea5e36dbad1645975e558dfd8c063d730/src/Illuminate/Foundation/Console/ServeCommand.php#L107-L120 with exec seems to work.

This would break Windows though (not sure about macOS), so it would have to be done conditionally for Linux only.

Sure enough, forcing Process::isSigchildEnabled() (as mentioned in the Symfony issue @AegirLeet linked to) to return true fixes the issue. So I guess this is primarily a Symfony issue? Unsure how to progress with this.

Looks like Symfony's process component actually already has a fix for this in place at https://github.com/symfony/symfony/blob/c2522fa325c36baf278b4390a0d7fd0a339ebc1c/src/Symfony/Component/Process/Process.php#L298-L301, but this isn't used because the commandline isn't supplied as an array (?). Letting the process component handle it through that mechanism would probably be better.

Using new Process(['/path/to/php', '-S', 'hostname:port', '/path/to/server.php'], ...) instead of Process::fromShellCommandline(...) works, at least on Linux. Not sure what other effects that might have though.

Was this page helpful?
0 / 5 - 0 ratings