Provide a simpler way to default runserver IP/port to 0.0.0.0:8000

1,714 views
Skip to first unread message

emo...@mozilla.com

unread,
Nov 28, 2016, 8:49:56 AM11/28/16
to Django developers (Contributions to Django itself)
This was filed as https://code.djangoproject.com/ticket/27537 , but moving here for discussion.

We have the following use case:
* An app uses a VM for the local development environment (eg via Vagrant).
* It's required to be accessible from the VM host (either on it's own IP, or on localhost via eg Virtualbox port forwarding) to allow development to occur using editors/browsers on the host. (The configuration is such that connections are only allowed from the VM host and not other machines on the network, so this is still secure.)
* The app has no need for nginx/apache, since production is Heroku (where that's discouraged) and so it uses gunicorn + WhiteNoise.
* During development, devs want to use runserver rather than gunicorn for convenience (and familiarity for existing Django devs).
* However `./manage.py runserver` doesn't work, since that binds to the loopback adapter of the VM guest OS only (127.0.0.1) and not 0.0.0.0. (This is a sensible default.)
* Therefore `./manage.py runserver 0.0.0.0:8000` must be used, which:
  - is not immediately obvious to devs new to the project (which is open source, so we often have new contributors). Docs are great, but not everyone reads them fully - defaults that work out of the box are best.
  - means even once they realise the need for the explicit binding address, they have to type it out every time.

Current workarounds are:
a) Add an nginx/apache/varnish service to the VM to reverse proxy from 0.0.0.0:X to 127.0.0.0:8000, increasing bootstrap time and boilerplate.
b) Not use runserver, and use gunicorn instead (where the `PORT` environment variable can be set, making it bind to `0.0.0.0:<PORT>` [1]).
c) Add bash aliases to the development environment (but then they're not immediately discoverable either, and break muscle memory for existing Django users).
d) Write a custom runserver command that overrides the inbuilt one (but this requires overriding the `handle()` method, since `127.0.0.1` is hardcoded [2], so even more boilerplate).

Possible changes that would make this easier:
1) Allow overriding runserver default IP/port using Django settings.
2) Allow overriding runserver default IP/port using environment variables (like `PORT` already supported by gunicorn, that defaults the IP to `0.0.0.0` too).
3) Move the hardcoded `127.0.0.1` string [2] to an attribute on the `Command`, like `default_port` is, to at least reduce the boilerplate for overriding using a custom runserver command.

I can imagine (3) would be more likely to be accepted, however it would still mean we have to override runserver, so (1) or (2) would be preferred.

I'm happy to implement this - it would just be helpful to know people's thoughts before I open a PR.

Many thanks :-)

Ed

[1] https://github.com/benoitc/gunicorn/blob/6eb01409da42a81b7020cd78c52613d8ec868e94/gunicorn/config.py#L543-L544
[2] https://github.com/django/django/blob/eb42d8d5d9b0968ce12d09760afff684bee56a3a/django/core/management/commands/runserver.py#L95

Shai Berger

unread,
Nov 28, 2016, 9:06:08 AM11/28/16
to django-d...@googlegroups.com
On Monday 28 November 2016 15:48:21 emo...@mozilla.com wrote:
> This was filed as https://code.djangoproject.com/ticket/27537 , but moving
> here for discussion.
>
It seems all you need in the overridden runserver is:

from (...)runserver import Command as BaseCommand

class Command(BaseCommand):
def handle(self, *args, **options):
options.setdefault('addrport', '0.0.0.0:8000')
super(Command, self).handle(*args, **options)

What am I missing?

James Pic

unread,
Nov 28, 2016, 9:10:06 AM11/28/16
to django-d...@googlegroups.com
Perhaps we could override this default with a setting ?

Florian Apolloner

unread,
Nov 28, 2016, 10:45:17 AM11/28/16
to Django developers (Contributions to Django itself)
-1 on a setting for that. I'd be okay with a DJANGO_RUNSERVER_BINDHOST environment variable or similar.

James Pic

unread,
Nov 28, 2016, 10:51:59 AM11/28/16
to django-d...@googlegroups.com
I recon i use a single settings module that feeds from env vars for any project i touch myself, but I suggested adding a setting because that would leave the choice up to the user like it's currently the case in django projects (some people still rely on local_settings import or use several settings modules such as settings.dev settings.staging etc). So i'm 0 on settings vs. env vars myself.

Kamal Velan

unread,
Dec 8, 2016, 6:50:53 AM12/8/16
to Django developers (Contributions to Django itself)
Considering how the secret key is still in the settings.py giving the user the option to make it an env variable
this can follow the same suit.

Tim Graham

unread,
Dec 8, 2016, 9:32:19 AM12/8/16
to Django developers (Contributions to Django itself)
I've had a similar thought that it would be nice to have some way to specify some default command arguments for management commands (such as --keepdb when running tests), however, adding settings (and/or environment variables?) for every such use case doesn't feel like it'll keep things simple.

I lean toward bash aliases to meet these needs, although moving the hardcoded '127.0.0.1' in runserver so it can be more easily overridden is certainly fine.

Zach

unread,
Dec 8, 2016, 11:04:46 AM12/8/16
to Django developers (Contributions to Django itself)
How about a PACKAGE setting similar to npm's package.json.

PACKAGE = {
    'scripts': {
        'test': 'test --keepdb',
        'runserver': 'runserver --ipv6',
    },
}

Florian Apolloner

unread,
Dec 8, 2016, 11:16:02 AM12/8/16
to Django developers (Contributions to Django itself)
On Thursday, December 8, 2016 at 5:04:46 PM UTC+1, Zach wrote:
How about a PACKAGE setting similar to npm's package.json.

Seems to be yet another way to do what custom management commands already over (with less overhead for sure, but also more limited)

Zach

unread,
Dec 8, 2016, 1:52:33 PM12/8/16
to Django developers (Contributions to Django itself)
The PACKAGE setting would be extendable and allow core settings to be included. For example:

PACKAGE = {
    'scripts': {
        'test': 'test --keepdb',
        'runserver': 'runserver --ipv6',
    },
    'LOGGING': {
        'version': 1,
        'disable_existing_loggers': False,
        'handlers': {
            'file': {
                'level': 'DEBUG',
                'class': 'logging.FileHandler',
                'filename': '/path/to/django/debug.log',
            },
        },
        'loggers': {
            'django': {
                'handlers': ['file'],
                'level': 'DEBUG',
                'propagate': True,
            },
        },
    },
}

Florian Apolloner

unread,
Dec 9, 2016, 4:50:58 AM12/9/16
to Django developers (Contributions to Django itself)
What exactly would that achieve or do? Also what does PACKAGE in that context mean? Django has projects and apps and a simple settings.py file. Are you proposing to add a new file where apps could declare settings? How would those be merged, etc…

Cheers,
Florian

Collin Anderson

unread,
Dec 23, 2016, 7:42:57 PM12/23/16
to django-d...@googlegroups.com
If it helps, there's a also a shortcut: ./manage.py runserver 0:8000. Much easier to type.

--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-developers+unsubscribe@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/1f272641-1fbd-4b0a-8f45-b757c0d2b975%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Daniel Stanton

unread,
Jan 15, 2017, 10:54:16 AM1/15/17
to Django developers (Contributions to Django itself)
I'd find this really helpful right now.

Solution 1, overriding the default in a settings file is the easiest solution to understand and the fastest to set up, against 2 and 3 standalone.

Solution 2 comes for free with Solution 1 - a user can read from environment variables via settings files if needed.

Solution 3 could be combined with Solution 1 for consistency (given the port can be set in this way) to offer a little more flexibility.

Ed's happy to implement this (there's already a PR for Solution 1) so it would be great to enable him to move forwards on it!

Shai Berger

unread,
Jan 17, 2017, 2:21:46 AM1/17/17
to django-d...@googlegroups.com
I am -1 on adding a setting to handle a use-case that can be handled by users
with a 6-line file:

Mario Frasca

unread,
Jul 18, 2019, 6:20:02 PM7/18/19
to Django developers (Contributions to Django itself)
was this rejected?
my use case is: I have a single django program, which I run in multiple instances, each on a different port and connecting to a different database.  for each instance, I have a separate `config_INSTANCE.py` file.  the database settings fit in the `config` file, but I have to put the port in a batch file, or something else, outside the config file.  it feels as if I were doing something which isn't allowed...
I don't need to run the program on a single different port, I need different config files each stating a different port.

Adam Johnson

unread,
Jul 19, 2019, 6:07:31 AM7/19/19
to django-d...@googlegroups.com
Mario,

It's not the best to re-open a 2.5 year old thread :) Better to start a new one, link to the old one, and state what you think is different.

I believe your use case can be fit with a custom ruserver management command to override the original as Shai suggested:

> from (...)runserver import Command as BaseCommand

> class Command(BaseCommand):
>       def handle(self, *args, **options):
>               options.setdefault('addrport', '0.0.0.0:8000')
>               super(Command, self).handle(*args, **options)

You can change the options.setdefault line to set the port in addrport from a new setting you invent and put in your config file, such as MYPROJECTNAME_RUNSERVER_PORT = 8001

--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.


--
Adam
Reply all
Reply to author
Forward
0 new messages